001: /*
002: Copyright (c) 2003-2006, 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.def;
030:
031: import java.util.ArrayList;
032: import java.util.HashMap;
033:
034: import org.jibx.binding.classes.ClassFile;
035: import org.jibx.binding.classes.MethodBuilder;
036: import org.jibx.binding.util.ArrayMap;
037:
038: import org.jibx.runtime.JiBXException;
039: import org.jibx.runtime.QName;
040:
041: /**
042: * Nesting level for definitions in binding. This tracks namespace and mapping
043: * definitions that apply to all enclosed items.
044: *
045: * @author Dennis M. Sosnoski
046: * @version 1.0
047: */
048:
049: public class DefinitionContext {
050: /** Containing binding definition component. */
051: private final IContainer m_container;
052:
053: /** Containing definition context. */
054: private final DefinitionContext m_context;
055:
056: /** Namespace used by default at this level for attributes. */
057: private NamespaceDefinition m_attributeDefault;
058:
059: /** Namespace used by default at this level for elements. */
060: private NamespaceDefinition m_elementDefault;
061:
062: /** Namespaces defined at level (lazy create). */
063: private ArrayList m_namespaces;
064:
065: /** Mapping from prefix to namespace definition (lazy create). */
066: private HashMap m_prefixMap;
067:
068: /** Mapping from URI to namespace definition (lazy create). */
069: private HashMap m_uriMap;
070:
071: /** Mapping from fully qualified class name to mapping index (lazy
072: create). */
073: private ArrayMap m_classMap;
074:
075: /** Class mappings defined at level (lazy create). */
076: private ArrayList m_mappings;
077:
078: /** Map from signatures to <code>String</code> conversions. */
079: private HashMap m_convertMap;
080:
081: /** Map from format qnames to <code>String</code> conversions. */
082: private HashMap m_formatMap;
083:
084: /** Named binding components (only for root context of a binding). */
085: private HashMap m_namedStructureMap;
086:
087: /**
088: * Constructor. Uses the containing context to establish the hierarchy for
089: * resolving namespaces and class mappings.
090: *
091: * @param contain containing binding definition component
092: */
093:
094: public DefinitionContext(IContainer contain) {
095: m_container = contain;
096: m_context = contain.getDefinitionContext();
097: // TODO: make these lazy
098: m_convertMap = new HashMap();
099: m_formatMap = new HashMap();
100: if (m_context == null) {
101: m_namedStructureMap = new HashMap();
102: }
103: }
104:
105: /**
106: * Check for duplicate or conflicting namespace. This also intializes the
107: * namespace structures for this context the first time the method is
108: * called.
109: *
110: * @param def
111: * @return duplicate flag (either complete duplicate, or prior definition
112: * of same URI with prefix is present)
113: * @throws JiBXException on conflicting prefix
114: */
115: private boolean checkDuplicateNamespace(NamespaceDefinition def)
116: throws JiBXException {
117:
118: // create structures if not already done
119: if (m_namespaces == null) {
120: m_namespaces = new ArrayList();
121: m_prefixMap = new HashMap();
122: m_uriMap = new HashMap();
123: }
124:
125: // check for conflict (or duplicate) on prefix
126: String uri = def.getUri();
127: String prefix = def.getPrefix();
128: NamespaceDefinition dup = (NamespaceDefinition) m_prefixMap
129: .get(prefix);
130: DefinitionContext ctx = this ;
131: while (dup == null && (ctx = ctx.m_context) != null) {
132: if (ctx.m_prefixMap != null) {
133: dup = (NamespaceDefinition) ctx.m_prefixMap.get(prefix);
134: }
135: }
136: if (dup == null) {
137:
138: // check for duplicate definition of same URI, but with prefix
139: NamespaceDefinition prior = (NamespaceDefinition) m_uriMap
140: .get(uri);
141: if (prior != null && prior.getPrefix() != null) {
142: return true;
143: } else {
144: return false;
145: }
146:
147: } else {
148:
149: // check for repeated definition of same namespace
150: if (uri.equals(dup.getUri())) {
151: return true;
152: } else {
153: throw new JiBXException("Namespace prefix conflict");
154: }
155: }
156: }
157:
158: /**
159: * Add namespace to internal tables.
160: *
161: * @param def
162: */
163: private void internalAddNamespace(NamespaceDefinition def) {
164: String uri = def.getUri();
165: String prefix = def.getPrefix();
166: def.setIndex(m_container.getBindingRoot().getNamespaceUriIndex(
167: uri, prefix));
168: m_namespaces.add(def);
169: m_prefixMap.put(prefix, def);
170: m_uriMap.put(uri, def);
171: }
172:
173: /**
174: * Add namespace to set defined at this level. If the new namespace
175: * conflicts with an existing namespace at this level (in terms of default
176: * usage or prefix) this throws an exception.
177: *
178: * @param def namespace definition to be added (duplicates ignored)
179: * @throws JiBXException on namespace definition conflict
180: */
181: public void addNamespace(NamespaceDefinition def)
182: throws JiBXException {
183: if (!checkDuplicateNamespace(def)) {
184:
185: // check for conflict as default for attributes
186: if (def.isAttributeDefault()) {
187: if (m_attributeDefault == null) {
188: m_attributeDefault = def;
189: } else {
190: throw new JiBXException(
191: "Multiple default attribute namespaces at level");
192: }
193: }
194:
195: // check for conflict as default for elements
196: if (def.isElementDefault()) {
197: if (m_elementDefault == null) {
198: m_elementDefault = def;
199: } else {
200: throw new JiBXException(
201: "Multiple default element namespaces at level");
202: }
203: }
204:
205: // no conflicts, add it
206: internalAddNamespace(def);
207: }
208: }
209:
210: /**
211: * Add namespace declaration to set defined at this level. This method
212: * treats all namespaces as though they were declared with default="none".
213: * If the new namespace prefix conflicts with an existing namespace this
214: * throws an exception.
215: *
216: * @param def namespace definition to be added (duplicates ignored)
217: * @throws JiBXException on namespace definition conflict
218: */
219: public void addImpliedNamespace(NamespaceDefinition def)
220: throws JiBXException {
221: if (!checkDuplicateNamespace(def)) {
222: internalAddNamespace(def);
223: }
224: }
225:
226: /**
227: * Add class mapping to set defined at this level. If the new mapping
228: * conflicts with an existing one at this level it throws an exception.
229: *
230: * @param def mapping definition to be added
231: * @throws JiBXException on mapping definition conflict
232: */
233:
234: public void addMapping(IMapping def) throws JiBXException {
235:
236: // create structure if not already done
237: if (m_mappings == null) {
238: m_classMap = new ArrayMap();
239: m_mappings = new ArrayList();
240: }
241:
242: // check for conflict on class name before adding to definitions
243: String name = def.getTypeName();
244: if (name == null) {
245: name = def.getReferenceType();
246: }
247: int index = m_classMap.findOrAdd(name);
248: if (index < m_mappings.size()) {
249: if (def.getTypeName() == null) {
250: throw new JiBXException(
251: "Conflicting mappings for class " + name);
252: } else {
253: throw new JiBXException(
254: "Conflicting mappings for type name " + name);
255: }
256: } else {
257: m_mappings.add(def);
258: }
259: }
260:
261: /**
262: * Add named structure component to set defined at this level. If the name
263: * conflicts with an existing one at this level it throws an exception.
264: *
265: * @param name component name to be set
266: * @param comp named component
267: * @throws JiBXException on mapping definition conflict
268: */
269:
270: public void addNamedStructure(String name, IComponent comp)
271: throws JiBXException {
272: if (m_namedStructureMap == null) {
273: m_context.addNamedStructure(name, comp);
274: } else {
275: m_namedStructureMap.put(name, comp);
276: }
277: }
278:
279: /**
280: * Get the default namespace for a contained name. Elements and attributes
281: * are treated separately, since namespace handling differs between the two.
282: *
283: * @param attr flag for attribute name
284: * @return default namespace URI, or <code>null</code> if none
285: */
286:
287: private NamespaceDefinition getDefaultNamespace(boolean attr) {
288: NamespaceDefinition ns;
289: if (attr) {
290: ns = m_attributeDefault;
291: } else {
292: ns = m_elementDefault;
293: }
294: if (ns == null && m_context != null) {
295: ns = m_context.getDefaultNamespace(attr);
296: }
297: return ns;
298: }
299:
300: /**
301: * Get the default namespace URI for a contained name. Elements and
302: * attributes are treated separately, since namespace handling differs
303: * between the two.
304: *
305: * @param attr flag for attribute name
306: * @return default namespace URI, or <code>null</code> if none
307: */
308:
309: public String getDefaultURI(boolean attr) {
310: NamespaceDefinition ns = getDefaultNamespace(attr);
311: if (ns == null) {
312: return null;
313: } else {
314: return ns.getUri();
315: }
316: }
317:
318: /**
319: * Get the default namespace index for a contained name. Elements and
320: * attributes are treated separately, since namespace handling differs
321: * between the two.
322: *
323: * @param attr flag for attribute name
324: * @return default namespace index
325: */
326:
327: public int getDefaultIndex(boolean attr) {
328: NamespaceDefinition ns = getDefaultNamespace(attr);
329: if (ns == null) {
330: return 0;
331: } else {
332: return ns.getIndex();
333: }
334: }
335:
336: /**
337: * Get namespace index for a given URI. Finds the prefix for a URI in a
338: * name contained by this level, throwing an exception if the URI is not
339: * found or does not have a prefix.
340: *
341: * @param uri namespace URI to be found
342: * @param attr flag for attribute name
343: * @return namespace index for URI
344: * @throws JiBXException if URI not defined or not usable
345: */
346:
347: public int getNamespaceIndex(String uri, boolean attr)
348: throws JiBXException {
349:
350: // check for namespace URI defined at this level
351: if (m_uriMap != null) {
352: Object value = m_uriMap.get(uri);
353: if (value != null) {
354: if (!attr
355: || ((NamespaceDefinition) value).getPrefix() != null) {
356: return ((NamespaceDefinition) value).getIndex();
357: }
358: }
359: }
360:
361: // if all else fails, try the higher level
362: if (m_context == null) {
363: throw new JiBXException("Namespace URI \"" + uri
364: + "\" not defined or not usable");
365: } else {
366: return m_context.getNamespaceIndex(uri, attr);
367: }
368: }
369:
370: /**
371: * Get mapping definition for class if defined at this level.
372: *
373: * @param name fully qualified class name
374: * @return mapping definition for class, or <code>null</code> if not defined
375: */
376:
377: public IMapping getMappingAtLevel(String name) {
378:
379: // check for class mapping defined at this level
380: if (m_classMap != null) {
381:
382: // check for definition at this level
383: int index = m_classMap.find(name);
384: if (index >= 0) {
385: return (IMapping) m_mappings.get(index);
386: }
387: }
388: return null;
389: }
390:
391: /**
392: * Get mapping definition for class. Finds the mapping for a fully
393: * qualified class name, throwing an exception if no mapping is defined.
394: * This can only be used during the linkage phase.
395: *
396: * @param name fully qualified class name
397: * @return mapping definition for class, or <code>null</code> if not defined
398: */
399:
400: public IMapping getClassMapping(String name) {
401:
402: // check for class mapping defined at this level
403: IMapping def = getMappingAtLevel(name);
404: if (def == null && m_context != null) {
405:
406: // try finding definition at higher level
407: def = m_context.getClassMapping(name);
408:
409: }
410: return def;
411: }
412:
413: /**
414: * Get nested structure by name. Finds the nested structure with the given
415: * name, throwing an exception if no component with that name is defined.
416: *
417: * @param name component name to be found
418: * @return component with given name
419: * @throws JiBXException if name not defined
420: */
421:
422: public IComponent getNamedStructure(String name)
423: throws JiBXException {
424:
425: // check for named component defined at this level
426: IComponent comp = null;
427: if (m_namedStructureMap != null) {
428: comp = (IComponent) m_namedStructureMap.get(name);
429: }
430: if (comp == null) {
431: if (m_context == null) {
432: throw new JiBXException("Referenced label \"" + name
433: + "\" not defined");
434: } else {
435: comp = m_context.getNamedStructure(name);
436: }
437: }
438: return comp;
439: }
440:
441: /**
442: * Get mapping definitions at level.
443: *
444: * @return mapping definitions, <code>null</code> if none defined at level
445: */
446:
447: public ArrayList getMappings() {
448: return m_mappings;
449: }
450:
451: /**
452: * Get specific conversion definition for type. Finds with an exact match
453: * on the class name, checking the containing definitions if a conversion
454: * is not found at this level.
455: *
456: * @param name fully qualified class name to be converted
457: * @return conversion definition for class, or <code>null</code> if not
458: * found
459: */
460:
461: public StringConversion getSpecificConversion(String name) {
462: StringConversion conv = (StringConversion) m_convertMap
463: .get(name);
464: if (conv == null && m_context != null) {
465: conv = m_context.getSpecificConversion(name);
466: }
467: return conv;
468: }
469:
470: /**
471: * Get conversion definition for class. Finds the conversion based on a
472: * fully qualified class name. If a specific conversion for the actual
473: * class is not found (either in this or a containing level) this returns
474: * the generic object conversion.
475: *
476: * @param clas information for target conversion class
477: * @return conversion definition for class
478: */
479:
480: public StringConversion getConversion(ClassFile clas) {
481:
482: // use conversions for superclasses only
483: StringConversion conv = getSpecificConversion(clas.getName());
484: if (conv == null) {
485: return BindingDefinition.s_objectConversion;
486: } else {
487: return conv;
488: }
489: }
490:
491: /**
492: * Get named conversion definition. Finds the conversion with the supplied
493: * name, checking the containing definitions if the conversion is not found
494: * at this level.
495: *
496: * @param name conversion name to be found
497: * @return conversion definition for class
498: */
499:
500: public StringConversion getNamedConversion(QName name) {
501: StringConversion conv = (StringConversion) m_formatMap
502: .get(name);
503: if (conv == null && m_context != null) {
504: conv = m_context.getNamedConversion(name);
505: }
506: return conv;
507: }
508:
509: /**
510: * Add named conversion. Checks for duplicate conversions defined within
511: * a level with the same name.
512: *
513: * @param name format name for this conversion
514: * @param conv conversion definition for class
515: * @throws JiBXException if duplicate conversion definition
516: */
517:
518: public void addConversion(QName name, StringConversion conv)
519: throws JiBXException {
520: if (m_formatMap.put(name, conv) != null) {
521: throw new JiBXException(
522: "Duplicate conversion defined with name " + name);
523: }
524: }
525:
526: /**
527: * Set specific conversion definition for type. Sets the conversion based
528: * on a type signature, checking for duplicate conversions defined within
529: * a level.
530: *
531: * @param conv conversion definition for class
532: * @throws JiBXException if duplicate conversion definition
533: */
534:
535: public void setConversion(StringConversion conv)
536: throws JiBXException {
537: if (m_convertMap.put(conv.getTypeName(), conv) != null) {
538: throw new JiBXException(
539: "Duplicate conversion defined for type "
540: + conv.getTypeName());
541: }
542: }
543:
544: /**
545: * Sets a named conversion definition.
546: *
547: * @param name format name for this conversion
548: * @param conv conversion definition for class
549: * @throws JiBXException if duplicate conversion definition
550: */
551:
552: public void setNamedConversion(QName name, StringConversion conv)
553: throws JiBXException {
554: addConversion(name, conv);
555: }
556:
557: /**
558: * Sets a conversion definition by both type and name. Both the type and
559: * name are checked for duplicate conversions defined within a level.
560: *
561: * @param name format name for this conversion
562: * @param conv conversion definition for class
563: * @throws JiBXException if duplicate conversion definition
564: */
565:
566: public void setDefaultConversion(QName name, StringConversion conv)
567: throws JiBXException {
568: addConversion(name, conv);
569: setConversion(conv);
570: }
571:
572: /**
573: * Check if one or more namespaces are defined in this context.
574: *
575: * @return <code>true</code> if namespaces are defined, <code>false</code>
576: * if not
577: */
578: public boolean hasNamespace() {
579: return m_namespaces != null && m_namespaces.size() > 0;
580: }
581:
582: /**
583: * Get the namespaces defined in this context
584: *
585: * @return namespace definitions (may be <code>null</code> if none)
586: */
587: public ArrayList getNamespaces() {
588: return m_namespaces;
589: }
590:
591: /**
592: * Internal method to generate code to fill array with namespace indexes.
593: * The code generated to this point must have the array reference on the
594: * stack.
595: *
596: * @param nss namespaces to be handled
597: * @param mb method builder for generated code
598: */
599:
600: private void genFillNamespaceIndexes(ArrayList nss, MethodBuilder mb) {
601: if (nss != null) {
602: for (int i = 0; i < nss.size(); i++) {
603: mb.appendDUP();
604: mb.appendLoadConstant(i);
605: mb
606: .appendLoadConstant(((NamespaceDefinition) nss
607: .get(i)).getIndex());
608: mb.appendIASTORE();
609: }
610: }
611: }
612:
613: /**
614: * Internal method to generate code to fill array with namespace prefixes.
615: * The code generated to this point must have the array reference on the
616: * stack.
617: *
618: * @param nss namespaces to be handled
619: * @param mb method builder for generated code
620: */
621:
622: private void genFillNamespacePrefixes(ArrayList nss,
623: MethodBuilder mb) {
624: if (nss != null) {
625: for (int i = 0; i < nss.size(); i++) {
626: mb.appendDUP();
627: mb.appendLoadConstant(i);
628: String prefix = ((NamespaceDefinition) nss.get(i))
629: .getPrefix();
630: if (prefix == null) {
631: prefix = "";
632: }
633: mb.appendLoadConstant(prefix);
634: mb.appendAASTORE();
635: }
636: }
637: }
638:
639: /**
640: * Generate code for loading namespace index and URI arrays. The code
641: * creates the arrays and leaves the references on the stack.
642: *
643: * @param mb method builder for generated code
644: */
645:
646: public void genLoadNamespaces(MethodBuilder mb) {
647:
648: // first create the array of namespace indexes
649: int count = m_namespaces == null ? 0 : m_namespaces.size();
650: mb.appendLoadConstant(count);
651: mb.appendCreateArray("int");
652: genFillNamespaceIndexes(m_namespaces, mb);
653:
654: // next create the array of prefixes
655: mb.appendLoadConstant(count);
656: mb.appendCreateArray("java.lang.String");
657: genFillNamespacePrefixes(m_namespaces, mb);
658: }
659:
660: /**
661: * Generate code. Executes code generation for each top-level mapping
662: * defined in this binding, which in turn propagates the code generation
663: * all the way down.
664: *
665: * @param verbose flag for verbose output
666: * @param force create marshaller/unmarshaller even for abstract non-base
667: * mappings flag
668: * @throws JiBXException if error in transformation
669: */
670:
671: public void generateCode(boolean verbose, boolean force)
672: throws JiBXException {
673: if (m_mappings != null) {
674: for (int i = 0; i < m_mappings.size(); i++) {
675: IMapping mapping = (IMapping) m_mappings.get(i);
676: if (verbose) {
677: System.out.println("Generating code for mapping "
678: + mapping.getBoundType());
679: }
680: ((IMapping) m_mappings.get(i)).generateCode(force);
681: }
682: }
683: }
684:
685: /**
686: * Links extension mappings to their base mappings. This must be done before
687: * the more general linking step in order to determine which abstract
688: * mappings are standalone and which are extended by other mappings
689: *
690: * @throws JiBXException if error in linking
691: */
692: public void linkMappings() throws JiBXException {
693:
694: // check if any mappings are defined
695: if (m_mappings != null) {
696: for (int i = 0; i < m_mappings.size(); i++) {
697: Object obj = m_mappings.get(i);
698: if (obj instanceof MappingDefinition) {
699: ((MappingDefinition) obj).linkMappings();
700: }
701: }
702: }
703: }
704:
705: /**
706: * Set linkages between binding components. This is called after all the
707: * basic information has been set up. All linkage to higher level
708: * components should be done by this method, in order to prevent problems
709: * due to the order of definitions between components. For the definition
710: * context this calls the same method on all mappings defined in this
711: * context.
712: *
713: * @throws JiBXException if error in configuration
714: */
715:
716: public void setLinkages() throws JiBXException {
717:
718: // check if any mappings are defined
719: if (m_mappings != null) {
720: for (int i = 0; i < m_mappings.size(); i++) {
721: Object obj = m_mappings.get(i);
722: if (obj instanceof MappingDefinition) {
723: ((MappingDefinition) obj).setLinkages();
724: }
725: }
726: }
727: }
728:
729: // DEBUG
730: public void print(int depth) {
731: BindingDefinition.indent(depth);
732: System.out.print("context");
733: if (m_namespaces != null) {
734: System.out.print(" (ns#=" + m_namespaces.size() + ')');
735: }
736: if (m_mappings != null) {
737: System.out.print(" (mp#=" + m_mappings.size() + ')');
738: }
739: if (m_namedStructureMap != null) {
740: System.out.print(" (nm#=" + m_namedStructureMap.size()
741: + ')');
742: }
743: if (m_convertMap != null) {
744: System.out.print(" (cv#=" + m_convertMap.size() + ')');
745: }
746: if (m_formatMap != null) {
747: System.out.print(" (fm#=" + m_formatMap.size() + ')');
748: }
749: System.out.println();
750: if (m_namespaces != null) {
751: for (int i = 0; i < m_namespaces.size(); i++) {
752: NamespaceDefinition ndef = (NamespaceDefinition) m_namespaces
753: .get(i);
754: ndef.print(depth + 1);
755: }
756: }
757: if (m_mappings != null) {
758: for (int i = 0; i < m_mappings.size(); i++) {
759: Object obj = m_mappings.get(i);
760: if (obj instanceof MappingDefinition) {
761: MappingDefinition mdef = (MappingDefinition) obj;
762: mdef.print(depth + 1);
763: } else if (obj instanceof MappingDirect) {
764: MappingDirect mdir = (MappingDirect) obj;
765: mdir.print(depth + 1);
766: } else {
767: BindingDefinition.indent(depth + 1);
768: System.out.println("unexpected type "
769: + obj.getClass().getName());
770: }
771: }
772: }
773: }
774: }
|