Skip to content

Commit 4a4bba5

Browse files
committed
v1.7.4: stability fixes and improvements
1 parent 94544be commit 4a4bba5

File tree

10 files changed

+194
-36
lines changed

10 files changed

+194
-36
lines changed

README.md

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,13 @@ for a higher control compared to the EasyML Facade.
6060

6161
### Release Notes
6262

63+
Release 1.7.4
64+
- bugfix: XMLReader text driver comsume with pretty print
65+
- bugfix: XMLWriter text driver attribute validation
66+
- bugfix: XMLWriter and XMLReader allow field names containing "$"
67+
- bugfix: only register AWT strategies if AWT available
68+
69+
6370
Release 1.7.3
6471
- feature: XMLReader exclude fields (similar to XMLWriter exclude fields)
6572
- feature: added OptionalDouble, OptionalInt, OptionalLong strategies.
@@ -84,9 +91,6 @@ Release 1.6.0 (requires Java 9, recommended up to Java 17)
8491
- XMLWriter and XMLReader use getters and setters.
8592
- feature: support for Java 9 modules.
8693
- NON-BACKWARD COMPATIBLE refactor of ReflectionUtil.
87-
88-
89-
Release 1.5.3
9094
- refactor: remove deprecated Class.newInstance() usages.
9195
- refactor: limited reflection usage from Properties, EnumSet, EnumMap,
9296
SingletonList, SingletonSet, SingletonMap strategies.

