KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jibx > binding > def > DefinitionContext


1 /*
2 Copyright (c) 2003-2004, Dennis M. Sosnoski
3 All rights reserved.
4
5 Redistribution and use in source and binary forms, with or without modification,
6 are permitted provided that the following conditions are met:
7
8  * Redistributions of source code must retain the above copyright notice, this
9    list of conditions and the following disclaimer.
10  * Redistributions in binary form must reproduce the above copyright notice,
11    this list of conditions and the following disclaimer in the documentation
12    and/or other materials provided with the distribution.
13  * Neither the name of JiBX nor the names of its contributors may be used
14    to endorse or promote products derived from this software without specific
15    prior written permission.
16
17 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
18 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
21 ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
24 ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
26 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */

28
29 package org.jibx.binding.def;
30
31 import java.util.ArrayList JavaDoc;
32 import java.util.HashMap JavaDoc;
33
34 import org.jibx.binding.classes.ClassFile;
35 import org.jibx.binding.classes.MethodBuilder;
36 import org.jibx.binding.util.ArrayMap;
37
38 import org.jibx.runtime.JiBXException;
39
40 /**
41  * Nesting level for definitions in binding. This tracks namespace and mapping
42  * definitions that apply to all enclosed items.
43  *
44  * @author Dennis M. Sosnoski
45  * @version 1.0
46  */

