001: /*
002: * Copyright 2006 Sun Microsystems, Inc. All Rights Reserved.
003: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004: *
005: * This code is free software; you can redistribute it and/or modify it
006: * under the terms of the GNU General Public License version 2 only, as
007: * published by the Free Software Foundation. Sun designates this
008: * particular file as subject to the "Classpath" exception as provided
009: * by Sun in the LICENSE file that accompanied this code.
010: *
011: * This code is distributed in the hope that it will be useful, but WITHOUT
012: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014: * version 2 for more details (a copy is included in the LICENSE file that
015: * accompanied this code).
016: *
017: * You should have received a copy of the GNU General Public License version
018: * 2 along with this work; if not, write to the Free Software Foundation,
019: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020: *
021: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022: * CA 95054 USA or visit www.sun.com if you need additional information or
023: * have any questions.
024: */
025:
026: package com.sun.xml.internal.bind.v2.runtime.unmarshaller;
027:
028: import java.util.Collection;
029: import java.util.Collections;
030:
031: import javax.xml.bind.Unmarshaller;
032: import javax.xml.bind.ValidationEvent;
033: import javax.xml.bind.helpers.ValidationEventImpl;
034: import javax.xml.namespace.QName;
035:
036: import com.sun.xml.internal.bind.v2.runtime.JaxBeanInfo;
037:
038: import org.xml.sax.SAXException;
039:
040: /**
041: * @author Kohsuke Kawaguchi
042: */
043: public abstract class Loader {
044:
045: // allow derived classes to change it later
046: protected boolean expectText;
047:
048: protected Loader(boolean expectText) {
049: this .expectText = expectText;
050: }
051:
052: protected Loader() {
053: }
054:
055: //
056: //
057: //
058: // Contract
059: //
060: //
061: //
062: /**
063: * Called when the loader is activated, which is when a new start tag is seen
064: * and when the parent designated this loader as the child loader.
065: *
066: * <p>
067: * The callee may change <tt>state.loader</tt> to designate another {@link Loader}
068: * for the processing. It's the responsibility of the callee to forward the startElement
069: * event in such a case.
070: *
071: * @param ea
072: * info about the start tag. never null.
073: */
074: public void startElement(UnmarshallingContext.State state,
075: TagName ea) throws SAXException {
076: }
077:
078: /**
079: * Called when this loaderis an active loaderand we see a new child start tag.
080: *
081: * <p>
082: * The callee is expected to designate another loaderas a loaderthat processes
083: * this element, then it should also register a {@link Receiver}.
084: * The designated loaderwill become an active loader.
085: *
086: * <p>
087: * The default implementation reports an error saying an element is unexpected.
088: */
089: public void childElement(UnmarshallingContext.State state,
090: TagName ea) throws SAXException {
091: // notify the error, then recover by ignoring the whole element.
092: reportUnexpectedChildElement(ea, true);
093: state.loader = Discarder.INSTANCE;
094: state.receiver = null;
095: }
096:
097: protected final void reportUnexpectedChildElement(TagName ea,
098: boolean canRecover) throws SAXException {
099: reportError(Messages.UNEXPECTED_ELEMENT.format(ea.uri,
100: ea.local, computeExpectedElements()), canRecover);
101: }
102:
103: /**
104: * Returns a set of tag names expected as possible child elements in this context.
105: */
106: public Collection<QName> getExpectedChildElements() {
107: return Collections.emptyList();
108: }
109:
110: /**
111: * Called when this loaderis an active loaderand we see a chunk of text.
112: *
113: * The runtime makes sure that adjacent characters (even those separated
114: * by comments, PIs, etc) are reported as one event.
115: * IOW, you won't see two text event calls in a row.
116: */
117: public void text(UnmarshallingContext.State state, CharSequence text)
118: throws SAXException {
119: // make str printable
120: text = text.toString().replace('\r', ' ').replace('\n', ' ')
121: .replace('\t', ' ').trim();
122: reportError(Messages.UNEXPECTED_TEXT.format(text), true);
123: }
124:
125: /**
126: * True if this loader expects the {@link #text(UnmarshallingContext.State, CharSequence)} method
127: * to be called. False otherwise.
128: */
129: public final boolean expectText() {
130: return expectText;
131: }
132:
133: /**
134: * Called when this loaderis an active loaderand we see an end tag.
135: */
136: public void leaveElement(UnmarshallingContext.State state,
137: TagName ea) throws SAXException {
138: }
139:
140: //
141: //
142: //
143: // utility methods
144: //
145: //
146: //
147: /**
148: * Computes the names of possible root elements for a better error diagnosis.
149: */
150: private String computeExpectedElements() {
151: StringBuilder r = new StringBuilder();
152:
153: for (QName n : getExpectedChildElements()) {
154: if (r.length() != 0)
155: r.append(',');
156: r.append("<{").append(n.getNamespaceURI()).append('}')
157: .append(n.getLocalPart()).append('>');
158: }
159: if (r.length() == 0) {
160: return "(none)";
161: }
162:
163: return r.toString();
164: }
165:
166: /**
167: * Fires the beforeUnmarshal event if necessary.
168: *
169: * @param state
170: * state of the newly create child object.
171: */
172: protected final void fireBeforeUnmarshal(JaxBeanInfo beanInfo,
173: Object child, UnmarshallingContext.State state)
174: throws SAXException {
175: if (beanInfo.lookForLifecycleMethods()) {
176: UnmarshallingContext context = state.getContext();
177: Unmarshaller.Listener listener = context.parent
178: .getListener();
179: if (beanInfo.hasBeforeUnmarshalMethod()) {
180: beanInfo.invokeBeforeUnmarshalMethod(context.parent,
181: child, state.prev.target);
182: }
183: if (listener != null) {
184: listener.beforeUnmarshal(child, state.prev.target);
185: }
186: }
187: }
188:
189: /**
190: * Fires the afterUnmarshal event if necessary.
191: *
192: * @param state
193: * state of the parent object
194: */
195: protected final void fireAfterUnmarshal(JaxBeanInfo beanInfo,
196: Object child, UnmarshallingContext.State state)
197: throws SAXException {
198: // fire the event callback
199: if (beanInfo.lookForLifecycleMethods()) {
200: UnmarshallingContext context = state.getContext();
201: Unmarshaller.Listener listener = context.parent
202: .getListener();
203: if (beanInfo.hasAfterUnmarshalMethod()) {
204: beanInfo.invokeAfterUnmarshalMethod(context.parent,
205: child, state.target);
206: }
207: if (listener != null)
208: listener.afterUnmarshal(child, state.target);
209: }
210: }
211:
212: /**
213: * Last resort when something goes terribly wrong within the unmarshaller.
214: */
215: protected static void handleGenericException(Exception e)
216: throws SAXException {
217: handleGenericException(e, false);
218: }
219:
220: public static void handleGenericException(Exception e,
221: boolean canRecover) throws SAXException {
222: reportError(e.getMessage(), e, canRecover);
223: }
224:
225: protected static void reportError(String msg, boolean canRecover)
226: throws SAXException {
227: reportError(msg, null, canRecover);
228: }
229:
230: public static void reportError(String msg, Exception nested,
231: boolean canRecover) throws SAXException {
232: UnmarshallingContext context = UnmarshallingContext
233: .getInstance();
234: context.handleEvent(new ValidationEventImpl(
235: canRecover ? ValidationEvent.ERROR
236: : ValidationEvent.FATAL_ERROR, msg, context
237: .getLocator().getLocation(), nested),
238: canRecover);
239: }
240:
241: /**
242: * This method is called by the generated derived class
243: * when a datatype parse method throws an exception.
244: */
245: protected static void handleParseConversionException(
246: UnmarshallingContext.State state, Exception e)
247: throws SAXException {
248: // wrap it into a ParseConversionEvent and report it
249: state.getContext().handleError(e);
250: }
251: }
|