easyml/src/net/sourceforge/easyml/EasyML.java

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@
8585
* objects.<br/>
8686
*
8787
* @author Victor Cordis ( cordis.victor at gmail.com)
88-
* @version 1.7.3
88+
* @version 1.7.4
8989
* @see XMLReader
9090
* @see XMLWriter
9191
* @since 1.0
@@ -207,7 +207,9 @@ public static void defaultConfiguration(XMLWriter writer) {
207207
// simple.add(IntStrategy.INSTANCE);
208208
// simple.add(StringStrategy.INSTANCE);
209209
// awt:
210-
composite.add(ColorStrategy.INSTANCE);
210+
if (ColorStrategy.isAvailable()) {
211+
composite.add(ColorStrategy.INSTANCE);
212+
}
211213
// io:
212214
composite.add(ExternalizableStrategy.INSTANCE);
213215
simple.add(FileStrategy.INSTANCE);
@@ -309,7 +311,9 @@ public static void defaultConfiguration(XMLReader reader) {
309311
// simple.put(IntStrategy.NAME, IntStrategy.INSTANCE);
310312
// simple.put(StringStrategy.NAME, StringStrategy.INSTANCE);
311313
// awt:
312-
composite.put(ColorStrategy.NAME, ColorStrategy.INSTANCE);
314+
if (ColorStrategy.isAvailable()) {
315+
composite.put(ColorStrategy.NAME, ColorStrategy.INSTANCE);
316+
}
313317
// io:
314318
composite.put(ExternalizableStrategy.NAME, ExternalizableStrategy.INSTANCE);
315319
simple.put(FileStrategy.NAME, FileStrategy.INSTANCE);

easyml/src/net/sourceforge/easyml/XMLReaderDOMDriver.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
*/
1919
package net.sourceforge.easyml;
2020

21+
import net.sourceforge.easyml.util.XMLUtil;
2122
import org.w3c.dom.Document;
2223
import org.w3c.dom.Element;
2324
import org.w3c.dom.Node;
@@ -31,7 +32,7 @@
3132
* transforming the DOM to text so that it can be inputed as text to EasyML.
3233
*
3334
* @author Victor Cordis ( cordis.victor at gmail.com)
34-
* @version 1.7.3
35+
* @version 1.7.4
3536
* @since 1.1.0
3637
*/
3738
final class XMLReaderDOMDriver extends XMLReader.Driver {
@@ -155,7 +156,7 @@ public String elementName() {
155156
if (this.crt == null) {
156157
throw new IllegalStateException("not at element start or end: " + this.positionDescriptor());
157158
}
158-
return this.crt.getNodeName();
159+
return XMLUtil.unescapeXMLTag(this.crt.getNodeName());
159160
}
160161

161162
/**

easyml/src/net/sourceforge/easyml/XMLReaderTextDriver.java

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
*/
1919
package net.sourceforge.easyml;
2020

21+
import net.sourceforge.easyml.util.XMLUtil;
2122
import org.kxml2.io.KXmlParser;
2223
import org.xmlpull.v1.XmlPullParser;
2324
import org.xmlpull.v1.XmlPullParserException;
@@ -33,7 +34,7 @@
3334
* parameters.
3435
*
3536
* @author Victor Cordis ( cordis.victor at gmail.com)
36-
* @version 1.7.3
37+
* @version 1.7.4
3738
* @since 1.1.0
3839
*/
3940
final class XMLReaderTextDriver extends XMLReader.Driver {
@@ -131,7 +132,7 @@ public String elementName() {
131132
try {
132133
final int eventType = this.parser.getEventType();
133134
if (eventType == XmlPullParser.START_TAG || eventType == XmlPullParser.END_TAG) {
134-
return this.parser.getName();
135+
return XMLUtil.unescapeXMLTag(this.parser.getName());
135136
}
136137
throw new IllegalStateException("expected element start or end: " + this.parser.getLineNumber() + "," + this.parser.getColumnNumber());
137138
} catch (XmlPullParserException xppX) {
@@ -175,12 +176,12 @@ public void consume() {
175176
throw new IllegalStateException("not at element start: " + this.positionDescriptor());
176177
}
177178
try {
178-
int depth = 0;
179+
int depth = 1;
179180
do {
180-
final int next = parser.next();
181-
if (next == XmlPullParser.START_TAG) {
181+
final int eventType = parser.next();
182+
if (eventType == XmlPullParser.START_TAG) {
182183
depth++;
183-
} else if (next == XmlPullParser.END_TAG) {
184+
} else if (eventType == XmlPullParser.END_TAG) {
184185
depth--;
185186
}
186187
} while (depth != 0);

easyml/src/net/sourceforge/easyml/XMLWriterDOMDriver.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
*/
1919
package net.sourceforge.easyml;
2020

21+
import net.sourceforge.easyml.util.XMLUtil;
2122
import org.w3c.dom.Document;
2223
import org.w3c.dom.Element;
2324
import org.w3c.dom.Node;
@@ -27,7 +28,7 @@
2728
* XML to DOM documents.
2829
*
2930
* @author Victor Cordis ( cordis.victor at gmail.com)
30-
* @version 1.3.9
31+
* @version 1.4.1
3132
* @since 1.1.0
3233
*/
3334
final class XMLWriterDOMDriver extends XMLWriter.Driver {
@@ -61,7 +62,8 @@ public void startElement(String name) {
6162
throw new IllegalStateException("cannot write element start");
6263
}
6364
// update state:
64-
final Element started = this.root.createElement(name);
65+
final String escapedName = XMLUtil.escapeXMLTag(name);
66+
final Element started = this.root.createElement(escapedName);
6567
if (this.crt == null) {
6668
this.root.appendChild(started);
6769
} else {

easyml/src/net/sourceforge/easyml/XMLWriterTextDriver.java

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
* XML to output streams.
3131
*
3232
* @author Victor Cordis ( cordis.victor at gmail.com)
33-
* @version 1.4.1
33+
* @version 1.7.4
3434
* @since 1.1.0
3535
*/
3636
final class XMLWriterTextDriver extends XMLWriter.Driver {
@@ -73,10 +73,11 @@ public void startElement(String name) {
7373
} else {
7474
writeIndentedLt();
7575
}
76-
this.writer.write(name);
76+
final String escapedName = XMLUtil.escapeXMLTag(name);
77+
this.writer.write(escapedName);
7778
this.writeOneTimeUniqueId(id -> tryWriteAttrEqValue(DTD.ATTRIBUTE_ID, id));
7879
// update state:
79-
this.elementStack.add(name);
80+
this.elementStack.add(escapedName);
8081
this.state = XMLWriter.Driver.STATE_START;
8182
} catch (IOException ioX) {
8283
throw new RuntimeException(ioX);
@@ -123,11 +124,14 @@ private void writeAttrEqValue(String attr, String value) throws IOException {
123124
*/
124125
@Override
125126
public void setAttribute(String attribute, String value) {
127+
if (!XMLUtil.isLegalXMLTag(attribute)) {
128+
throw new IllegalArgumentException("attribute: " + attribute);
129+
}
126130
if (this.state != XMLWriter.Driver.STATE_START) {
127131
throw new IllegalStateException("cannot write element attributes");
128132
}
129133
try {
130-
writeAttrEqValue(XMLUtil.escapeXML(attribute), XMLUtil.escapeXML(value));
134+
writeAttrEqValue(attribute, XMLUtil.escapeXML(value));
131135
} catch (IOException ioX) {
132136
throw new RuntimeException(ioX);
133137
}

easyml/src/net/sourceforge/easyml/marshalling/java/awt/ColorStrategy.java

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
* thread-safe.
3030
*
3131
* @author Victor Cordis ( cordis.victor at gmail.com)
32-
* @version 1.7.1
32+
* @version 1.7.4
3333
* @since 1.0
3434
*/
3535
public final class ColorStrategy extends AbstractStrategy implements CompositeStrategy<Color> {
@@ -52,12 +52,21 @@ private static Class targetClass() {
5252
try {
5353
// init Color.class by using Class.forName with initialize=false:
5454
return Class.forName("java.awt.Color", false, ColorStrategy.class.getClassLoader());
55-
} catch (ClassNotFoundException neverThrown) {
55+
} catch (ClassNotFoundException unavailableAWT) {
5656
// java.awt.Color should be a part of the JDK.
5757
return null;
5858
}
5959
}
6060

61+
/**
62+
* Returns true if this strategy is available for usage, false if AWT not present.
63+
*
64+
* @return true if AWT present, false otherwise
65+
*/
66+
public static boolean isAvailable() {
67+
return TARGET != null;
68+
}
69+
6170
private ColorStrategy() {
6271
}
6372

easyml/src/net/sourceforge/easyml/util/XMLUtil.java

Lines changed: 69 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -18,28 +18,41 @@
1818
*/
1919
package net.sourceforge.easyml.util;
2020

21+
import java.util.regex.Matcher;
22+
import java.util.regex.Pattern;
23+
2124
/**
2225
* XMLUtil utility class used to format the XML element and element attribute
2326
* values by escaping and un-escaping illegal XML characters.
2427
*
2528
* @author Victor Cordis ( cordis.victor at gmail.com)
26-
* @version 1.5.0
29+
* @version 1.7.4
2730
* @since 1.0
2831
*/
2932
public final class XMLUtil {
3033

31-
static final char XML_ILLEGAL_LT = '<';
32-
static final char XML_ILLEGAL_GT = '>';
33-
static final char XML_ILLEGAL_AMP = '&';
34-
static final char XML_ILLEGAL_QUOT = '"';
35-
static final char XML_ILLEGAL_APOS = '\'';
36-
static final char XML_ILLEGAL_CR = '\r';
37-
static final String XML_LEGAL_LT = "&lt;";
38-
static final String XML_LEGAL_GT = "&gt;";
39-
static final String XML_LEGAL_AMP = "&amp;";
40-
static final String XML_LEGAL_QUOT = "&quot;";
41-
static final String XML_LEGAL_APOS = "&apos;";
42-
static final String XML_LEGAL_CR = "&#13;";
34+
/**
35+
* Constant holding the illegal <code>$</code>.
36+
*/
37+
public static final char XML_TAG_ILLEGAL_$ = '$';
38+
/**
39+
* Constant holding the escaped form of <code>$</code>.
40+
*/
41+
public static final String XML_TAG_LEGAL_$ = "_-_";
42+
private static final String XML_TAG_ILLEGAL_$_QUOTED = Matcher.quoteReplacement(Character.toString(XML_TAG_ILLEGAL_$));
43+
private static final Pattern XML_TAG_LEGAL_$_PATTERN = Pattern.compile(Pattern.quote(XML_TAG_LEGAL_$));
44+
private static final char XML_ILLEGAL_LT = '<';
45+
private static final char XML_ILLEGAL_GT = '>';
46+
private static final char XML_ILLEGAL_AMP = '&';
47+
private static final char XML_ILLEGAL_QUOT = '"';
48+
private static final char XML_ILLEGAL_APOS = '\'';
49+
private static final char XML_ILLEGAL_CR = '\r';
50+
private static final String XML_LEGAL_LT = "&lt;";
51+
private static final String XML_LEGAL_GT = "&gt;";
52+
private static final String XML_LEGAL_AMP = "&amp;";
53+
private static final String XML_LEGAL_QUOT = "&quot;";
54+
private static final String XML_LEGAL_APOS = "&apos;";
55+
private static final String XML_LEGAL_CR = "&#13;";
4356

4457
/**
4558
* Returns true if the input text is free from illegal XML characters, false otherwise.
@@ -130,6 +143,49 @@ private static void appendEscaped(StringBuilder destination, char c) {
130143
}
131144
}
132145

146+
/**
147+
* Escapes <code>$</code> in the given tag.
148+
*
149+
* @param tag to escape
150+
* @return the escaped tag
151+
*/
152+
public static String escapeXMLTag(String tag) {
153+
final int symbolIdx = tag.indexOf(XML_TAG_ILLEGAL_$);
154+
if (symbolIdx == -1) {
155+
return tag; // all legal.
156+
}
157+
final int len = tag.length();
158+
final StringBuilder sb = new StringBuilder(len + 10);
159+
// copy initial legal part:
160+
for (int i = 0; i < symbolIdx; i++) {
161+
sb.append(tag.charAt(i));
162+
}
163+
// escape the remaining part:
164+
for (int i = symbolIdx; i < len; i++) {
165+
final char c = tag.charAt(i);
166+
if (c == XML_TAG_ILLEGAL_$) {
167+
sb.append(XML_TAG_LEGAL_$);
168+
} else {
169+
sb.append(c);
170+
}
171+
}
172+
return sb.toString();
173+
}
174+
175+
/**
176+
* Unescapes <code>$</code> in the given tag.
177+
*
178+
* @param tag to unescape
179+
* @return the unescaped tag
180+
*/
181+
public static String unescapeXMLTag(String tag) {
182+
final int escapedIdx = tag.indexOf(XML_TAG_LEGAL_$);
183+
if (escapedIdx == -1) {
184+
return tag; // all legal.
185+
}
186+
return XML_TAG_LEGAL_$_PATTERN.matcher(tag).replaceAll(XML_TAG_ILLEGAL_$_QUOTED);
187+
}
188+
133189
private XMLUtil() {
134190
}
135191
}

easyml/test/net/sourceforge/easyml/BugsTest.java

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,6 @@ public void testBugObjectX_defValFieldHidesSuperDefValField() throws Exception {
7777
public void testBugSerial_PutGetFieldsCustomKeys() throws Exception {
7878
easyml = new EasyMLBuilder()
7979
//.withStyle(EasyML.Style.PRETTY)
80-
//.withProfile(EasyML.Profile.GENERIC)
8180
.withStrategy(new SerializableStrategy())
8281
.build();
8382

@@ -89,6 +88,20 @@ public void testBugSerial_PutGetFieldsCustomKeys() throws Exception {
8988
assertEquals(expected, easyml.deserialize(xml));
9089
}
9190

91+
@Test
92+
public void testBugSpecialCharField() throws Exception {
93+
easyml = new EasyMLBuilder()
94+
//.withStyle(EasyML.Style.PRETTY)
95+
.build();
96+
97+
final SpecialSymbolField expected = new SpecialSymbolField("specialValue");
98+
99+
String xml = easyml.serialize(expected);
100+
System.out.println(xml);
101+
102+
assertEquals(expected, easyml.deserialize(xml));
103+
}
104+
92105
private static class Person {
93106

94107
private final String name;
@@ -237,4 +250,41 @@ public String toString() {
237250
'}';
238251
}
239252
}
253+
254+
private static class SpecialSymbolField {
255+
256+
private final String special$field;
257+
258+
public SpecialSymbolField() {
259+
special$field = null;
260+
}
261+
262+
public SpecialSymbolField(String special$field) {
263+
this.special$field = special$field;
264+
}
265+
266+
public String getSpecial$field() {
267+
return special$field;
268+
}
269+
270+
@Override
271+
public boolean equals(Object o) {
272+
if (this == o) return true;
273+
if (o == null || getClass() != o.getClass()) return false;
274+
SpecialSymbolField that = (SpecialSymbolField) o;
275+
return Objects.equals(special$field, that.special$field);
276+
}
277+
278+
@Override
279+
public int hashCode() {
280+
return Objects.hash(special$field);
281+
}
282+
283+
@Override
284+
public String toString() {
285+
return "SpecialSymbolField{" +
286+
"special$field='" + special$field + '\'' +
287+
'}';
288+
}
289+
}
240290
}

0 commit comments

Comments
 (0)