001: /*
002: Copyright (c) 2005-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.model;
030:
031: import java.util.HashMap;
032: import java.util.HashSet;
033:
034: /**
035: * Context for components using a hierarchy of definitions based on class type.
036: * This is used to track conversion definitions in the form of <b>format</b> and
037: * <b>template</b> elements. The access methods take the different levels of
038: * nesting into account, automatically delegating to the containing context (if
039: * defined) when a lookup fails.
040: *
041: * @author Dennis M. Sosnoski
042: */
043: public class ClassHierarchyContext {
044: /** Link to containing context. */
045: private final ClassHierarchyContext m_outerContext;
046:
047: /** Map from type name to binding component. */
048: private HashMap m_typeToComponentMap;
049:
050: /** Set of compatible type names. */
051: private HashSet m_compatibleTypeSet;
052:
053: /** Map from format names to <code>String</code> conversions (lazy create). */
054: private HashMap m_nameToComponentMap;
055:
056: /**
057: * Constructor.
058: *
059: * @param outer containing context (<code>null</code> if at root of tree)
060: */
061: protected ClassHierarchyContext(ClassHierarchyContext outer) {
062: m_outerContext = outer;
063: m_typeToComponentMap = new HashMap();
064: m_compatibleTypeSet = new HashSet();
065: m_nameToComponentMap = new HashMap();
066: }
067:
068: /**
069: * Get containing context.
070: *
071: * @return containing context information (<code>null</code> if at root of
072: * tree)
073: */
074: public ClassHierarchyContext getContaining() {
075: return m_outerContext;
076: }
077:
078: /**
079: * Add typed component to set defined at this level. This associated the
080: * component with the type for class hierarchy-based lookups.
081: *
082: * @param clas class information to be associated with component
083: * @param comp definition component to be added
084: * @param vctx validation context in use
085: */
086: public void addTypedComponent(IClass clas, ElementBase comp,
087: ValidationContext vctx) {
088: String type = clas.getName();
089: if (m_typeToComponentMap.put(type, comp) == null) {
090:
091: // new type, add all interfaces and supertypes to compatible set
092: IClass sclas = clas;
093: do {
094: String[] interfaces = sclas.getInterfaces();
095: for (int i = 0; i < interfaces.length; i++) {
096: m_compatibleTypeSet.add(interfaces[i]);
097: }
098: m_compatibleTypeSet.add(sclas.getName());
099: } while ((sclas = sclas.getSuperClass()) != null);
100:
101: } else {
102: vctx.addError("Duplicate conversion defined for type "
103: + type, comp);
104: }
105: }
106:
107: /**
108: * Add named component to set defined at this level. This associated the
109: * component with the type for class hierarchy-based lookups.
110: * TODO: Make this use qname instead of text
111: *
112: * @param label name to be associated with component
113: * @param comp definition component to be added
114: * @param vctx validation context in use
115: */
116: public void addNamedComponent(String label, ElementBase comp,
117: ValidationContext vctx) {
118: if (m_nameToComponentMap.put(label, comp) != null) {
119: if (label.startsWith("{}")) {
120: label = label.substring(2);
121: }
122: vctx.addError("Duplicate name " + label, comp);
123: }
124: }
125:
126: /**
127: * Get specific binding component for type. Finds with an exact match
128: * on the type name, checking the containing definitions if a matching
129: * component is not found at this level.
130: *
131: * @param name fully qualified class name to be converted
132: * @return binding component for class, or <code>null</code> if not
133: * found
134: */
135: public ElementBase getSpecificComponent(String name) {
136: ElementBase comp = null;
137: if (m_typeToComponentMap != null) {
138: comp = (ElementBase) m_typeToComponentMap.get(name);
139: }
140: if (comp == null && m_outerContext != null) {
141: comp = m_outerContext.getSpecificComponent(name);
142: }
143: return comp;
144: }
145:
146: /**
147: * Get named binding component definition. Finds the component with the
148: * supplied name, checking the containing definitions if the component is
149: * not found at this level.
150: *
151: * @param name component name to be found
152: * @return binding component with name, or <code>null</code> if not
153: * found
154: */
155: public ElementBase getNamedComponent(String name) {
156: ElementBase comp = null;
157: if (m_nameToComponentMap != null) {
158: comp = (ElementBase) m_nameToComponentMap.get(name);
159: }
160: if (comp == null && m_outerContext != null) {
161: comp = m_outerContext.getNamedComponent(name);
162: }
163: return comp;
164: }
165:
166: /**
167: * Get best binding component for class. Finds the component based on a
168: * fully qualified class name. If a specific component for the actual
169: * class is not found (either in this or a containing level) this returns
170: * the most specific superclass component.
171: *
172: * @param clas information for target class
173: * @return binding component definition for class, or <code>null</code> if
174: * none found
175: */
176: public ElementBase getMostSpecificComponent(IClass clas) {
177: ElementBase comp = getSpecificComponent(clas.getName());
178: while (comp == null) {
179: IClass sclas = clas.getSuperClass();
180: if (sclas == null) {
181: break;
182: }
183: clas = sclas;
184: comp = getSpecificComponent(clas.getName());
185: }
186: return comp;
187: }
188:
189: /**
190: * Checks if a class is compatible with one or more components. If a
191: * specific component for the actual class is not found (either in this or a
192: * containing level) this checks for components that handle subclasses or
193: * implementations of the class.
194: *
195: * @param clas information for target class
196: * @return <code>true</code> if compatible type, <code>false</code> if not
197: */
198: public boolean isCompatibleType(IClass clas) {
199: if (m_compatibleTypeSet.contains(clas.getName())) {
200: return true;
201: } else if (m_outerContext != null) {
202: return m_outerContext.isCompatibleType(clas);
203: } else {
204: return false;
205: }
206: }
207: }
|