001: /*
002: Copyright (c) 2007, Dennis M. Sosnoski
003: All rights reserved.
004:
005: Redistribution and use in source and binary forms, with or without modification,
006: are permitted provided that the following conditions are met:
007:
008: * Redistributions of source code must retain the above copyright notice, this
009: list of conditions and the following disclaimer.
010: * Redistributions in binary form must reproduce the above copyright notice,
011: this list of conditions and the following disclaimer in the documentation
012: and/or other materials provided with the distribution.
013: * Neither the name of JiBX nor the names of its contributors may be used
014: to endorse or promote products derived from this software without specific
015: prior written permission.
016:
017: THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
018: ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
019: WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
020: DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
021: ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
022: (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
023: LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
024: ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
025: (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
026: SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
027: */
028:
029: package org.jibx.binding.generator;
030:
031: import java.util.ArrayList;
032: import java.util.Collections;
033: import java.util.HashMap;
034: import java.util.Iterator;
035: import java.util.List;
036: import java.util.Map;
037:
038: import org.jibx.binding.classes.ClassCache;
039: import org.jibx.binding.model.ClassWrapper;
040: import org.jibx.binding.model.IClass;
041: import org.jibx.binding.model.IClassLocator;
042: import org.jibx.runtime.EnumSet;
043: import org.jibx.runtime.IBindingFactory;
044: import org.jibx.runtime.IUnmarshaller;
045: import org.jibx.runtime.IUnmarshallingContext;
046: import org.jibx.runtime.JiBXException;
047: import org.jibx.runtime.impl.UnmarshallingContext;
048:
049: /**
050: * Global customization information. This includes some options specific to the
051: * <binding> element of the definition, as well as controls for structuring
052: * of the generated binding(s). It handles the binding customization child
053: * elements directly, by invoking the abstract unmarshallers for the child
054: * elements to process the content. It also allows for extension elements which
055: * are not part of the binding customization structure, as long as the binding
056: * in use defines the unmarshalling for these elements.
057: */
058: public class GlobalCustom extends NestingBase {
059: /** Element name in XML customization file. */
060: public static final String ELEMENT_NAME = "custom";
061:
062: //
063: // Value set information
064:
065: public static final int IN_BINDING = 0;
066: public static final int OUT_BINDING = 1;
067: public static final int BOTH_BINDING = 2;
068:
069: /*package*/static final EnumSet s_directionEnum = new EnumSet(
070: IN_BINDING, new String[] { "input", "output", "both" });
071:
072: //
073: // Instance data
074:
075: // structure for package hierarchy
076: private Map m_packageMap;
077:
078: // class locator
079: private final IClassLocator m_classLocator;
080:
081: // extension elements
082: private List m_extensionChildren;
083:
084: // values applied directly to <binding> element (or to structure)
085: private boolean m_addConstructors;
086: private boolean m_forceClasses;
087: private boolean m_trackSource;
088: private boolean m_namespaceModular;
089: private boolean m_isInput;
090: private boolean m_isOutput;
091:
092: /**
093: * Constructor with class locator supplied.
094: *
095: * @param loc
096: */
097: public GlobalCustom(IClassLocator loc) {
098: super (null);
099: m_packageMap = new HashMap();
100: m_classLocator = loc;
101: PackageCustom dflt = new PackageCustom("", "", this );
102: m_packageMap.put("", dflt);
103: m_isInput = true;
104: m_isOutput = true;
105: }
106:
107: /**
108: * Constructor. This always creates the default package as the only direct
109: * child, since other packages will be treated as children of the default
110: * package.
111: */
112: public GlobalCustom() {
113: this (new IClassLocator() {
114: public IClass getClassInfo(String name) {
115: try {
116: return new ClassWrapper(this , ClassCache
117: .getClassFile(name));
118: } catch (JiBXException e) {
119: throw new IllegalStateException("Class not found "
120: + name);
121: }
122: }
123: });
124: }
125:
126: /**
127: * Get global customizations root.
128: *
129: * @return global customization
130: */
131: public GlobalCustom getGlobal() {
132: return this ;
133: }
134:
135: //
136: // Access methods for values applied only at top level
137:
138: /**
139: * Get 'add-constructors' setting.
140: *
141: * @return 'add-constructors' value
142: */
143: public boolean isAddConstructors() {
144: return m_addConstructors;
145: }
146:
147: /**
148: * Set 'add-constructors' value.
149: *
150: * @param add 'add-constructors' value
151: */
152: public void setAddConstructors(boolean add) {
153: m_addConstructors = add;
154: }
155:
156: /**
157: * Get 'force-classes' setting.
158: *
159: * @return 'force-classes' value
160: */
161: public boolean isForceClasses() {
162: return m_forceClasses;
163: }
164:
165: /**
166: * Set 'force-classes' value.
167: *
168: * @param force 'force-classes' value
169: */
170: public void setForceClasses(boolean force) {
171: m_forceClasses = force;
172: }
173:
174: /**
175: * Get 'track-source' attribute value.
176: *
177: * @return 'track-source' value
178: */
179: public boolean isTrackSource() {
180: return m_trackSource;
181: }
182:
183: /**
184: * Set 'track-source' value.
185: *
186: * @param track 'track-source' value
187: */
188: public void setTrackSource(boolean track) {
189: m_trackSource = track;
190: }
191:
192: /**
193: * Check if using namespace modular bindings.
194: *
195: * @return flag for bindings to be modular by namespace
196: */
197: public boolean isNamespaceModular() {
198: return m_namespaceModular;
199: }
200:
201: /**
202: * Check for an input binding.
203: *
204: * @return input flag
205: */
206: public boolean isInput() {
207: return m_isInput;
208: }
209:
210: /**
211: * Set input binding flag.
212: *
213: * @param input
214: */
215: public void setInput(boolean input) {
216: m_isInput = input;
217: }
218:
219: /**
220: * Check for an output binding.
221: *
222: * @return output flag
223: */
224: public boolean isOutput() {
225: return m_isOutput;
226: }
227:
228: /**
229: * Set output binding falg.
230: *
231: * @param output
232: */
233: public void setOutput(boolean output) {
234: m_isOutput = output;
235: }
236:
237: /**
238: * Get class locator.
239: *
240: * @return locator
241: */
242: protected IClassLocator getClassLocator() {
243: return m_classLocator;
244: }
245:
246: /**
247: * Get class information.
248: *
249: * @param type fully-qualified class name
250: * @return information
251: */
252: public IClass getClassInfo(String type) {
253: return m_classLocator.getClassInfo(type);
254: }
255:
256: /**
257: * Get the extension elements used in this customization. This does not
258: * include the <package> or <class> child elements, which are
259: * added directly to the customization structures.
260: *
261: * @return child list
262: */
263: public List getExtensionChildren() {
264: if (m_extensionChildren == null) {
265: return Collections.EMPTY_LIST;
266: } else {
267: return m_extensionChildren;
268: }
269: }
270:
271: /**
272: * Internal method used during unmarshalling to add a child extension
273: * element.
274: *
275: * @param child
276: */
277: protected void internalAddExtensionChild(Object child) {
278: if (m_extensionChildren == null) {
279: m_extensionChildren = new ArrayList();
280: }
281: m_extensionChildren.add(child);
282: }
283:
284: /**
285: * Add a child extension element. This both adds the child to the list and
286: * invokeds the extension element's {@link IApply#apply(IClassLocator)}
287: * method, if present.
288: *
289: * @param child
290: */
291: public void addExtensionChild(Object child) {
292: internalAddExtensionChild(child);
293: if (child instanceof IApply) {
294: ((IApply) child).apply(m_classLocator);
295: }
296: }
297:
298: /**
299: * Check if a class is included in the customization information. This
300: * method does not alter the structures in any way, it only checks if the
301: * class customization information is part of the existing structure.
302: *
303: * @param type fully qualified class name
304: * @return <code>true</code> if class includes, <code>false</code> if not
305: */
306: public boolean isClassUsed(String type) {
307: int split = type.lastIndexOf('.');
308: if (split < 0) {
309: split = 0;
310: }
311: PackageCustom pack = (PackageCustom) m_packageMap.get(type
312: .substring(0, split));
313: if (pack == null) {
314: return false;
315: } else {
316: return pack
317: .getClassCustomization(type.substring(split + 1)) != null;
318: }
319: }
320:
321: /**
322: * Get class customization information.
323: *
324: * @param type fully qualified class name
325: * @return class information (<code>null</code> if not defined)
326: */
327: public ClassCustom getClassCustomization(String type) {
328: int split = type.lastIndexOf('.');
329: if (split < 0) {
330: split = 0;
331: }
332: PackageCustom pack = (PackageCustom) m_packageMap.get(type
333: .substring(0, split));
334: if (pack == null) {
335: return null;
336: } else {
337: return pack
338: .getClassCustomization(type.substring(split + 1));
339: }
340: }
341:
342: /**
343: * Add new class customization information. This creates the customization
344: * information and adds it to the internal structures, initializing all
345: * values based on the settings inherited from <package> and <global>
346: * elements of the structure. This method should only be used after first
347: * calling {@link #getClassCustomization(String)} and obtaining a
348: * <code>null</code> result.
349: *
350: * @param type fully qualified class name
351: * @return class information
352: */
353: public ClassCustom addClassCustomization(String type) {
354: int split = type.lastIndexOf('.');
355: if (split < 0) {
356: split = 0;
357: }
358: PackageCustom pack = getPackage(type.substring(0, split));
359: ClassCustom clas = pack.addClassCustomization(type
360: .substring(split + 1));
361: clas.apply(m_classLocator);
362: return clas;
363: }
364:
365: /**
366: * Get class customization information, creating it if it doesn't already
367: * exist.
368: *
369: * @param type fully qualified class name
370: * @return class information
371: */
372: public ClassCustom forceClassCustomization(String type) {
373: ClassCustom custom = getClassCustomization(type);
374: if (custom == null) {
375: custom = addClassCustomization(type);
376: }
377: return custom;
378: }
379:
380: /**
381: * Check if type represents a known mapping.
382: *
383: * @param type fully qualified class name
384: * @return known mapping flag
385: */
386: public boolean isKnownMapping(String type) {
387: // TODO: add known mappings for org.w3c.dom.*, etc.
388: return false;
389: }
390:
391: /**
392: * Direction set text method. This is intended for use during unmarshalling.
393: * TODO: add validation
394: *
395: * @param text
396: * @param ictx
397: */
398: private void setDirectionText(String text,
399: IUnmarshallingContext ictx) {
400: int direct = s_directionEnum.getValue(text);
401: if (direct < 0) {
402: throw new IllegalArgumentException("Value '" + text
403: + "' not recognized for 'direction' attribute");
404: } else {
405: m_isInput = direct != OUT_BINDING;
406: m_isOutput = direct != IN_BINDING;
407: }
408: }
409:
410: /**
411: * Direction get text method. This is intended for use during marshalling.
412: *
413: * @return text
414: */
415: private String getDirectionText() {
416: if (m_isInput && m_isOutput) {
417: return s_directionEnum.getName(BOTH_BINDING);
418: } else if (m_isInput) {
419: return s_directionEnum.getName(IN_BINDING);
420: } else {
421: return s_directionEnum.getName(OUT_BINDING);
422: }
423: }
424:
425: /**
426: * Fills in class information based on inspection of the actual class data.
427: * This needs to be done as a separate step following unmarshalling, so that
428: * the full details of the unmarshalled customizations are available.
429: */
430: public void fillClasses() {
431:
432: // fix the namespace used as base for packages (if not set by override)
433: if (getNamespace() == null) {
434: setNamespace(getSpecifiedNamespace());
435: }
436:
437: // create information for some special classes
438: ClassCustom custom = forceClassCustomization("java.util.List");
439: if (custom.getCreateType() == null
440: && custom.getFactoryMethod() == null) {
441: custom.setCreateType("java.util.ArrayList");
442: }
443: custom = forceClassCustomization("java.util.Set");
444: if (custom.getCreateType() == null
445: && custom.getFactoryMethod() == null) {
446: custom.setCreateType("java.util.HashSet");
447: }
448: custom = forceClassCustomization("java.util.Collection");
449: if (custom.getCreateType() == null
450: && custom.getFactoryMethod() == null) {
451: custom.setCreateType("java.util.ArrayList");
452: }
453:
454: // fill in details for all packages
455: for (Iterator iter = m_packageMap.values().iterator(); iter
456: .hasNext();) {
457: PackageCustom pack = (PackageCustom) iter.next();
458: pack.apply(m_classLocator);
459: }
460:
461: // fill in details for any extension elements
462: if (m_extensionChildren != null) {
463: for (int i = 0; i < m_extensionChildren.size(); i++) {
464: Object child = m_extensionChildren.get(i);
465: if (child instanceof IApply) {
466: ((IApply) child).apply(m_classLocator);
467: }
468: }
469: }
470: }
471:
472: //
473: // Package structure support
474:
475: /**
476: * Get package customizations. If the requested package is already defined
477: * the existing instance will be returned, otherwise a new instance will be
478: * created (along with any ancestor packages) and added to the structure.
479: *
480: * @param name
481: * @return package
482: */
483: public PackageCustom getPackage(String name) {
484:
485: // find existing package information
486: PackageCustom pack = (PackageCustom) m_packageMap.get(name);
487: if (pack == null) {
488:
489: // create new package information
490: PackageCustom parent = null;
491: int split = name.lastIndexOf('.');
492: String simple = name;
493: String full = name;
494: if (split > 0) {
495: parent = getPackage(name.substring(0, split));
496: simple = name.substring(split + 1);
497: full = parent.getName() + '.' + simple;
498: } else if (name.length() > 0) {
499: parent = getPackage("");
500: }
501: pack = new PackageCustom(simple, full, parent);
502: m_packageMap.put(name, pack);
503: pack.apply(m_classLocator);
504: }
505: return pack;
506: }
507:
508: /**
509: * Unmarshaller implementation for class. This handles the nested structure
510: * of packages and classes, using the abstract mappings defined by the
511: * binding to handle all the actual details.
512: */
513: public static class Mapper implements IUnmarshaller {
514: private int m_packageIndex;
515: private int m_classIndex;
516:
517: public boolean isPresent(IUnmarshallingContext ictx)
518: throws JiBXException {
519: return true;
520: }
521:
522: /**
523: * Build the fully-qualified name for a package or class by appending
524: * the supplied name attribute value to the fully-qualified name of the
525: * containing package.
526: *
527: * @param contain
528: * @param ctx
529: * @throws JiBXException
530: */
531: private String buildFullName(PackageCustom contain,
532: UnmarshallingContext ctx) throws JiBXException {
533: String lead = "";
534: if (contain != null) {
535: lead = contain.getName() + '.';
536: }
537: return lead + ctx.attributeText(null, "name");
538: }
539:
540: private PackageCustom unmarshalPackage(GlobalCustom global,
541: PackageCustom contain, UnmarshallingContext ctx)
542: throws JiBXException {
543:
544: // create class instance and populate mapped values
545: PackageCustom inst = global.getPackage(buildFullName(
546: contain, ctx));
547: ctx.pushTrackedObject(inst);
548: ctx.getUnmarshaller(m_packageIndex).unmarshal(inst, ctx);
549:
550: // handle nested package and class information
551: while (ctx.isStart()) {
552: String element = ctx.getName();
553: if (PackageCustom.ELEMENT_NAME.equals(element)) {
554: unmarshalPackage(global, inst, ctx);
555: } else if (ClassCustom.ELEMENT_NAME.equals(element)) {
556: unmarshalClass(global, inst, ctx);
557: }
558: }
559: ctx.popObject();
560: ctx.parsePastEndTag(null, PackageCustom.ELEMENT_NAME);
561: return inst;
562: }
563:
564: private ClassCustom unmarshalClass(GlobalCustom global,
565: PackageCustom contain, UnmarshallingContext ctx)
566: throws JiBXException {
567:
568: // create class instance and populate mapped values
569: String name = buildFullName(contain, ctx);
570: int split = name.lastIndexOf('.');
571: if (split < 0) {
572: split = 0;
573: }
574: PackageCustom pack = global.getPackage(name.substring(0,
575: split));
576: String simple = name.substring(split + 1);
577: ClassCustom inst = pack.getClassCustomization(simple);
578: if (inst == null) {
579: inst = pack.addClassCustomization(simple);
580: }
581: ctx.pushTrackedObject(inst);
582: ctx.getUnmarshaller(m_classIndex).unmarshal(inst, ctx);
583: ctx.popObject();
584: ctx.parsePastEndTag(null, ClassCustom.ELEMENT_NAME);
585: return inst;
586: }
587:
588: public Object unmarshal(Object obj, IUnmarshallingContext ictx)
589: throws JiBXException {
590:
591: // set up the mapping indexes
592: UnmarshallingContext ctx = (UnmarshallingContext) ictx;
593: IBindingFactory factory = ((UnmarshallingContext) ictx)
594: .getFactory();
595: m_packageIndex = factory
596: .getTypeIndex(PackageCustom.ELEMENT_NAME);
597: m_classIndex = factory
598: .getTypeIndex(ClassCustom.ELEMENT_NAME);
599: if (m_packageIndex < 0 || m_classIndex < 0) {
600: throw new JiBXException(
601: "Missing required abstract mappings");
602: }
603:
604: // process all child elements
605: GlobalCustom global = (GlobalCustom) obj;
606: while (ctx.isStart()) {
607:
608: // check type of child present
609: String element = ctx.getName();
610: if (PackageCustom.ELEMENT_NAME.equals(element)) {
611: unmarshalPackage(global, null, ctx);
612: } else if (ClassCustom.ELEMENT_NAME.equals(element)) {
613: unmarshalClass(global, null, ctx);
614: } else {
615: global.internalAddExtensionChild(ctx
616: .unmarshalElement());
617: }
618: }
619: return global;
620: }
621: }
622: }
|