001: /*
002: Copyright (c) 2004-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.ArrayList;
032:
033: import org.jibx.binding.util.StringArray;
034: import org.jibx.runtime.IMarshallingContext;
035: import org.jibx.runtime.IUnmarshallingContext;
036: import org.jibx.runtime.JiBXException;
037: import org.jibx.runtime.QName;
038:
039: /**
040: * Model component for <b>mapping</b> element of binding definition.
041: *
042: * @author Dennis M. Sosnoski
043: */
044: public class MappingElement extends TemplateElementBase {
045: /** Enumeration of allowed attribute names */
046: public static final StringArray s_allowedAttributes = new StringArray(
047: new String[] { "abstract", "class", "extends", "type-name" },
048: new StringArray(NameAttributes.s_allowedAttributes,
049: ContainerElementBase.s_allowedAttributes));
050: // TODO: move "mapping" to TemplateElementBase in 2.0
051:
052: /** Abstract mapping flag. */
053: private boolean m_isAbstract;
054:
055: /** Name attributes information for nesting. */
056: private NameAttributes m_nameAttrs;
057:
058: /** Name of mapped class extended by this mapping. */
059: private String m_extendsName;
060:
061: /** Type qualified name (defaults to fully-qualified class name in
062: no-namespace namespace). */
063: private QName m_typeQName;
064:
065: /** Mapping extended by this mapping. */
066: private MappingElement m_extendsMapping;
067:
068: /**
069: * Default constructor.
070: */
071: public MappingElement() {
072: super (MAPPING_ELEMENT);
073: m_nameAttrs = new NameAttributes();
074: m_topChildren = new ArrayList();
075: }
076:
077: /**
078: * Check for abstract mapping.
079: *
080: * @return <code>true</code> if abstract, <code>false</code> if not
081: */
082: public boolean isAbstract() {
083: return m_isAbstract;
084: }
085:
086: /**
087: * Set abstract mapping.
088: *
089: * @param abs <code>true</code> if abstract, <code>false</code> if not
090: */
091: public void setAbstract(boolean abs) {
092: m_isAbstract = abs;
093: }
094:
095: /**
096: * Get type name.
097: *
098: * @return type name
099: */
100: public String getTypeName() {
101: return (m_typeQName == null) ? null : m_typeQName.toString();
102: }
103:
104: /**
105: * Set type name.
106: *
107: * @param name type name
108: */
109: public void setTypeName(String name) {
110: m_typeQName = new QName(name);
111: }
112:
113: /**
114: * Get type qualified name.
115: *
116: * @return type qualified name
117: */
118: public QName getTypeQName() {
119: return m_typeQName;
120: }
121:
122: /**
123: * Set type qualified name.
124: *
125: * @param qname type qualified name
126: */
127: public void setTypeQName(QName qname) {
128: m_typeQName = qname;
129: }
130:
131: /**
132: * Set name of mapped class extended by this one.
133: *
134: * @param name
135: */
136: public void setExtendsName(String name) {
137: m_extendsName = name;
138: }
139:
140: /**
141: * Get name of mapped class extended by this one.
142: *
143: * @return name
144: */
145: public String getExtendsName() {
146: return m_extendsName;
147: }
148:
149: /**
150: * Get mapping extended by this one.
151: *
152: * @return mapping extended by this one
153: */
154: public MappingElement getExtendsMapping() {
155: return m_extendsMapping;
156: }
157:
158: /* (non-Javadoc)
159: * @see org.jibx.binding.model.TemplateElementBase#isDefaultTemplate()
160: */
161: public boolean isDefaultTemplate() {
162: return m_typeQName == null;
163: }
164:
165: //
166: // Access methods
167:
168: /**
169: * Get name attributes. This is provided for use with the name attributes as
170: * a hash key.
171: *
172: * @return name attributes structure
173: */
174: public NameAttributes getNameAttributes() {
175: return m_nameAttrs;
176: }
177:
178: //
179: // Name attribute delegate methods
180:
181: /**
182: * Get name.
183: *
184: * @return name text
185: */
186: public String getName() {
187: return m_nameAttrs.getName();
188: }
189:
190: /**
191: * Set name.
192: *
193: * @param name text for name
194: */
195: public void setName(String name) {
196: m_nameAttrs.setName(name);
197: }
198:
199: /**
200: * Get specified namespace URI.
201: *
202: * @return namespace URI (<code>null</code> if not set)
203: */
204: public String getUri() {
205: return m_nameAttrs.getUri();
206: }
207:
208: /**
209: * Set namespace URI.
210: *
211: * @param uri namespace URI (<code>null</code> if not set)
212: */
213: public void setUri(String uri) {
214: m_nameAttrs.setUri(uri);
215: }
216:
217: /**
218: * Get specified namespace prefix.
219: *
220: * @return namespace prefix (<code>null</code> if not set)
221: */
222: public String getPrefix() {
223: return m_nameAttrs.getPrefix();
224: }
225:
226: /**
227: * Set namespace prefix.
228: *
229: * @param prefix namespace prefix (<code>null</code> if not set)
230: */
231: public void setPrefix(String prefix) {
232: m_nameAttrs.setPrefix(prefix);
233: }
234:
235: /**
236: * Get effective namespace information. This call is only meaningful after
237: * validation.
238: *
239: * @return effective namespace information
240: */
241: public NamespaceElement getNamespace() {
242: return m_nameAttrs.getNamespace();
243: }
244:
245: //
246: // Validation methods
247:
248: /**
249: * JiBX access method to set mapping type name as qualified name.
250: *
251: * @param text mapping name text (<code>null</code> if none)
252: * @param ictx unmarshalling context
253: * @throws JiBXException on deserialization error
254: */
255: private void setQualifiedTypeName(String text,
256: IUnmarshallingContext ictx) throws JiBXException {
257: m_typeQName = QName.deserialize(text, ictx);
258: }
259:
260: /**
261: * JiBX access method to get mapping type name as qualified name.
262: *
263: * @param ictx marshalling context
264: * @return mapping type name text (<code>null</code> if none)
265: * @throws JiBXException on deserialization error
266: */
267: private String getQualifiedTypeName(IMarshallingContext ictx)
268: throws JiBXException {
269: return QName.serialize(m_typeQName, ictx);
270: }
271:
272: /**
273: * Make sure all attributes are defined.
274: *
275: * @param uctx unmarshalling context
276: * @exception JiBXException on unmarshalling error
277: */
278: private void preSet(IUnmarshallingContext uctx)
279: throws JiBXException {
280: validateAttributes(uctx, s_allowedAttributes);
281: }
282:
283: /* (non-Javadoc)
284: * @see org.jibx.binding.model.ElementBase#prevalidate(org.jibx.binding.model.ValidationContext)
285: */
286: public void prevalidate(ValidationContext vctx) {
287: m_nameAttrs.prevalidate(vctx);
288: if (m_isAbstract) {
289: if (m_typeQName != null && m_nameAttrs.getName() != null) {
290: vctx
291: .addError("Type name cannot be used with an element name");
292: }
293: if (isNillable()) {
294: vctx
295: .addError("nillable='true' cannot be used on an abstract mapping");
296: }
297: } else {
298: if (m_nameAttrs.getName() == null) {
299: if ((vctx.isInBinding() && getUnmarshallerName() == null)
300: || (vctx.isOutBinding() && getMarshallerName() == null)) {
301: vctx
302: .addError("Non-abstract mapping must define an element name");
303: }
304: if (isNillable()) {
305: vctx
306: .addError("nillable='true' cannot be used without an element name");
307: }
308: }
309: if (m_typeQName != null) {
310: vctx
311: .addError("Type name can only be used with an abstract mapping");
312: }
313: }
314: super .prevalidate(vctx);
315: }
316:
317: /* (non-Javadoc)
318: * @see org.jibx.binding.model.ElementBase#validate(org.jibx.binding.model.ValidationContext)
319: */
320: public void validate(ValidationContext vctx) {
321: m_nameAttrs.validate(vctx);
322:
323: // check use of text values in children of structure with name
324: SequenceVisitor visitor = new SequenceVisitor(null, vctx);
325: TreeContext tctx = vctx.getChildContext();
326: tctx.tourTree(this , visitor);
327:
328: // make sure we can construct instances of concrete mapped class
329: if (!m_isAbstract) {
330: verifyConstruction(vctx, getHandledClass());
331: }
332:
333: // check for class that probably should extend another mapped class
334: if (m_extendsName == null && !m_isAbstract) {
335:
336: // first check ancestor classes for ones with specific mappings
337: IClass sclass = getHandledClass();
338: DefinitionContext defc = vctx.getCurrentDefinitions();
339: while ((sclass = sclass.getSuperClass()) != null) {
340: TemplateElementBase stmpl = defc
341: .getSpecificTemplate(sclass.getName());
342: if (stmpl != null) {
343: if (stmpl instanceof MappingElement) {
344: MappingElement smap = (MappingElement) stmpl;
345: if (smap.getExtensionTypes().size() > 0) {
346: vctx
347: .addWarning("Class "
348: + getClassName()
349: + " extends "
350: + smap.getClassName()
351: + " which has a base mapping with extensions,"
352: + " but this mapping does not extend it");
353: }
354: }
355: break;
356: }
357:
358: }
359:
360: // next check interfaces for ones with specific mappings
361: String[] interfaces = getHandledClass().getInterfaces();
362: for (int i = 0; i < interfaces.length; i++) {
363: String iname = interfaces[i];
364: TemplateElementBase itmpl = defc
365: .getSpecificTemplate(iname);
366: if (itmpl != null) {
367: if (itmpl instanceof MappingElement) {
368: MappingElement smap = (MappingElement) itmpl;
369: if (smap.getExtensionTypes().size() > 0) {
370: vctx
371: .addWarning("Class "
372: + getClassName()
373: + " implements "
374: + iname
375: + " which has a base mapping with extensions,"
376: + " but this mapping does not extend it");
377: }
378: }
379: break;
380: }
381:
382: }
383:
384: } else if (m_extendsName != null && m_isAbstract
385: && getExtensionTypes().size() == 0) {
386: vctx
387: .addWarning("Only concrete mappings should be 'leaf' mappings "
388: + "for extensions; you should either remove 'extends' attribute "
389: + "or add concrete extension mappings for this abstract mapping");
390: }
391:
392: // run base class validation
393: super .validate(vctx);
394: }
395:
396: /**
397: * Special validation method to link extension mappings to base mappings.
398: * This is called as a special step following registration, so that the
399: * normal validation pass can make use of the linkage information.
400: *
401: * @param vctx validation context
402: */
403: public void validateExtension(ValidationContext vctx) {
404: if (m_extendsName != null) {
405:
406: // find the base class mapping
407: TemplateElementBase base = vctx.getDefinitions()
408: .getSpecificTemplate(m_extendsName);
409: if ((base instanceof MappingElement)) {
410:
411: // check base class using custom marshaller/unmarshaller
412: MappingElement mbase = (MappingElement) base;
413: if (mbase.getMarshaller() != null
414: || mbase.getUnmarshaller() != null) {
415: vctx
416: .addError("Cannot extend a mapping using custom "
417: + "marshaller/unmarshaller");
418: }
419:
420: // add reference to base class
421: mbase.addExtensionType(this );
422: m_extendsMapping = mbase;
423:
424: } else {
425: vctx.addFatal("No mapping found for class "
426: + m_extendsName);
427: }
428: }
429: }
430: }
|