47
48 public class DefinitionContext
49 {
50     /** Containing binding definition component. */
51     private final IContainer m_container;
52
53     /** Containing definition context. */
54     private final DefinitionContext m_context;
55
56     /** Namespace used by default at this level for attributes. */
57     private NamespaceDefinition m_attributeDefault;
58
59     /** Namespace used by default at this level for elements. */
60     private NamespaceDefinition m_elementDefault;
61
62     /** Namespaces defined at level (lazy create). */
63     private ArrayList JavaDoc m_namespaces;
64
65     /** Mapping from prefix to namespace definition (lazy create). */
66     private HashMap JavaDoc m_prefixMap;
67
68     /** Mapping from URI to namespace definition (lazy create). */
69     private HashMap JavaDoc m_uriMap;
70
71     /** Mapping from fully qualified class name to mapping index (lazy
72      create). */

73     private ArrayMap m_classMap;
74
75     /** Class mappings defined at level (lazy create). */
76     private ArrayList JavaDoc m_mappings;
77     
78     /** Map from signatures to <code>String</code> conversions. */
79     private HashMap JavaDoc m_convertMap;
80     
81     /** Map from format names to <code>String</code> conversions. */
82     private HashMap JavaDoc m_formatMap;
83     
84     /** Named binding components (only for root context of a binding). */
85     private HashMap JavaDoc m_namedStructureMap;
86
87     /**
88      * Constructor. Uses the containing context to establish the hierarchy for
89      * resolving namespaces and class mappings.
90      *
91      * @param contain containing binding definition component
92      */

93
94     public DefinitionContext(IContainer contain) {
95         m_container = contain;
96         m_context = contain.getDefinitionContext();
97         // TODO: make these lazy
98
m_convertMap = new HashMap JavaDoc();
99         m_formatMap = new HashMap JavaDoc();
100         if (m_context == null) {
101             m_namedStructureMap = new HashMap JavaDoc();
102         }
103     }
104
105     /**
106      * Add namespace to set defined at this level. If the new namespace
107      * conflicts with an existing namespace at this level (in terms of default
108      * usage or prefix) this throws an exception.
109      *
110      * @param def namespace definition to be added
111      * @throws JiBXException on namespace definition conflict
112      */

113
114     public void addNamespace(NamespaceDefinition def) throws JiBXException {
115
116         // create structures if not already done
117
if (m_namespaces == null) {
118             m_namespaces = new ArrayList JavaDoc();
119             m_prefixMap = new HashMap JavaDoc();
120             m_uriMap = new HashMap JavaDoc();
121         }
122
123         // check for conflict as default for attributes
124
if (def.isAttributeDefault()) {
125             if (m_attributeDefault == null) {
126                 m_attributeDefault = def;
127             } else {
128                 throw new JiBXException
129                     ("Multiple default attribute namespaces at level");
130             }
131         }
132
133         // check for conflict as default for elements
134
if (def.isElementDefault()) {
135             if (m_elementDefault == null) {
136                 m_elementDefault = def;
137             } else {
138                 throw new JiBXException
139                     ("Multiple default element namespaces at level");
140             }
141         }
142
143         // check for conflict on prefix
144
String JavaDoc prefix = def.getPrefix();
145         if (m_prefixMap.get(prefix) != null) {
146             throw new JiBXException("Namespace prefix conflict");
147         }
148         
149         // check for duplicate definition of same URI
150
String JavaDoc uri = def.getUri();
151         Object JavaDoc prior = m_uriMap.get(uri);
152         if (prior != null && ((NamespaceDefinition)prior).getPrefix() != null) {
153             return;
154         }
155
156         // add only if successful in all tests
157
def.setIndex(m_container.getBindingRoot().
158             getNamespaceUriIndex(def.getUri()));
159         m_namespaces.add(def);
160         m_prefixMap.put(prefix, def);
161         m_uriMap.put(uri, def);
162     }
163
164     /**
165      * Add class mapping to set defined at this level. If the new mapping
166      * conflicts with an existing one at this level it throws an exception.
167      *
168      * @param def mapping definition to be added
169      * @throws JiBXException on mapping definition conflict
170      */

171
172     public void addMapping(IMapping def) throws JiBXException {
173
174         // create structure if not already done
175
if (m_mappings == null) {
176             m_classMap = new ArrayMap();
177             m_mappings = new ArrayList JavaDoc();
178         }
179
180         // check for conflict on class name before adding to definitions
181
String JavaDoc cname = def.getBoundType();
182         int index = m_classMap.findOrAdd(cname);
183         if (index < m_mappings.size()) {
184             throw new JiBXException
185                 ("Conflicting mappings for class " + cname);
186         } else {
187             m_mappings.add(def);
188         }
189     }
190
191     /**
192      * Add named structure component to set defined at this level. If the name
193      * conflicts with an existing one at this level it throws an exception.
194      *
195      * @param name component name to be set
196      * @param comp named component
197      * @throws JiBXException on mapping definition conflict
198      */

199
200     public void addNamedStructure(String JavaDoc name, IComponent comp)
201         throws JiBXException {
202         if (m_namedStructureMap == null) {
203             m_context.addNamedStructure(name, comp);
204         } else {
205             m_namedStructureMap.put(name, comp);
206         }
207     }
208
209     /**
210      * Get the default namespace for a contained name. Elements and attributes
211      * are treated separately, since namespace handling differs between the two.
212      *
213      * @param attr flag for attribute name
214      * @return default namespace URI, or <code>null</code> if none
215      */

216
217     private NamespaceDefinition getDefaultNamespace(boolean attr) {
218         NamespaceDefinition ns;
219         if (attr) {
220             ns = m_attributeDefault;
221         } else {
222             ns = m_elementDefault;
223         }
224         if (ns == null && m_context != null) {
225             ns = m_context.getDefaultNamespace(attr);
226         }
227         return ns;
228     }
229
230     /**
231      * Get the default namespace URI for a contained name. Elements and
232      * attributes are treated separately, since namespace handling differs
233      * between the two.
234      *
235      * @param attr flag for attribute name
236      * @return default namespace URI, or <code>null</code> if none
237      */

238
239     public String JavaDoc getDefaultURI(boolean attr) {
240         NamespaceDefinition ns = getDefaultNamespace(attr);
241         if (ns == null) {
242             return null;
243         } else {
244             return ns.getUri();
245         }
246     }
247
248     /**
249      * Get the default namespace index for a contained name. Elements and
250      * attributes are treated separately, since namespace handling differs
251      * between the two.
252      *
253      * @param attr flag for attribute name
254      * @return default namespace index
255      */

256
257     public int getDefaultIndex(boolean attr) {
258         NamespaceDefinition ns = getDefaultNamespace(attr);
259         if (ns == null) {
260             return 0;
261         } else {
262             return ns.getIndex();
263         }
264     }
265
266     /**
267      * Get namespace index for a given URI. Finds the prefix for a URI in a
268      * name contained by this level, throwing an exception if the URI is not
269      * found or does not have a prefix.
270      *
271      * @param uri namespace URI to be found
272      * @param attr flag for attribute name
273      * @return namespace index for URI
274      * @throws JiBXException if URI not defined or not usable
275      */

276
277     public int getNamespaceIndex(String JavaDoc uri, boolean attr)
278         throws JiBXException {
279
280         // check for namespace URI defined at this level
281
if (m_uriMap != null) {
282             Object JavaDoc value = m_uriMap.get(uri);
283             if (value != null) {
284                 if (!attr || ((NamespaceDefinition)value).getPrefix() != null) {
285                     return ((NamespaceDefinition)value).getIndex();
286                 }
287             }
288         }
289
290         // if all else fails, try the higher level
291
if (m_context == null) {
292             throw new JiBXException("Namespace URI \"" + uri +
293                 "\" not defined or not usable");
294         } else {
295             return m_context.getNamespaceIndex(uri, attr);
296         }
297     }
298
299     /**
300      * Get mapping definition for class if defined at this level.
301      *
302      * @param name fully qualified class name
303      * @return mapping definition for class, or <code>null</code> if not defined
304      */

305
306     public IMapping getMappingAtLevel(String JavaDoc name) {
307
308         // check for class mapping defined at this level
309
if (m_classMap != null) {
310             
311             // check for definition at this level
312
int index = m_classMap.find(name);
313             if (index >= 0) {
314                 return (IMapping)m_mappings.get(index);
315             }
316         }
317         return null;
318     }
319
320     /**
321      * Get mapping definition for class. Finds the mapping for a fully
322      * qualified class name, throwing an exception if no mapping is defined.
323      * This can only be used during the linkage phase.
324      *
325      * TODO: really not necessary to have both this and getMappingAtLevel now
326      *
327      * @param name fully qualified class name
328      * @return mapping definition for class, or <code>null</code> if not defined
329      */

330
331     public IMapping getClassMapping(String JavaDoc name) {
332
333         // check for class mapping defined at this level
334
IMapping def = getMappingAtLevel(name);
335         if (def == null && m_context != null) {
336
337             // try finding definition at higher level
338
def = m_context.getClassMapping(name);
339             
340         }
341         return def;
342     }
343
344     /**
345      * Get nested structure by name. Finds the nested structure with the given
346      * name, throwing an exception if no component with that name is defined.
347      *
348      * @param name component name to be found
349      * @return component with given name
350      * @throws JiBXException if name not defined
351      */

352
353     public IComponent getNamedStructure(String JavaDoc name) throws JiBXException {
354
355         // check for named component defined at this level
356
IComponent comp = null;
357         if (m_namedStructureMap != null) {
358             comp = (IComponent)m_namedStructureMap.get(name);
359         }
360         if (comp == null) {
361             if (m_context == null) {
362                 throw new JiBXException("Referenced label \"" + name +
363                     "\" not defined");
364             } else {
365                 comp = m_context.getNamedStructure(name);
366             }
367         }
368         return comp;
369     }
370
371     /**
372      * Get mapping definitions at level.
373      *
374      * @return mapping definitions, <code>null</code> if none defined at level
375      */

376
377     public ArrayList JavaDoc getMappings() {
378         return m_mappings;
379     }
380
381     /**
382      * Get specific conversion definition for type. Finds with an exact match
383      * on the class name, checking the containing definitions if a conversion
384      * is not found at this level.
385      *
386      * @param name fully qualified class name to be converted
387      * @return conversion definition for class, or <code>null</code> if not
388      * found
389      */

390
391     public StringConversion getSpecificConversion(String JavaDoc name) {
392         StringConversion conv = (StringConversion)m_convertMap.get(name);
393         if (conv == null && m_context != null) {
394             conv = m_context.getSpecificConversion(name);
395         }
396         return conv;
397     }
398
399     /**
400      * Get conversion definition for class. Finds the conversion based on a
401      * fully qualified class name. If a specific conversion for the actual
402      * class is not found (either in this or a containing level) this returns
403      * the generic object conversion.
404      *
405      * @param clas information for target conversion class
406      * @return conversion definition for class
407      */

408
409     public StringConversion getConversion(ClassFile clas) {
410         
411         // use conversions for superclasses only
412
StringConversion conv = getSpecificConversion(clas.getName());
413         if (conv == null) {
414             return BindingDefinition.s_objectConversion;
415         } else {
416             return conv;
417         }
418     }
419
420     /**
421      * Get named conversion definition. Finds the conversion with the supplied
422      * name, checking the containing definitions if the conversion is not found
423      * at this level.
424      *
425      * @param name conversion name to be found
426      * @return conversion definition for class
427      */

428
429     public StringConversion getNamedConversion(String JavaDoc name) {
430         StringConversion conv = (StringConversion)m_formatMap.get(name);
431         if (conv == null && m_context != null) {
432             conv = m_context.getNamedConversion(name);
433         }
434         return conv;
435     }
436
437     /**
438      * Add named conversion. Checks for duplicate conversions defined within
439      * a level with the same name.
440      *
441      * @param name format name for this conversion
442      * @param conv conversion definition for class
443      * @throws JiBXException if duplicate conversion definition
444      */

445
446     public void addConversion(String JavaDoc name, StringConversion conv)
447         throws JiBXException {
448         if (m_formatMap.put(name, conv) != null) {
449             throw new JiBXException("Duplicate conversion defined with name " +
450                 name);
451         }
452     }
453
454     /**
455      * Set specific conversion definition for type. Sets the conversion based
456      * on a type signature, checking for duplicate conversions defined within
457      * a level.
458      *
459      * @param conv conversion definition for class
460      * @throws JiBXException if duplicate conversion definition
461      */

462
463     public void setConversion(StringConversion conv)
464         throws JiBXException {
465         if (m_convertMap.put(conv.getTypeName(), conv) != null) {
466             throw new JiBXException("Duplicate conversion defined for type " +
467                 conv.getTypeName());
468         }
469     }
470
471     /**
472      * Sets a named conversion definition.
473      *
474      * @param name format name for this conversion
475      * @param conv conversion definition for class
476      * @throws JiBXException if duplicate conversion definition
477      */

478
479     public void setNamedConversion(String JavaDoc name, StringConversion conv)
480         throws JiBXException {
481         addConversion(name, conv);
482     }
483
484     /**
485      * Sets a conversion definition by both type and name. Both the type and
486      * name are checked for duplicate conversions defined within a level.
487      *
488      * @param name format name for this conversion
489      * @param conv conversion definition for class
490      * @throws JiBXException if duplicate conversion definition
491      */

492
493     public void setDefaultConversion(String JavaDoc name, StringConversion conv)
494         throws JiBXException {
495         addConversion(name, conv);
496         setConversion(conv);
497     }
498     
499     /**
500      * Check if one or more namespaces are defined in this context.
501      *
502      * @return <code>true</code> if namespaces are defined, <code>false</code>
503      * if not
504      */

505
506     public boolean hasNamespace() {
507         return m_namespaces != null && m_namespaces.size() > 0;
508     }
509
510     /**
511      * Internal method to generate code to fill array with namespace indexes.
512      * The code generated to this point must have the array reference on the
513      * stack.
514      *
515      * @param nss namespaces to be handled
516      * @param mb method builder for generated code
517      * @return next index beyond values stored
518      * @throws JiBXException if error in generating code
519      */

520
521     private void genFillNamespaceIndexes(ArrayList JavaDoc nss, MethodBuilder mb)
522         throws JiBXException {
523         if (nss != null) {
524             for (int i = 0; i < nss.size(); i++) {
525                 mb.appendDUP();
526                 mb.appendLoadConstant(i);
527                 mb.appendLoadConstant
528                     (((NamespaceDefinition)nss.get(i)).getIndex());
529                 mb.appendIASTORE();
530             }
531         }
532     }
533
534     /**
535      * Internal method to generate code to fill array with namespace prefixes.
536      * The code generated to this point must have the array reference on the
537      * stack.
538      *
539      * @param nss namespaces to be handled
540      * @param mb method builder for generated code
541      * @return next index beyond values stored
542      * @throws JiBXException if error in generating code
543      */

544
545     private void genFillNamespacePrefixes(ArrayList JavaDoc nss, MethodBuilder mb)
546         throws JiBXException {
547         if (nss != null) {
548             for (int i = 0; i < nss.size(); i++) {
549                 mb.appendDUP();
550                 mb.appendLoadConstant(i);
551                 String JavaDoc prefix = ((NamespaceDefinition)nss.get(i)).getPrefix();
552                 if (prefix == null) {
553                     prefix = "";
554                 }
555                 mb.appendLoadConstant(prefix);
556                 mb.appendAASTORE();
557             }
558         }
559     }
560
561     /**
562      * Generate code for loading namespace index and URI arrays. The code
563      * creates the arrays and leaves the references on the stack.
564      *
565      * @param mb method builder for generated code
566      * @throws JiBXException if error in generating code
567      */

568
569     public void genLoadNamespaces(MethodBuilder mb) throws JiBXException {
570         
571         // first create the array of namespace indexes
572
int count = m_namespaces == null ? 0 : m_namespaces.size();
573         mb.appendLoadConstant(count);
574         mb.appendCreateArray("int");
575         genFillNamespaceIndexes(m_namespaces, mb);
576         
577         // next create the array of prefixes
578
mb.appendLoadConstant(count);
579         mb.appendCreateArray("java.lang.String");
580         genFillNamespacePrefixes(m_namespaces, mb);
581     }
582
583     /**
584      * Generate code. Executes code generation for each top-level mapping
585      * defined in this binding, which in turn propagates the code generation
586      * all the way down.
587      *
588      * @param verbose flag for verbose output
589      * @throws JiBXException if error in transformation
590      */

591
592     public void generateCode(boolean verbose) throws JiBXException {
593         if (m_mappings != null) {
594             for (int i = 0; i < m_mappings.size(); i++) {
595                 IMapping mapping = (IMapping)m_mappings.get(i);
596                 if (verbose) {
597                     System.out.println("Generating code for mapping " +
598                         mapping.getBoundType());
599                 }
600                 ((IMapping)m_mappings.get(i)).generateCode();
601             }
602         }
603     }
604     
605     /**
606      * Links extension mappings to their base mappings. This must be done before
607      * the more general linking step in order to determine which abstract
608      * mappings are standalone and which are extended by other mappings
609      *
610      * @throws JiBXException if error in linking
611      */

612     public void linkMappings() throws JiBXException {
613         
614         // check if any mappings are defined
615
if (m_mappings != null) {
616             for (int i = 0; i < m_mappings.size(); i++) {
617                 Object JavaDoc obj = m_mappings.get(i);
618                 if (obj instanceof MappingDefinition) {
619                     ((MappingDefinition)obj).linkMappings();
620                 }
621             }
622         }
623     }
624     
625     /**
626      * Set linkages between binding components. This is called after all the
627      * basic information has been set up. All linkage to higher level
628      * components should be done by this method, in order to prevent problems
629      * due to the order of definitions between components. For the definition
630      * context this calls the same method on all mappings defined in this
631      * context.
632      *
633      * @throws JiBXException if error in configuration
634      */

635
636     public void setLinkages() throws JiBXException {
637         
638         // check if any mappings are defined
639
if (m_mappings != null) {
640             for (int i = 0; i < m_mappings.size(); i++) {
641                 Object JavaDoc obj = m_mappings.get(i);
642                 if (obj instanceof MappingDefinition) {
643                     ((MappingDefinition)obj).setLinkages();
644                 }
645             }
646         }
647     }
648     
649     // DEBUG
650
public void print(int depth) {
651         BindingDefinition.indent(depth);
652         System.out.print("context");
653         if (m_namespaces != null) {
654             System.out.print(" (ns#=" + m_namespaces.size() + ')');
655         }
656         if (m_mappings != null) {
657             System.out.print(" (mp#=" + m_mappings.size() + ')');
658         }
659         if (m_namedStructureMap != null) {
660             System.out.print(" (nm#=" + m_namedStructureMap.size() + ')');
661         }
662         if (m_convertMap != null) {
663             System.out.print(" (cv#=" + m_convertMap.size() + ')');
664         }
665         if (m_formatMap != null) {
666             System.out.print(" (fm#=" + m_formatMap.size() + ')');
667         }
668         System.out.println();
669         if (m_namespaces != null) {
670             for (int i = 0; i < m_namespaces.size(); i++) {
671                 NamespaceDefinition ndef =
672                     (NamespaceDefinition)m_namespaces.get(i);
673                 ndef.print(depth+1);
674             }
675         }
676         if (m_mappings != null) {
677             for (int i = 0; i < m_mappings.size(); i++) {
678                 Object JavaDoc obj = m_mappings.get(i);
679                 if (obj instanceof MappingDefinition) {
680                     MappingDefinition mdef = (MappingDefinition)obj;
681                     mdef.print(depth+1);
682                 } else if (obj instanceof MappingDirect) {
683                     MappingDirect mdir = (MappingDirect)obj;
684                     mdir.print(depth+1);
685                 } else {
686                     BindingDefinition.indent(depth+1);
687                     System.out.println("unexpected type " +
688                         obj.getClass().getName());
689                 }
690             }
691         }
692     }
693 }
694
Popular Tags