001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: *
017: */
018: package org.apache.ivy.util;
019:
020: import java.lang.reflect.InvocationTargetException;
021: import java.lang.reflect.Method;
022: import java.util.ArrayList;
023: import java.util.Arrays;
024: import java.util.Collections;
025: import java.util.HashMap;
026: import java.util.Iterator;
027: import java.util.LinkedHashMap;
028: import java.util.List;
029: import java.util.Map;
030: import java.util.Stack;
031:
032: import org.apache.ivy.core.IvyPatternHelper;
033:
034: /**
035: * Ant 1.6.1 like Configurator
036: * <p>
037: * This configurator is used to configure elements (initialised with
038: * setRoot) using the behaviour defined by ant for its tasks.
039: * <p>
040: * Example (based on <a
041: * href="http://ant.apache.org/manual/develop.html#writingowntask">Ant Example</a>):
042: * <pre>
043: * Configurator conf = new Configurator();
044: * conf.typeDef("buildpath", "Sample$BuildPath");
045: * conf.typeDef("xinterface", "Sample$XInterface");
046: * Sample.MyFileSelector mfs = new Sample.MyFileSelector();
047: * conf.setRoot(mfs);
048: * conf.startCreateChild("buildpath");
049: * conf.setAttribute("path", ".");
050: * conf.setAttribute("url", "abc");
051: * conf.startCreateChild("xinterface");
052: * conf.setAttribute("count", "4");
053: * conf.endCreateChild(); // xinterface
054: * conf.endCreateChild(); // buildpath
055: * </pre>
056: */
057: public class Configurator {
058: public static class Macro {
059: private MacroDef macrodef;
060:
061: private Map attValues = new HashMap();
062:
063: private Map macroRecords = new HashMap();
064:
065: public Macro(MacroDef def) {
066: macrodef = def;
067: }
068:
069: public void defineAttribute(String attributeName, String value) {
070: if (macrodef.getAttribute(attributeName) == null) {
071: throw new IllegalArgumentException(
072: "undeclared attribute " + attributeName
073: + " on macro " + macrodef.getName());
074: }
075: attValues.put(attributeName, value);
076: }
077:
078: public MacroRecord recordCreateChild(String name) {
079: MacroRecord macroRecord = new MacroRecord(name);
080: List records = (List) macroRecords.get(name);
081: if (records == null) {
082: records = new ArrayList();
083: macroRecords.put(name, records);
084: }
085: records.add(macroRecord);
086: return macroRecord;
087: }
088:
089: public Object play(Configurator conf) {
090: return macrodef.play(conf, attValues, macroRecords);
091: }
092:
093: }
094:
095: public static class Attribute {
096: private String name;
097:
098: private String defaultValue;
099:
100: public String getDefault() {
101: return defaultValue;
102: }
103:
104: public void setDefault(String default1) {
105: defaultValue = default1;
106: }
107:
108: public String getName() {
109: return name;
110: }
111:
112: public void setName(String name) {
113: this .name = name;
114: }
115: }
116:
117: public static class Element {
118: private String name;
119:
120: private boolean optional = false;
121:
122: public String getName() {
123: return name;
124: }
125:
126: public void setName(String name) {
127: this .name = name;
128: }
129:
130: public boolean isOptional() {
131: return optional;
132: }
133:
134: public void setOptional(boolean optional) {
135: this .optional = optional;
136: }
137: }
138:
139: public static class MacroRecord {
140: private String name;
141:
142: private Map attributes = new LinkedHashMap();
143:
144: private List children = new ArrayList();
145:
146: public MacroRecord(String name) {
147: this .name = name;
148: }
149:
150: public String getName() {
151: return name;
152: }
153:
154: public void recordAttribute(String name, String value) {
155: attributes.put(name, value);
156: }
157:
158: public MacroRecord recordChild(String name) {
159: MacroRecord child = new MacroRecord(name);
160: children.add(child);
161: return child;
162: }
163:
164: public Map getAttributes() {
165: return attributes;
166: }
167:
168: public List getChildren() {
169: return children;
170: }
171: }
172:
173: public static class MacroDef {
174: private String name;
175:
176: private Map attributes = new HashMap();
177:
178: private Map elements = new HashMap();
179:
180: private MacroRecord macroRecord;
181:
182: public MacroDef(String macroName) {
183: name = macroName;
184: }
185:
186: public Attribute getAttribute(String attributeName) {
187: return (Attribute) attributes.get(attributeName);
188: }
189:
190: public Object play(Configurator conf, Map attValues,
191: Map macroRecords) {
192: for (Iterator iter = attributes.values().iterator(); iter
193: .hasNext();) {
194: Attribute att = (Attribute) iter.next();
195: String val = (String) attValues.get(att.getName());
196: if (val == null) {
197: if (att.getDefault() == null) {
198: throw new IllegalArgumentException("attribute "
199: + att.getName() + " is required in "
200: + getName());
201: } else {
202: attValues.put(att.getName(), att.getDefault());
203: }
204: }
205: }
206: return play(conf, macroRecord, attValues, macroRecords);
207: }
208:
209: private Object play(Configurator conf, MacroRecord macroRecord,
210: Map attValues, Map childrenRecords) {
211: conf.startCreateChild(macroRecord.getName());
212: Map attributes = macroRecord.getAttributes();
213: for (Iterator iter = attributes.keySet().iterator(); iter
214: .hasNext();) {
215: String attName = (String) iter.next();
216: String attValue = replaceParam((String) attributes
217: .get(attName), attValues);
218: conf.setAttribute(attName, attValue);
219: }
220: for (Iterator iter = macroRecord.getChildren().iterator(); iter
221: .hasNext();) {
222: MacroRecord child = (MacroRecord) iter.next();
223: Element elt = (Element) elements.get(child.getName());
224: if (elt != null) {
225: List elements = (List) childrenRecords.get(child
226: .getName());
227: if (elements != null) {
228: for (Iterator iterator = elements.iterator(); iterator
229: .hasNext();) {
230: MacroRecord element = (MacroRecord) iterator
231: .next();
232: for (Iterator it2 = element.getChildren()
233: .iterator(); it2.hasNext();) {
234: MacroRecord r = (MacroRecord) it2
235: .next();
236: play(conf, r, attValues,
237: Collections.EMPTY_MAP);
238: }
239: }
240: } else if (!elt.isOptional()) {
241: throw new IllegalArgumentException(
242: "non optional element is not specified: "
243: + elt.getName() + " in macro "
244: + getName());
245: }
246: continue;
247: }
248: play(conf, child, attValues, childrenRecords);
249: }
250: return conf.endCreateChild();
251: }
252:
253: private String replaceParam(String string, Map attValues) {
254: return IvyPatternHelper.substituteParams(string, attValues);
255: }
256:
257: public String getName() {
258: return name;
259: }
260:
261: public void addConfiguredAttribute(Attribute att) {
262: attributes.put(att.getName(), att);
263: }
264:
265: public void addConfiguredElement(Element elt) {
266: elements.put(elt.getName(), elt);
267: }
268:
269: public Macro createMacro() {
270: return new Macro(this );
271: }
272:
273: public void addAttribute(String attName, String attDefaultValue) {
274: Attribute att = new Attribute();
275: att.setName(attName);
276: att.setDefault(attDefaultValue);
277: addConfiguredAttribute(att);
278: }
279:
280: public void addElement(String elementName, boolean optional) {
281: Element elt = new Element();
282: elt.setName(elementName);
283: elt.setOptional(optional);
284: addConfiguredElement(elt);
285: }
286:
287: public MacroRecord recordCreateChild(String name) {
288: macroRecord = new MacroRecord(name);
289: return macroRecord;
290: }
291: }
292:
293: private static class ObjectDescriptor {
294: private Object obj;
295:
296: private String objName;
297:
298: private Map createMethods = new HashMap();
299:
300: private Map addMethods = new HashMap();
301:
302: private Map addConfiguredMethods = new HashMap();
303:
304: private Map setMethods = new HashMap();
305:
306: private Map typeAddMethods = new HashMap();
307:
308: private Map typeAddConfiguredMethods = new HashMap();
309:
310: public ObjectDescriptor(Object object, String objName) {
311: obj = object;
312: this .objName = objName;
313: Method[] methods = object.getClass().getMethods();
314: for (int i = 0; i < methods.length; i++) {
315: Method m = methods[i];
316: if (m.getName().startsWith("create")
317: && m.getParameterTypes().length == 0
318: && !Void.TYPE.equals(m.getReturnType())) {
319: String name = StringUtils.uncapitalize(m.getName()
320: .substring("create".length()));
321: if (name.length() == 0) {
322: continue;
323: }
324: addCreateMethod(name, m);
325: } else if (m.getName().startsWith("addConfigured")
326: && m.getParameterTypes().length == 1
327: && Void.TYPE.equals(m.getReturnType())) {
328: String name = StringUtils.uncapitalize(m.getName()
329: .substring("addConfigured".length()));
330: if (name.length() == 0) {
331: addAddConfiguredMethod(m);
332: }
333: addAddConfiguredMethod(name, m);
334: } else if (m.getName().startsWith("add")
335: && !m.getName().startsWith("addConfigured")
336: && m.getParameterTypes().length == 1
337: && Void.TYPE.equals(m.getReturnType())) {
338: String name = StringUtils.uncapitalize(m.getName()
339: .substring("add".length()));
340: if (name.length() == 0) {
341: addAddMethod(m);
342: }
343: addAddMethod(name, m);
344: } else if (m.getName().startsWith("set")
345: && m.getParameterTypes().length == 1
346: && Void.TYPE.equals(m.getReturnType())) {
347: String name = StringUtils.uncapitalize(m.getName()
348: .substring("set".length()));
349: if (name.length() == 0) {
350: continue;
351: }
352: addSetMethod(name, m);
353: }
354: }
355: }
356:
357: public void addCreateMethod(String name, Method m) {
358: createMethods.put(name, m);
359: }
360:
361: public void addAddMethod(String name, Method m) {
362: addMethods.put(name, m);
363: }
364:
365: public void addAddConfiguredMethod(String name, Method m) {
366: addConfiguredMethods.put(name, m);
367: }
368:
369: private void addAddMethod(Method m) {
370: typeAddMethods.put(m.getParameterTypes()[0], m);
371: }
372:
373: private void addAddConfiguredMethod(Method m) {
374: typeAddConfiguredMethods.put(m.getParameterTypes()[0], m);
375: }
376:
377: public void addSetMethod(String name, Method m) {
378: Method current = (Method) setMethods.get(name);
379: if (current != null
380: && current.getParameterTypes()[0] == String.class) {
381: // setter methods with String attribute take precedence
382: return;
383: }
384: setMethods.put(name, m);
385: }
386:
387: public Object getObject() {
388: return obj;
389: }
390:
391: public Method getCreateMethod(String name) {
392: return (Method) createMethods.get(name);
393: }
394:
395: public Method getAddMethod(String name) {
396: return (Method) addMethods.get(name);
397: }
398:
399: public Method getAddConfiguredMethod(String name) {
400: return (Method) addConfiguredMethods.get(name);
401: }
402:
403: public Method getAddMethod(Class type) {
404: return getTypeMatchingMethod(type, typeAddMethods);
405: }
406:
407: public Method getAddConfiguredMethod(Class type) {
408: return getTypeMatchingMethod(type, typeAddConfiguredMethods);
409: }
410:
411: private Method getTypeMatchingMethod(Class type, Map typeMethods) {
412: Method m = (Method) typeMethods.get(type);
413: if (m != null) {
414: return m;
415: }
416: for (Iterator iter = typeMethods.keySet().iterator(); iter
417: .hasNext();) {
418: Class clss = (Class) iter.next();
419: if (clss.isAssignableFrom(type)) {
420: return (Method) typeMethods.get(clss);
421: }
422: }
423: return null;
424: }
425:
426: public Method getSetMethod(String name) {
427: return (Method) setMethods.get(name);
428: }
429:
430: public String getObjectName() {
431: return objName;
432: }
433: }
434:
435: private Map typedefs = new HashMap();
436:
437: private Map macrodefs = new HashMap();
438:
439: // stack in which the top is current configured object descriptor
440: private Stack objectStack = new Stack();
441:
442: private static final List TRUE_VALUES = Arrays.asList(new String[] {
443: "true", "yes", "on" });
444:
445: public void typeDef(String name, String className)
446: throws ClassNotFoundException {
447: typeDef(name, Class.forName(className));
448: }
449:
450: public void typeDef(String name, Class clazz) {
451: typedefs.put(name, clazz);
452: }
453:
454: public void setRoot(Object root) {
455: if (root == null) {
456: throw new NullPointerException();
457: }
458: objectStack.clear();
459: setCurrent(root, null);
460: }
461:
462: public void clear() {
463: objectStack.clear();
464: }
465:
466: private void setCurrent(Object object, String name) {
467: objectStack.push(new ObjectDescriptor(object, name));
468: }
469:
470: public Object startCreateChild(String name) {
471: if (objectStack.isEmpty()) {
472: throw new IllegalStateException(
473: "set root before creating child");
474: }
475: ObjectDescriptor parentOD = (ObjectDescriptor) objectStack
476: .peek();
477: Object parent = parentOD.getObject();
478: if (parent instanceof MacroDef) {
479: if (!"attribute".equals(name) && !"element".equals(name)) {
480: MacroRecord record = ((MacroDef) parent)
481: .recordCreateChild(name);
482: setCurrent(record, name);
483: return record;
484: }
485: }
486: if (parent instanceof Macro) {
487: MacroRecord record = ((Macro) parent)
488: .recordCreateChild(name);
489: setCurrent(record, name);
490: return record;
491: }
492: if (parent instanceof MacroRecord) {
493: MacroRecord record = ((MacroRecord) parent)
494: .recordChild(name);
495: setCurrent(record, name);
496: return record;
497: }
498: Object child = null;
499: MacroDef macrodef = (MacroDef) macrodefs.get(name);
500: if (macrodef != null) {
501: Macro macro = macrodef.createMacro();
502: setCurrent(macro, name);
503: return macro;
504: }
505: Class childClass = (Class) typedefs.get(name);
506: Method addChild = null;
507: try {
508: if (childClass != null) {
509: return addChild(parentOD, childClass, name, null);
510: } else {
511: addChild = parentOD.getCreateMethod(name);
512: if (addChild != null) {
513: child = addChild.invoke(parent, new Object[0]);
514: setCurrent(child, name);
515: return child;
516: }
517: addChild = parentOD.getAddMethod(name);
518: if (addChild != null) {
519: childClass = addChild.getParameterTypes()[0];
520: child = childClass.newInstance();
521: addChild.invoke(parent, new Object[] { child });
522: setCurrent(child, name);
523: return child;
524: }
525: addChild = parentOD.getAddConfiguredMethod(name);
526: if (addChild != null) {
527: childClass = addChild.getParameterTypes()[0];
528: if (Map.class == childClass) {
529: child = new HashMap();
530: } else {
531: child = childClass.newInstance();
532: }
533: setCurrent(child, name);
534: return child;
535: }
536: }
537: } catch (InstantiationException ex) {
538: throw new IllegalArgumentException(
539: "no default constructor on " + childClass
540: + " for adding " + name + " on "
541: + parent.getClass());
542: } catch (Exception ex) {
543: IllegalArgumentException iae = new IllegalArgumentException(
544: "bad method found for " + name + " on "
545: + parent.getClass());
546: iae.initCause(ex);
547: throw iae;
548: }
549: throw new IllegalArgumentException(
550: "no appropriate method found for adding " + name
551: + " on " + parent.getClass());
552: }
553:
554: public void addChild(String name, Object child) {
555: if (objectStack.isEmpty()) {
556: throw new IllegalStateException(
557: "set root before creating child");
558: }
559: ObjectDescriptor parentOD = (ObjectDescriptor) objectStack
560: .peek();
561: try {
562: addChild(parentOD, child.getClass(), name, child);
563: } catch (InstantiationException ex) {
564: throw new IllegalArgumentException(
565: "no default constructor on " + child.getClass()
566: + " for adding " + name + " on "
567: + parentOD.getObject().getClass());
568: } catch (Exception ex) {
569: IllegalArgumentException iae = new IllegalArgumentException(
570: "bad method found for " + name + " on "
571: + parentOD.getObject().getClass());
572: iae.initCause(ex);
573: throw iae;
574: }
575: }
576:
577: private Object addChild(ObjectDescriptor parentOD,
578: Class childClass, String name, Object child)
579: throws InstantiationException, IllegalAccessException,
580: InvocationTargetException {
581: Object parent = parentOD.getObject();
582: Method addChild;
583: addChild = parentOD.getAddMethod(childClass);
584: if (addChild != null) {
585: if (child == null) {
586: child = childClass.newInstance();
587: }
588: addChild.invoke(parent, new Object[] { child });
589: setCurrent(child, name);
590: return child;
591: }
592: addChild = parentOD.getAddConfiguredMethod(childClass);
593: if (addChild != null) {
594: if (child == null) {
595: if (Map.class == childClass) {
596: child = new HashMap();
597: } else {
598: child = childClass.newInstance();
599: }
600: }
601: setCurrent(child, name);
602: return child;
603: }
604: throw new IllegalArgumentException(
605: "no appropriate method found for adding " + name
606: + " on " + parent.getClass());
607: }
608:
609: public boolean isTopLevelMacroRecord() {
610: if (objectStack.isEmpty()) {
611: return false;
612: }
613: ObjectDescriptor od = (ObjectDescriptor) objectStack.peek();
614: return (od.getObject() instanceof MacroDef);
615: }
616:
617: public void setAttribute(String attributeName, String value) {
618: if (objectStack.isEmpty()) {
619: throw new IllegalStateException(
620: "set root before setting attribute");
621: }
622: ObjectDescriptor od = (ObjectDescriptor) objectStack.peek();
623: if (od.getObject() instanceof Macro) {
624: ((Macro) od.getObject()).defineAttribute(attributeName,
625: value);
626: return;
627: }
628: if (od.getObject() instanceof MacroRecord) {
629: ((MacroRecord) od.getObject()).recordAttribute(
630: attributeName, value);
631: return;
632: }
633: Method m = od.getSetMethod(attributeName);
634: if (m == null) {
635: if (od.getObject() instanceof Map) {
636: ((Map) od.getObject()).put(attributeName, value);
637: return;
638: }
639: throw new IllegalArgumentException(
640: "no set method found for " + attributeName + " on "
641: + od.getObject().getClass());
642: }
643: Object convertedValue = null;
644: Class paramClass = m.getParameterTypes()[0];
645: try {
646: if (paramClass.equals(String.class)) {
647: convertedValue = value;
648: } else if (paramClass.equals(Boolean.class)
649: || paramClass.equals(boolean.class)) {
650: convertedValue = Boolean.valueOf(TRUE_VALUES
651: .contains(value));
652: } else if (paramClass.equals(Character.class)
653: || paramClass.equals(char.class)) {
654: convertedValue = new Character(
655: value.length() > 0 ? value.charAt(0) : ' ');
656: } else if (paramClass.equals(Short.class)
657: || paramClass.equals(short.class)) {
658: convertedValue = Short.valueOf(value);
659: } else if (paramClass.equals(Integer.class)
660: || paramClass.equals(int.class)) {
661: convertedValue = Integer.valueOf(value);
662: } else if (paramClass.equals(Long.class)
663: || paramClass.equals(long.class)) {
664: convertedValue = Long.valueOf(value);
665: } else if (paramClass.equals(Class.class)) {
666: convertedValue = Class.forName(value);
667: } else {
668: convertedValue = paramClass.getConstructor(
669: new Class[] { String.class }).newInstance(
670: new Object[] { value });
671: }
672: } catch (Exception ex) {
673: IllegalArgumentException iae = new IllegalArgumentException(
674: "impossible to convert " + value + " to "
675: + paramClass + " for setting "
676: + attributeName + " on "
677: + od.getObject().getClass());
678: iae.initCause(ex);
679: throw iae;
680: }
681: try {
682: m.invoke(od.getObject(), new Object[] { convertedValue });
683: } catch (Exception ex) {
684: IllegalArgumentException iae = new IllegalArgumentException(
685: "impossible to set " + attributeName + " to "
686: + convertedValue + " on "
687: + od.getObject().getClass());
688: iae.initCause(ex);
689: throw iae;
690: }
691: }
692:
693: public void addText(String text) {
694: if (objectStack.isEmpty()) {
695: throw new IllegalStateException(
696: "set root before adding text");
697: }
698: ObjectDescriptor od = (ObjectDescriptor) objectStack.peek();
699: try {
700: od.getObject().getClass().getMethod("addText",
701: new Class[] { String.class }).invoke(
702: od.getObject(), new Object[] { text });
703: } catch (Exception ex) {
704: IllegalArgumentException iae = new IllegalArgumentException(
705: "impossible to add text on "
706: + od.getObject().getClass());
707: iae.initCause(ex);
708: throw iae;
709: }
710: }
711:
712: /**
713: * @return the finished child
714: */
715: public Object endCreateChild() {
716: if (objectStack.isEmpty()) {
717: throw new IllegalStateException(
718: "set root before ending child");
719: }
720: ObjectDescriptor od = (ObjectDescriptor) objectStack.pop();
721: if (objectStack.isEmpty()) {
722: objectStack.push(od); // back to previous state
723: throw new IllegalStateException("cannot end root");
724: }
725: if (od.getObject() instanceof Macro) {
726: return ((Macro) od.getObject()).play(this );
727: }
728: ObjectDescriptor parentOD = (ObjectDescriptor) objectStack
729: .peek();
730: String name = od.getObjectName();
731: Class childClass = (Class) typedefs.get(name);
732: Method m = null;
733: if (childClass != null) {
734: m = parentOD.getAddConfiguredMethod(childClass);
735: } else {
736: m = parentOD.getAddConfiguredMethod(name);
737: }
738: try {
739: if (m != null) {
740: m.invoke(parentOD.getObject(), new Object[] { od
741: .getObject() });
742: }
743: return od.getObject();
744: } catch (Exception ex) {
745: IllegalArgumentException iae = new IllegalArgumentException(
746: "impossible to add configured child for " + name
747: + " on " + parentOD.getObject().getClass());
748: iae.initCause(ex);
749: throw iae;
750: }
751: }
752:
753: public Object getCurrent() {
754: return objectStack.isEmpty() ? null
755: : ((ObjectDescriptor) objectStack.peek()).getObject();
756: }
757:
758: public int getDepth() {
759: return objectStack.size();
760: }
761:
762: public MacroDef startMacroDef(String macroName) {
763: MacroDef macroDef = new MacroDef(macroName);
764: setCurrent(macroDef, macroName);
765: return macroDef;
766: }
767:
768: public void addMacroAttribute(String attName, String attDefaultValue) {
769: ((MacroDef) getCurrent())
770: .addAttribute(attName, attDefaultValue);
771: }
772:
773: public void addMacroElement(String elementName, boolean optional) {
774: ((MacroDef) getCurrent()).addElement(elementName, optional);
775: }
776:
777: public void endMacroDef() {
778: addConfiguredMacrodef(((MacroDef) getCurrent()));
779: objectStack.pop();
780: }
781:
782: public void addConfiguredMacrodef(MacroDef macrodef) {
783: macrodefs.put(macrodef.getName(), macrodef);
784: }
785:
786: public Class getTypeDef(String name) {
787: return (Class) typedefs.get(name);
788: }
789: }
|