Skip to content

Commit e1b7ce0

Browse files
committed
Fixed attributes handling
1 parent 420ae53 commit e1b7ce0

2 files changed

Lines changed: 187 additions & 97 deletions

File tree

src/com/maxprograms/xml/AttlistDecl.java

Lines changed: 151 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414
import java.io.IOException;
1515
import java.io.OutputStream;
1616
import java.nio.charset.Charset;
17-
import java.util.Iterator;
1817
import java.util.List;
1918
import java.util.Vector;
2019

@@ -26,6 +25,7 @@ public class AttlistDecl implements XMLNode {
2625
public AttlistDecl(String declaration) {
2726
attributes = new Vector<>();
2827
int i = "<!ATTLIST".length();
28+
declaration = declaration.trim();
2929
char c = declaration.charAt(i);
3030
while (XMLUtils.isXmlSpace(c)) {
3131
i++;
@@ -38,44 +38,159 @@ public AttlistDecl(String declaration) {
3838
c = declaration.charAt(i);
3939
}
4040
listName = sb.toString();
41-
// TODO this.parseAttributes(declaration.substring(i).trim());
41+
parseAttributes(declaration.substring(i, declaration.lastIndexOf(">")).trim());
4242
}
4343

4444
private void parseAttributes(String declaration) {
4545
int i = 0;
46-
char c = declaration.charAt(i);
47-
while (XMLUtils.isXmlSpace(c)) {
48-
i++;
49-
c = declaration.charAt(i);
50-
}
51-
StringBuilder nameBuilder = new StringBuilder();
52-
while (!XMLUtils.isXmlSpace(c)) {
53-
nameBuilder.append(c);
54-
i++;
55-
c = declaration.charAt(i);
56-
}
57-
String name = nameBuilder.toString();
58-
while (XMLUtils.isXmlSpace(c)) {
59-
i++;
60-
c = declaration.charAt(i);
61-
}
62-
StringBuilder typeBuilder = new StringBuilder();
63-
if (c == '(') {
64-
// it is an ennumeration
65-
while (c != ')') {
66-
typeBuilder.append(c);
46+
while (i < declaration.length()) {
47+
char c = declaration.charAt(i);
48+
49+
// Skip whitespace
50+
while (i < declaration.length() && XMLUtils.isXmlSpace(c)) {
6751
i++;
68-
c = declaration.charAt(i);
52+
if (i < declaration.length()) {
53+
c = declaration.charAt(i);
54+
}
6955
}
70-
typeBuilder.append(')');
71-
} else {
72-
while (!XMLUtils.isXmlSpace(c) || c == '(') {
73-
typeBuilder.append(c);
56+
57+
// Parse attribute name
58+
StringBuilder nameBuilder = new StringBuilder();
59+
while (i < declaration.length() && !XMLUtils.isXmlSpace(c)) {
60+
nameBuilder.append(c);
61+
i++;
62+
if (i < declaration.length()) {
63+
c = declaration.charAt(i);
64+
}
65+
}
66+
String name = nameBuilder.toString();
67+
68+
if (name.startsWith("%") && name.endsWith(";")) {
69+
// It is a parameter entity reference
70+
AttributeDecl parameterEntity = new AttributeDecl(name, null, null, false);
71+
parameterEntity.setParameterEntity(true);
72+
attributes.add(parameterEntity);
73+
continue;
74+
}
75+
// Skip whitespace
76+
while (i < declaration.length() && XMLUtils.isXmlSpace(c)) {
77+
i++;
78+
if (i < declaration.length()) {
79+
c = declaration.charAt(i);
80+
}
81+
}
82+
83+
// Parse attribute type, it can be:
84+
// StringType: 'CDATA'
85+
// TokenizedType: 'ID','IDREF','IDREFS','ENTITY','ENTITIES','NMTOKEN' or
86+
// 'NMTOKENS'
87+
// or an enumeration od string values
88+
89+
StringBuilder typeBuilder = new StringBuilder();
90+
if (c == '(') {
91+
// It is an enumeration
92+
while (i < declaration.length() && c != ')') {
93+
typeBuilder.append(c);
94+
i++;
95+
if (i < declaration.length()) {
96+
c = declaration.charAt(i);
97+
}
98+
}
99+
if (c == ')') {
100+
typeBuilder.append(c);
101+
i++;
102+
c = declaration.charAt(i);
103+
}
104+
} else {
105+
while (i < declaration.length() && !XMLUtils.isXmlSpace(c)) {
106+
typeBuilder.append(c);
107+
i++;
108+
if (i < declaration.length()) {
109+
c = declaration.charAt(i);
110+
}
111+
}
112+
}
113+
String type = typeBuilder.toString();
114+
115+
// Skip whitespace
116+
while (i < declaration.length() && XMLUtils.isXmlSpace(c)) {
117+
i++;
118+
if (i < declaration.length()) {
119+
c = declaration.charAt(i);
120+
}
121+
}
122+
123+
// Parse defaultDecl section
124+
// DefaultDecl can be:
125+
// #IMPLIED, #REQUIRED, #FIXED or a quoted string
126+
127+
if (c == '#') {
128+
// Parse #IMPLIED, #REQUIRED, or #FIXED
129+
StringBuilder keywordBuilder = new StringBuilder();
130+
while (i < declaration.length() && !XMLUtils.isXmlSpace(c)) {
131+
keywordBuilder.append(c);
132+
i++;
133+
if (i < declaration.length()) {
134+
c = declaration.charAt(i);
135+
}
136+
}
137+
String keyword = keywordBuilder.toString();
138+
if ("#FIXED".equals(keyword)) {
139+
StringBuilder defaultValueBuilder = new StringBuilder();
140+
141+
// Skip whitespace after #FIXED
142+
while (i < declaration.length() && XMLUtils.isXmlSpace(c)) {
143+
i++;
144+
if (i < declaration.length()) {
145+
c = declaration.charAt(i);
146+
}
147+
}
148+
149+
// Parse the fixed default value
150+
if (c == '"' || c == '\'') {
151+
char quote = c;
152+
i++;
153+
if (i < declaration.length()) {
154+
c = declaration.charAt(i);
155+
}
156+
while (i < declaration.length() && c != quote) {
157+
defaultValueBuilder.append(c);
158+
i++;
159+
if (i < declaration.length()) {
160+
c = declaration.charAt(i);
161+
}
162+
}
163+
if (c == quote) {
164+
i++;
165+
}
166+
} else {
167+
throw new IllegalArgumentException("Expected quoted default value after #FIXED.");
168+
}
169+
attributes.add(new AttributeDecl(name, type, defaultValueBuilder.toString(), true));
170+
} else {
171+
attributes.add(new AttributeDecl(name, type, keyword, false));
172+
}
173+
} else if (c == '"' || c == '\'') {
174+
StringBuilder defaultValueBuilder = new StringBuilder();
175+
// Default value is quoted
176+
char quote = c;
74177
i++;
75-
c = declaration.charAt(i);
178+
if (i < declaration.length()) {
179+
c = declaration.charAt(i);
180+
}
181+
while (i < declaration.length() && c != quote) {
182+
defaultValueBuilder.append(c);
183+
i++;
184+
if (i < declaration.length()) {
185+
c = declaration.charAt(i);
186+
}
187+
}
188+
if (c == quote) {
189+
i++;
190+
}
191+
attributes.add(new AttributeDecl(name, type, defaultValueBuilder.toString(), false));
76192
}
77193
}
78-
String type = typeBuilder.toString();
79194
}
80195

81196
public String getListName() {
@@ -93,18 +208,17 @@ public short getNodeType() {
93208

94209
@Override
95210
public String toString() {
96-
StringBuilder sb = new StringBuilder("<!ATTLIST");
97-
Iterator<AttributeDecl> it = attributes.iterator();
98-
while (it.hasNext()) {
99-
sb.append("\n ");
100-
sb.append(it.next().toString());
211+
StringBuilder sb = new StringBuilder("<!ATTLIST ");
212+
sb.append(listName);
213+
for (AttributeDecl attr : attributes) {
214+
sb.append("\n ").append(attr.toString());
101215
}
102-
sb.append("\n>\n");
216+
sb.append("\n>");
103217
return sb.toString();
104218
}
105219

106220
@Override
107221
public void writeBytes(OutputStream output, Charset charset) throws IOException {
108222
output.write(toString().getBytes(charset));
109223
}
110-
}
224+
}

src/com/maxprograms/xml/AttributeDecl.java

Lines changed: 36 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -12,81 +12,57 @@
1212

1313
package com.maxprograms.xml;
1414

15-
import java.io.IOException;
16-
import java.io.OutputStream;
17-
import java.nio.charset.Charset;
18-
19-
public class AttributeDecl implements XMLNode, Comparable<AttributeDecl> {
20-
21-
String element;
22-
String attribute;
23-
String type;
24-
String mode;
25-
String value;
26-
27-
public AttributeDecl(String element, String attribute, String type, String mode, String value) {
28-
this.element = element;
29-
this.attribute = attribute;
15+
public class AttributeDecl {
16+
private String name;
17+
private String type;
18+
private String defaultValue;
19+
private boolean isFixed;
20+
private boolean isParameterEntity = false;
21+
22+
public AttributeDecl(String name, String type, String defaultValue, boolean isFixed) {
23+
this.name = name;
3024
this.type = type;
31-
this.mode = mode;
32-
this.value = value;
25+
this.defaultValue = defaultValue;
26+
this.isFixed = isFixed;
3327
}
3428

35-
public String getElementName() {
36-
return element;
29+
public void setParameterEntity(boolean isParameterEntity) {
30+
this.isParameterEntity = isParameterEntity;
3731
}
3832

39-
public String getAttributeName() {
40-
return attribute;
41-
}
42-
43-
public String getMode() {
44-
return mode;
33+
public String getName() {
34+
return name;
4535
}
4636

4737
public String getType() {
4838
return type;
4939
}
5040

51-
public String getValue() {
52-
return value;
53-
}
54-
55-
@Override
56-
public String toString() {
57-
if (value != null) {
58-
return "<!ATTLIST " + element + " " + attribute + " " + type + " " + mode + " \"" + value + "\">";
59-
}
60-
return "<!ATTLIST " + element + " " + attribute + " " + type + " " + mode + ">";
41+
public String getDefaultValue() {
42+
return defaultValue;
6143
}
6244

63-
@Override
64-
public int compareTo(AttributeDecl o) {
65-
int first = element.compareTo(o.element);
66-
return first != 0 ? first : attribute.compareTo(o.attribute);
45+
public boolean isFixed() {
46+
return isFixed;
6747
}
6848

6949
@Override
70-
public short getNodeType() {
71-
return XMLNode.ATTRIBUTE_DECL_NODE;
72-
}
73-
74-
@Override
75-
public void writeBytes(OutputStream output, Charset charset) throws IOException {
76-
output.write(toString().getBytes(charset));
77-
}
78-
79-
@Override
80-
public int hashCode() {
81-
return toString().hashCode();
82-
}
83-
84-
@Override
85-
public boolean equals(Object obj) {
86-
if (obj instanceof AttributeDecl other) {
87-
return toString().equals(other.toString());
50+
public String toString() {
51+
if (isParameterEntity) {
52+
return name;
8853
}
89-
return false;
54+
StringBuilder sb = new StringBuilder();
55+
sb.append(name).append(" ").append(type);
56+
if (isFixed) {
57+
sb.append(" #FIXED");
58+
sb.append(" \"").append(defaultValue).append("\"");
59+
} else if ("#REQUIRED".equals(defaultValue)) {
60+
sb.append(" #REQUIRED");
61+
} else if ("#IMPLIED".equals(defaultValue)) {
62+
sb.append(" #IMPLIED");
63+
} else if (defaultValue != null && !defaultValue.isEmpty()) {
64+
sb.append(" \"").append(defaultValue).append("\"");
65+
}
66+
return sb.toString();
9067
}
91-
92-
}
68+
}

0 commit comments

Comments
 (0)