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