001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common Development
008: * and Distribution License("CDDL") (collectively, the "License"). You
009: * may not use this file except in compliance with the License. You can obtain
010: * a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html
011: * or glassfish/bootstrap/legal/LICENSE.txt. See the License for the specific
012: * language governing permissions and limitations under the License.
013: *
014: * When distributing the software, include this License Header Notice in each
015: * file and include the License file at glassfish/bootstrap/legal/LICENSE.txt.
016: * Sun designates this particular file as subject to the "Classpath" exception
017: * as provided by Sun in the GPL Version 2 section of the License file that
018: * accompanied this code. If applicable, add the following below the License
019: * Header, with the fields enclosed by brackets [] replaced by your own
020: * identifying information: "Portions Copyrighted [year]
021: * [name of copyright owner]"
022: *
023: * Contributor(s):
024: *
025: * If you wish your version of this file to be governed by only the CDDL or
026: * only the GPL Version 2, indicate your decision by adding "[Contributor]
027: * elects to include this software in this distribution under the [CDDL or GPL
028: * Version 2] license." If you don't indicate a single choice of license, a
029: * recipient has the option to distribute your version of this file under
030: * either the CDDL, the GPL Version 2 or to extend the choice of license to
031: * its licensees as provided above. However, if you add GPL Version 2 code
032: * and therefore, elected the GPL Version 2 license, then the option applies
033: * only if the new code is made subject to such option by the copyright
034: * holder.
035: */
036:
037: package com.sun.xml.bind.v2.runtime.unmarshaller;
038:
039: import java.util.Collection;
040: import java.util.Collections;
041:
042: import javax.xml.bind.Unmarshaller;
043: import javax.xml.bind.ValidationEvent;
044: import javax.xml.bind.helpers.ValidationEventImpl;
045: import javax.xml.namespace.QName;
046:
047: import com.sun.xml.bind.v2.runtime.JaxBeanInfo;
048:
049: import org.xml.sax.SAXException;
050:
051: /**
052: * @author Kohsuke Kawaguchi
053: */
054: public abstract class Loader {
055:
056: // allow derived classes to change it later
057: protected boolean expectText;
058:
059: protected Loader(boolean expectText) {
060: this .expectText = expectText;
061: }
062:
063: protected Loader() {
064: }
065:
066: //
067: //
068: //
069: // Contract
070: //
071: //
072: //
073: /**
074: * Called when the loader is activated, which is when a new start tag is seen
075: * and when the parent designated this loader as the child loader.
076: *
077: * <p>
078: * The callee may change <tt>state.loader</tt> to designate another {@link Loader}
079: * for the processing. It's the responsibility of the callee to forward the startElement
080: * event in such a case.
081: *
082: * @param ea
083: * info about the start tag. never null.
084: */
085: public void startElement(UnmarshallingContext.State state,
086: TagName ea) throws SAXException {
087: }
088:
089: /**
090: * Called when this loaderis an active loaderand we see a new child start tag.
091: *
092: * <p>
093: * The callee is expected to designate another loaderas a loaderthat processes
094: * this element, then it should also register a {@link Receiver}.
095: * The designated loaderwill become an active loader.
096: *
097: * <p>
098: * The default implementation reports an error saying an element is unexpected.
099: */
100: public void childElement(UnmarshallingContext.State state,
101: TagName ea) throws SAXException {
102: // notify the error, then recover by ignoring the whole element.
103: reportUnexpectedChildElement(ea, true);
104: state.loader = Discarder.INSTANCE;
105: state.receiver = null;
106: }
107:
108: @SuppressWarnings({"StringEquality"})
109: protected final void reportUnexpectedChildElement(TagName ea,
110: boolean canRecover) throws SAXException {
111: if (canRecover
112: && !UnmarshallingContext.getInstance().parent
113: .hasEventHandler())
114: // this error happens particurly often (when input documents contain a lot of unexpected elements to be ignored),
115: // so don't bother computing all the messages and etc if we know that
116: // there's no event handler to receive the error in the end. See #286
117: return;
118: if (ea.uri != ea.uri.intern() || ea.local != ea.local.intern())
119: reportError(Messages.UNINTERNED_STRINGS.format(),
120: canRecover);
121: else
122: reportError(Messages.UNEXPECTED_ELEMENT.format(ea.uri,
123: ea.local, computeExpectedElements()), canRecover);
124: }
125:
126: /**
127: * Returns a set of tag names expected as possible child elements in this context.
128: */
129: public Collection<QName> getExpectedChildElements() {
130: return Collections.emptyList();
131: }
132:
133: /**
134: * Called when this loaderis an active loaderand we see a chunk of text.
135: *
136: * The runtime makes sure that adjacent characters (even those separated
137: * by comments, PIs, etc) are reported as one event.
138: * IOW, you won't see two text event calls in a row.
139: */
140: public void text(UnmarshallingContext.State state, CharSequence text)
141: throws SAXException {
142: // make str printable
143: text = text.toString().replace('\r', ' ').replace('\n', ' ')
144: .replace('\t', ' ').trim();
145: reportError(Messages.UNEXPECTED_TEXT.format(text), true);
146: }
147:
148: /**
149: * True if this loader expects the {@link #text(UnmarshallingContext.State, CharSequence)} method
150: * to be called. False otherwise.
151: */
152: public final boolean expectText() {
153: return expectText;
154: }
155:
156: /**
157: * Called when this loaderis an active loaderand we see an end tag.
158: */
159: public void leaveElement(UnmarshallingContext.State state,
160: TagName ea) throws SAXException {
161: }
162:
163: //
164: //
165: //
166: // utility methods
167: //
168: //
169: //
170: /**
171: * Computes the names of possible root elements for a better error diagnosis.
172: */
173: private String computeExpectedElements() {
174: StringBuilder r = new StringBuilder();
175:
176: for (QName n : getExpectedChildElements()) {
177: if (r.length() != 0)
178: r.append(',');
179: r.append("<{").append(n.getNamespaceURI()).append('}')
180: .append(n.getLocalPart()).append('>');
181: }
182: if (r.length() == 0) {
183: return "(none)";
184: }
185:
186: return r.toString();
187: }
188:
189: /**
190: * Fires the beforeUnmarshal event if necessary.
191: *
192: * @param state
193: * state of the newly create child object.
194: */
195: protected final void fireBeforeUnmarshal(JaxBeanInfo beanInfo,
196: Object child, UnmarshallingContext.State state)
197: throws SAXException {
198: if (beanInfo.lookForLifecycleMethods()) {
199: UnmarshallingContext context = state.getContext();
200: Unmarshaller.Listener listener = context.parent
201: .getListener();
202: if (beanInfo.hasBeforeUnmarshalMethod()) {
203: beanInfo.invokeBeforeUnmarshalMethod(context.parent,
204: child, state.prev.target);
205: }
206: if (listener != null) {
207: listener.beforeUnmarshal(child, state.prev.target);
208: }
209: }
210: }
211:
212: /**
213: * Fires the afterUnmarshal event if necessary.
214: *
215: * @param state
216: * state of the parent object
217: */
218: protected final void fireAfterUnmarshal(JaxBeanInfo beanInfo,
219: Object child, UnmarshallingContext.State state)
220: throws SAXException {
221: // fire the event callback
222: if (beanInfo.lookForLifecycleMethods()) {
223: UnmarshallingContext context = state.getContext();
224: Unmarshaller.Listener listener = context.parent
225: .getListener();
226: if (beanInfo.hasAfterUnmarshalMethod()) {
227: beanInfo.invokeAfterUnmarshalMethod(context.parent,
228: child, state.target);
229: }
230: if (listener != null)
231: listener.afterUnmarshal(child, state.target);
232: }
233: }
234:
235: /**
236: * Last resort when something goes terribly wrong within the unmarshaller.
237: */
238: protected static void handleGenericException(Exception e)
239: throws SAXException {
240: handleGenericException(e, false);
241: }
242:
243: public static void handleGenericException(Exception e,
244: boolean canRecover) throws SAXException {
245: reportError(e.getMessage(), e, canRecover);
246: }
247:
248: protected static void reportError(String msg, boolean canRecover)
249: throws SAXException {
250: reportError(msg, null, canRecover);
251: }
252:
253: public static void reportError(String msg, Exception nested,
254: boolean canRecover) throws SAXException {
255: UnmarshallingContext context = UnmarshallingContext
256: .getInstance();
257: context.handleEvent(new ValidationEventImpl(
258: canRecover ? ValidationEvent.ERROR
259: : ValidationEvent.FATAL_ERROR, msg, context
260: .getLocator().getLocation(), nested),
261: canRecover);
262: }
263:
264: /**
265: * This method is called by the generated derived class
266: * when a datatype parse method throws an exception.
267: */
268: protected static void handleParseConversionException(
269: UnmarshallingContext.State state, Exception e)
270: throws SAXException {
271: // wrap it into a ParseConversionEvent and report it
272: state.getContext().handleError(e);
273: }
274: }
|