001: /*
002: Copyright (c) 2005, 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.extras;
030:
031: import java.util.HashMap;
032:
033: import org.jibx.runtime.IAliasable;
034: import org.jibx.runtime.IMarshallable;
035: import org.jibx.runtime.IMarshaller;
036: import org.jibx.runtime.IMarshallingContext;
037: import org.jibx.runtime.IUnmarshaller;
038: import org.jibx.runtime.IUnmarshallingContext;
039: import org.jibx.runtime.JiBXException;
040: import org.jibx.runtime.impl.MarshallingContext;
041: import org.jibx.runtime.impl.UnmarshallingContext;
042:
043: /**
044: * <p>Abstract base custom marshaller/unmarshaller for an object that may have
045: * multiple references. The first time an object is seen when marshalling the
046: * full XML representation is generated; successive uses of the same object then
047: * use XML references to the object ID. To use this class you need to create a
048: * subclass with a constructor using the same signature as the one provided
049: * (calling the base class constructor from your subclass constructor) and
050: * implement the abstract {@link #getIdValue(java.lang.Object)} method in your subclass. You can
051: * also override the provided {@link #getAttributeName()} method to change the
052: * name used for the IDREF attribute, which must not match the name of an
053: * attribute used in the normal marshalled form of the object. The name used for
054: * this marshaller/unmarshaller in the mapping definition must match the name
055: * used for the base object type being handled.</p>
056: *
057: * @author Dennis M. Sosnoski
058: * @version 1.0
059: */
060:
061: public abstract class IdDefRefMapperBase implements IMarshaller,
062: IUnmarshaller, IAliasable {
063:
064: private String m_uri;
065: private int m_index;
066: private String m_name;
067:
068: /**
069: * Aliased constructor taking a name definition for reference elements. The
070: * subclass version will be used by JiBX to define the element name to be
071: * used with this custom marshaller/unmarshaller.
072: *
073: * @param uri namespace URI for the top-level element
074: * @param index namespace index corresponding to the defined URI within the
075: * marshalling context definitions
076: * @param name local name for the reference element
077: */
078: public IdDefRefMapperBase(String uri, int index, String name) {
079: m_uri = uri;
080: m_index = index;
081: m_name = name;
082: }
083:
084: /**
085: * Get the ID value from object being marshalled.
086: *
087: * @return ID value
088: */
089: protected abstract String getIdValue(Object item);
090:
091: /**
092: * Method which can be overridden to supply a different name for the ID
093: * reference attribute. The attribute name used by default is just "ref".
094: */
095: protected String getAttributeName() {
096: return "ref";
097: }
098:
099: /* (non-Javadoc)
100: * @see org.jibx.runtime.IMarshaller#isExtension(int)
101: */
102: public boolean isExtension(int index) {
103: return false;
104: }
105:
106: /* (non-Javadoc)
107: * @see org.jibx.runtime.IMarshaller#marshal(java.lang.Object,
108: * org.jibx.runtime.IMarshallingContext)
109: */
110: public void marshal(Object obj, IMarshallingContext ictx)
111: throws JiBXException {
112:
113: // make sure the parameters are as expected
114: if (obj == null) {
115: return;
116: } else if (!(ictx instanceof MarshallingContext)) {
117: throw new JiBXException(
118: "Invalid context type for marshaller");
119: } else {
120:
121: // check if ID already defined
122: MarshallingContext ctx = (MarshallingContext) ictx;
123: HashMap map = ctx.getIdMap();
124: String id = getIdValue(obj);
125: Object value = map.get(id);
126: if (value == null) {
127: if (obj instanceof IMarshallable) {
128:
129: // new id, write full representation and add to map
130: map.put(id, obj);
131: ((IMarshallable) obj).marshal(ctx);
132:
133: } else {
134: throw new JiBXException("Object of type "
135: + obj.getClass().getName()
136: + " is not marshallable");
137: }
138: } else if (value.equals(obj)) {
139:
140: // generate reference to previously-defined item
141: ctx.startTagAttributes(m_index, m_name);
142: ctx.attribute(0, getAttributeName(), id);
143: ctx.closeStartEmpty();
144:
145: } else {
146: throw new JiBXException("Duplicate definition for ID "
147: + id);
148: }
149: }
150: }
151:
152: /* (non-Javadoc)
153: * @see org.jibx.runtime.IUnmarshaller#isPresent(org.jibx.runtime.IUnmarshallingContext)
154: */
155: public boolean isPresent(IUnmarshallingContext ictx)
156: throws JiBXException {
157: return ictx.isAt(m_uri, m_name);
158: }
159:
160: /* (non-Javadoc)
161: * @see org.jibx.runtime.IUnmarshaller#unmarshal(java.lang.Object,
162: * org.jibx.runtime.IUnmarshallingContext)
163: */
164: public Object unmarshal(Object obj, IUnmarshallingContext ictx)
165: throws JiBXException {
166:
167: // make sure we're at the appropriate start tag
168: UnmarshallingContext ctx = (UnmarshallingContext) ictx;
169: if (!ctx.isAt(m_uri, m_name)) {
170: return null;
171: } else {
172:
173: // check for reference to existing ID
174: String id = ctx.attributeText(null, getAttributeName(),
175: null);
176: if (id == null) {
177:
178: // no ID value supplied, unmarshal full definition
179: obj = ctx.unmarshalElement();
180:
181: } else {
182:
183: // find object based on ID
184: obj = ctx.findID(id, 0);
185: ctx.parsePastEndTag(m_uri, m_name);
186: if (obj == null) {
187: ctx
188: .throwStartTagException("Reference to undefined ID "
189: + id);
190: }
191: }
192: }
193: return obj;
194: }
195: }
|