001: package org.objectweb.celtix.tools.common.toolspec.parser;
002:
003: import java.util.StringTokenizer;
004: import java.util.logging.Level;
005: import java.util.logging.Logger;
006:
007: import org.w3c.dom.Element;
008: import org.w3c.dom.NodeList;
009:
010: import org.objectweb.celtix.common.logging.LogUtils;
011: import org.objectweb.celtix.tools.common.toolspec.Tool;
012:
013: public class Option implements TokenConsumer {
014:
015: private static final Logger LOG = LogUtils
016: .getL7dLogger(Option.class);
017: private static final String VALUE_ENUM_SEPARATOR = "|";
018: protected Element argument;
019: protected Element annotation;
020: private final Element element;
021: private Element valueType;
022:
023: private int numMatches;
024:
025: public Option(Element el) {
026: this .element = el;
027:
028: NodeList list = element.getElementsByTagNameNS(
029: Tool.TOOL_SPEC_PUBLIC_ID, "associatedArgument");
030:
031: if (list != null && list.getLength() > 0) {
032: argument = (Element) list.item(0);
033: }
034:
035: list = element.getElementsByTagNameNS(Tool.TOOL_SPEC_PUBLIC_ID,
036: "annotation");
037: if (list != null && list.getLength() > 0) {
038: annotation = (Element) list.item(0);
039: }
040:
041: if (annotation == null && argument != null) {
042: list = argument.getElementsByTagNameNS(
043: Tool.TOOL_SPEC_PUBLIC_ID, "annotation");
044: if (list != null && list.getLength() > 0) {
045: annotation = (Element) list.item(0);
046: }
047: }
048: }
049:
050: public boolean hasArgument() {
051: return argument != null;
052: }
053:
054: public boolean hasImmediateArgument() {
055: return hasArgument()
056: && "immediate".equals(argument
057: .getAttribute("placement"));
058: }
059:
060: /**
061: * @return whether the first token was accepted
062: */
063: public boolean accept(TokenInputStream args, Element result,
064: ErrorVisitor errors) {
065:
066: if (args.available() == 0) {
067: return false;
068: }
069: String arg = args.peek();
070:
071: if (arg == null) {
072: LOG.severe("ARGUMENT_IS_NULL_MSG");
073: }
074:
075: // go through each switch to see if we can match one to the arg.
076: NodeList switches = element.getElementsByTagNameNS(
077: Tool.TOOL_SPEC_PUBLIC_ID, "switch");
078:
079: boolean accepted = false;
080:
081: for (int i = 0; i < switches.getLength(); i++) {
082:
083: String switchArg = "-"
084: + switches.item(i).getFirstChild().getNodeValue();
085: if (LOG.isLoggable(Level.FINE)) {
086: LOG.fine("switchArg is " + switchArg);
087: }
088: if (hasImmediateArgument() ? arg.startsWith(switchArg)
089: : arg.equals(switchArg)) {
090: LOG.fine("Matches a switch!!!");
091: // consume the token
092: args.read();
093: // Add ourselves to the result document
094: Element optionEl = result.getOwnerDocument()
095: .createElementNS(
096: "http://www.xsume.com/Xutil/Command",
097: "option");
098:
099: optionEl.setAttribute("name", getName());
100:
101: // Add argument value to result
102: if (hasArgument()) {
103: String argValue;
104: if (hasImmediateArgument()) {
105: argValue = arg.substring(switchArg.length());
106: } else {
107: argValue = readArgumentValue(args, switchArg,
108: errors);
109: }
110: if (argValue != null) {
111: if (LOG.isLoggable(Level.FINE)) {
112: LOG
113: .fine("Setting argument value of option to "
114: + argValue);
115: }
116: optionEl.appendChild(result.getOwnerDocument()
117: .createTextNode(argValue));
118:
119: } else {
120: break;
121: }
122: }
123: result.appendChild(optionEl);
124: numMatches++;
125: accepted = true;
126: }
127: }
128: return accepted;
129: }
130:
131: private String readArgumentValue(TokenInputStream args,
132: String switchArg, ErrorVisitor errors) {
133: String value = null;
134: if (args.available() > 0) {
135: value = args.read();
136: if (value.startsWith("-")) {
137: errors.add(new ErrorVisitor.InvalidOption(switchArg));
138: value = null;
139: } else if (hasInvalidCharacter(value)) {
140: errors.add(new ErrorVisitor.UserError(switchArg
141: + " has invalid character!"));
142: }
143: if (!isInEnumArgumentValue(value)) {
144: errors
145: .add(new ErrorVisitor.UserError(switchArg + " "
146: + value
147: + " not in the enumeration value list!"));
148: }
149: } else {
150: errors.add(new ErrorVisitor.InvalidOption(switchArg));
151: }
152: return value;
153: }
154:
155: private boolean hasInvalidCharacter(String argValue) {
156: NodeList list = argument.getElementsByTagNameNS(
157: Tool.TOOL_SPEC_PUBLIC_ID, "valuetype");
158: String valuetypeStr = null;
159:
160: if (list != null && list.getLength() > 0) {
161: valueType = (Element) list.item(0);
162: valuetypeStr = valueType.getFirstChild().getNodeValue();
163:
164: if ("IdentifyString".equals(valuetypeStr)) {
165: return !isIdentifyString(argValue);
166: } else if ("NamingSpacePackageString".equals(valuetypeStr)) {
167: return !isNamingSpacePackageString(argValue);
168: } else if ("Digital".equals(valuetypeStr)) {
169: for (int i = 0; i < argValue.length(); i++) {
170: if (!Character.isDigit(argValue.charAt(i))) {
171: return true;
172: }
173: }
174: }
175: }
176: return false;
177: }
178:
179: private boolean isInEnumArgumentValue(String argValue) {
180: boolean result = true;
181: NodeList list = argument.getElementsByTagNameNS(
182: Tool.TOOL_SPEC_PUBLIC_ID, "valueenum");
183: if (list != null && list.getLength() == 1) {
184: result = false;
185: String enumValue = list.item(0).getTextContent();
186: StringTokenizer stk = new StringTokenizer(enumValue,
187: VALUE_ENUM_SEPARATOR);
188: if (stk.countTokens() <= 0) {
189: return result;
190: }
191: while (stk.hasMoreTokens()) {
192: if (argValue.equals(stk.nextToken())) {
193: result = true;
194: }
195: }
196: }
197: return result;
198: }
199:
200: private boolean isIdentifyString(String value) {
201: for (int i = 0; i < value.length(); i++) {
202: if (value.charAt(i) == '.') {
203: continue;
204: } else {
205: if (!Character.isJavaIdentifierPart(value.charAt(i))) {
206: return false;
207: }
208: }
209: }
210: return true;
211: }
212:
213: private boolean isNamingSpacePackageString(String value) {
214: if (value.indexOf("=") < 0) {
215: return isIdentifyString(value);
216: } else {
217: String packageName = value.substring(
218: value.indexOf("=") + 1, value.length());
219: return isIdentifyString(packageName);
220: }
221: }
222:
223: public boolean isSatisfied(ErrorVisitor errors) {
224: if (errors.getErrors().size() > 0) {
225: return false;
226: }
227: if (LOG.isLoggable(Level.FINE)) {
228: LOG.fine("For this option, minOccurs="
229: + element.getAttribute("minOccurs")
230: + " and maxOccurs="
231: + element.getAttribute("maxOccurs")
232: + ", numMatches currently " + numMatches);
233: }
234: boolean result = true;
235:
236: if (!isAtleastMinimum()) {
237: errors.add(new ErrorVisitor.MissingOption(this ));
238: result = false;
239: }
240: if (result && !isNoGreaterThanMaximum()) {
241: errors.add(new ErrorVisitor.DuplicateOption(getName()));
242: result = false;
243: }
244: if (LOG.isLoggable(Level.FINE)) {
245: LOG.fine("isSatisfied() returning " + result);
246: }
247: return result;
248: }
249:
250: private boolean isAtleastMinimum() {
251: boolean result = true;
252: int minOccurs = 0;
253:
254: if (!"".equals(element.getAttribute("minOccurs"))) {
255: result = numMatches >= Integer.parseInt(element
256: .getAttribute("minOccurs"));
257: } else {
258: result = numMatches >= minOccurs;
259: }
260: return result;
261: }
262:
263: private boolean isNoGreaterThanMaximum() {
264: boolean result = true;
265: int maxOccurs = 1;
266:
267: if (!"".equals(element.getAttribute("maxOccurs"))) {
268: result = "unbounded".equals(element
269: .getAttribute("maxOccurs"))
270: || numMatches <= Integer.parseInt(element
271: .getAttribute("maxOccurs"));
272: } else {
273: result = numMatches <= maxOccurs;
274: }
275: return result;
276: }
277:
278: public String getName() {
279: return element.getAttribute("id");
280: }
281:
282: public String getAnnotation() {
283: return annotation.getFirstChild().getNodeValue();
284: }
285:
286: public String getPrimarySwitch() {
287: NodeList switches = element.getElementsByTagNameNS(
288: Tool.TOOL_SPEC_PUBLIC_ID, "switch");
289:
290: // options must have atleast one switch, as enforced by schema, so no
291: // need for defensive coding.
292: return switches.item(0).getFirstChild().getNodeValue();
293: }
294:
295: public String toString() {
296: return getName();
297: }
298:
299: }
|