From 2d8cf20d0d5ca6b1fbdefc22229d4b7cf1497ede Mon Sep 17 00:00:00 2001 From: mark Date: Tue, 17 Jan 2006 18:09:40 +0000 Subject: Imported GNU Classpath 0.20 * Makefile.am (AM_CPPFLAGS): Add classpath/include. * java/nio/charset/spi/CharsetProvider.java: New override file. * java/security/Security.java: Likewise. * sources.am: Regenerated. * Makefile.in: Likewise. git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@109831 138bc75d-0d04-0410-961f-82ee72b054a4 --- libjava/classpath/gnu/CORBA/Poa/AOM.java | 58 +- libjava/classpath/gnu/CORBA/Poa/gnuPOA.java | 76 +- libjava/classpath/gnu/classpath/jdwp/Jdwp.java | 35 +- .../gnu/classpath/jdwp/event/EventManager.java | 10 +- .../classpath/gnu/java/awt/image/ImageDecoder.java | 32 + .../gnu/java/awt/peer/gtk/GdkGraphics.java | 94 +- .../gnu/java/awt/peer/gtk/GdkGraphics2D.java | 15 +- .../gnu/java/awt/peer/gtk/GdkPixbufDecoder.java | 15 +- .../beans/encoder/ArrayPersistenceDelegate.java | 153 + .../beans/encoder/ClassPersistenceDelegate.java | 80 + .../encoder/CollectionPersistenceDelegate.java | 84 + .../classpath/gnu/java/beans/encoder/Context.java | 88 + .../java/beans/encoder/GenericScannerState.java | 257 + .../java/beans/encoder/IgnoringScannerState.java | 133 + .../java/beans/encoder/MapPersistenceDelegate.java | 81 + .../classpath/gnu/java/beans/encoder/ObjectId.java | 132 + .../encoder/PrimitivePersistenceDelegate.java | 74 + .../java/beans/encoder/ReportingScannerState.java | 131 + libjava/classpath/gnu/java/beans/encoder/Root.java | 198 + .../gnu/java/beans/encoder/ScanEngine.java | 860 ++++ .../gnu/java/beans/encoder/ScannerState.java | 236 + .../gnu/java/beans/encoder/StAXWriter.java | 233 + .../classpath/gnu/java/beans/encoder/Writer.java | 174 + .../beans/encoder/elements/ArrayInstantiation.java | 74 + .../gnu/java/beans/encoder/elements/Array_Get.java | 62 + .../gnu/java/beans/encoder/elements/Array_Set.java | 57 + .../beans/encoder/elements/ClassResolution.java | 67 + .../gnu/java/beans/encoder/elements/Element.java | 157 + .../gnu/java/beans/encoder/elements/List_Get.java | 56 + .../gnu/java/beans/encoder/elements/List_Set.java | 56 + .../beans/encoder/elements/MethodInvocation.java | 62 + .../java/beans/encoder/elements/NullObject.java | 61 + .../encoder/elements/ObjectInstantiation.java | 68 + .../beans/encoder/elements/ObjectReference.java | 68 + .../encoder/elements/PrimitiveInstantiation.java | 69 + .../beans/encoder/elements/StaticFieldAccess.java | 66 + .../encoder/elements/StaticMethodInvocation.java | 67 + .../beans/encoder/elements/StringReference.java | 63 + .../classpath/gnu/java/net/CRLFInputStream.java | 2 +- .../gnu/java/net/PlainDatagramSocketImpl.java | 133 +- .../classpath/gnu/java/net/PlainSocketImpl.java | 244 +- .../gnu/java/nio/channels/FileChannelImpl.java | 6 +- .../classpath/gnu/java/nio/charset/Provider.java | 27 +- .../gnu/java/nio/charset/iconv/IconvProvider.java | 15 +- .../classpath/gnu/java/rmi/server/UnicastRef.java | 3 +- libjava/classpath/gnu/java/security/Engine.java | 11 +- .../gnu/java/util/WeakIdentityHashMap.java | 862 ++++ libjava/classpath/gnu/regexp/RE.java | 54 +- libjava/classpath/gnu/regexp/RETokenRepeated.java | 21 +- .../gnu/xml/aelfred2/ContentHandler2.java | 65 - libjava/classpath/gnu/xml/aelfred2/SAXDriver.java | 15 - libjava/classpath/gnu/xml/aelfred2/XmlParser.java | 3 - libjava/classpath/gnu/xml/dom/Consumer.java | 16 - libjava/classpath/gnu/xml/dom/DomDocument.java | 25 + libjava/classpath/gnu/xml/dom/DomElement.java | 33 + libjava/classpath/gnu/xml/dom/DomNamedNodeMap.java | 2 +- libjava/classpath/gnu/xml/dom/DomNode.java | 215 +- libjava/classpath/gnu/xml/dom/ls/DomLSParser.java | 1 + libjava/classpath/gnu/xml/dom/ls/SAXEventSink.java | 182 +- .../classpath/gnu/xml/pipeline/DomConsumer.java | 17 +- .../classpath/gnu/xml/pipeline/EventFilter.java | 15 +- .../classpath/gnu/xml/stream/BufferedReader.java | 198 + libjava/classpath/gnu/xml/stream/CRLFReader.java | 180 + libjava/classpath/gnu/xml/stream/SAXParser.java | 966 ++++ .../classpath/gnu/xml/stream/SAXParserFactory.java | 104 + .../classpath/gnu/xml/stream/UnicodeReader.java | 201 + .../classpath/gnu/xml/stream/XIncludeFilter.java | 932 ++++ .../gnu/xml/stream/XMLInputFactoryImpl.java | 75 +- libjava/classpath/gnu/xml/stream/XMLParser.java | 5266 ++++++++++++++++++++ .../gnu/xml/stream/XMLStreamWriterImpl.java | 2 + .../classpath/gnu/xml/transform/AttributeNode.java | 54 +- libjava/classpath/gnu/xml/transform/Bindings.java | 22 + .../xml/transform/ElementAvailableFunction.java | 19 +- .../classpath/gnu/xml/transform/ElementNode.java | 157 +- .../xml/transform/FunctionAvailableFunction.java | 28 +- .../classpath/gnu/xml/transform/LiteralNode.java | 64 +- .../classpath/gnu/xml/transform/MessageNode.java | 23 +- .../gnu/xml/transform/NamespaceProxy.java | 77 + .../gnu/xml/transform/StreamSerializer.java | 474 +- .../gnu/xml/transform/StrippingInstruction.java | 73 + .../classpath/gnu/xml/transform/Stylesheet.java | 375 +- .../gnu/xml/transform/TransformerFactoryImpl.java | 49 +- .../gnu/xml/transform/TransformerImpl.java | 240 +- .../classpath/gnu/xml/transform/ValueOfNode.java | 56 +- libjava/classpath/gnu/xml/xpath/Expr.java | 45 +- .../classpath/gnu/xml/xpath/LocalNameFunction.java | 6 +- libjava/classpath/gnu/xml/xpath/NameFunction.java | 6 +- libjava/classpath/gnu/xml/xpath/NameTest.java | 39 +- libjava/classpath/gnu/xml/xpath/NamespaceTest.java | 26 +- .../gnu/xml/xpath/NamespaceUriFunction.java | 6 +- libjava/classpath/gnu/xml/xpath/NodeTypeTest.java | 20 +- libjava/classpath/gnu/xml/xpath/Selector.java | 157 +- .../classpath/gnu/xml/xpath/SubstringFunction.java | 21 +- 93 files changed, 14651 insertions(+), 1579 deletions(-) create mode 100644 libjava/classpath/gnu/java/beans/encoder/ArrayPersistenceDelegate.java create mode 100644 libjava/classpath/gnu/java/beans/encoder/ClassPersistenceDelegate.java create mode 100644 libjava/classpath/gnu/java/beans/encoder/CollectionPersistenceDelegate.java create mode 100644 libjava/classpath/gnu/java/beans/encoder/Context.java create mode 100644 libjava/classpath/gnu/java/beans/encoder/GenericScannerState.java create mode 100644 libjava/classpath/gnu/java/beans/encoder/IgnoringScannerState.java create mode 100644 libjava/classpath/gnu/java/beans/encoder/MapPersistenceDelegate.java create mode 100644 libjava/classpath/gnu/java/beans/encoder/ObjectId.java create mode 100644 libjava/classpath/gnu/java/beans/encoder/PrimitivePersistenceDelegate.java create mode 100644 libjava/classpath/gnu/java/beans/encoder/ReportingScannerState.java create mode 100644 libjava/classpath/gnu/java/beans/encoder/Root.java create mode 100644 libjava/classpath/gnu/java/beans/encoder/ScanEngine.java create mode 100644 libjava/classpath/gnu/java/beans/encoder/ScannerState.java create mode 100644 libjava/classpath/gnu/java/beans/encoder/StAXWriter.java create mode 100644 libjava/classpath/gnu/java/beans/encoder/Writer.java create mode 100644 libjava/classpath/gnu/java/beans/encoder/elements/ArrayInstantiation.java create mode 100644 libjava/classpath/gnu/java/beans/encoder/elements/Array_Get.java create mode 100644 libjava/classpath/gnu/java/beans/encoder/elements/Array_Set.java create mode 100644 libjava/classpath/gnu/java/beans/encoder/elements/ClassResolution.java create mode 100644 libjava/classpath/gnu/java/beans/encoder/elements/Element.java create mode 100644 libjava/classpath/gnu/java/beans/encoder/elements/List_Get.java create mode 100644 libjava/classpath/gnu/java/beans/encoder/elements/List_Set.java create mode 100644 libjava/classpath/gnu/java/beans/encoder/elements/MethodInvocation.java create mode 100644 libjava/classpath/gnu/java/beans/encoder/elements/NullObject.java create mode 100644 libjava/classpath/gnu/java/beans/encoder/elements/ObjectInstantiation.java create mode 100644 libjava/classpath/gnu/java/beans/encoder/elements/ObjectReference.java create mode 100644 libjava/classpath/gnu/java/beans/encoder/elements/PrimitiveInstantiation.java create mode 100644 libjava/classpath/gnu/java/beans/encoder/elements/StaticFieldAccess.java create mode 100644 libjava/classpath/gnu/java/beans/encoder/elements/StaticMethodInvocation.java create mode 100644 libjava/classpath/gnu/java/beans/encoder/elements/StringReference.java create mode 100644 libjava/classpath/gnu/java/util/WeakIdentityHashMap.java delete mode 100644 libjava/classpath/gnu/xml/aelfred2/ContentHandler2.java create mode 100644 libjava/classpath/gnu/xml/stream/BufferedReader.java create mode 100644 libjava/classpath/gnu/xml/stream/CRLFReader.java create mode 100644 libjava/classpath/gnu/xml/stream/SAXParser.java create mode 100644 libjava/classpath/gnu/xml/stream/SAXParserFactory.java create mode 100644 libjava/classpath/gnu/xml/stream/UnicodeReader.java create mode 100644 libjava/classpath/gnu/xml/stream/XIncludeFilter.java create mode 100644 libjava/classpath/gnu/xml/stream/XMLParser.java create mode 100644 libjava/classpath/gnu/xml/transform/NamespaceProxy.java create mode 100644 libjava/classpath/gnu/xml/transform/StrippingInstruction.java (limited to 'libjava/classpath/gnu') diff --git a/libjava/classpath/gnu/CORBA/Poa/AOM.java b/libjava/classpath/gnu/CORBA/Poa/AOM.java index 9faf0883ded..70e787af151 100644 --- a/libjava/classpath/gnu/CORBA/Poa/AOM.java +++ b/libjava/classpath/gnu/CORBA/Poa/AOM.java @@ -40,6 +40,8 @@ package gnu.CORBA.Poa; import gnu.CORBA.ByteArrayComparator; +import org.omg.CORBA.portable.Delegate; +import org.omg.CORBA.portable.ObjectImpl; import org.omg.PortableServer.Servant; import java.util.Iterator; @@ -66,7 +68,7 @@ public class AOM /** * Create an initialised instance. */ - Obj(org.omg.CORBA.Object _object, byte[] _key, Servant _servant, gnuPOA _poa) + Obj(gnuServantObject _object, byte[] _key, Servant _servant, gnuPOA _poa) { object = _object; key = _key; @@ -77,7 +79,7 @@ public class AOM /** * The object. */ - public final org.omg.CORBA.Object object; + public final gnuServantObject object; /** * The servant, serving the given object. @@ -158,14 +160,13 @@ public class AOM Map objects = new TreeMap(new ByteArrayComparator()); /** - * Get the record of the stored object. If the object is mapped - * several times under the different keys, one of the mappings - * is used. - * + * Get the record of the stored object. If the object is mapped several times + * under the different keys, one of the mappings is used. + * * @param object the stored object - * - * @return the record about the stored object, null if - * this object is not stored here. + * + * @return the record about the stored object, null if this object is not + * stored here. */ public Obj findObject(org.omg.CORBA.Object stored_object) { @@ -173,9 +174,33 @@ public class AOM return null; Map.Entry item; - Iterator iter = objects.entrySet().iterator(); + Iterator iter; Obj ref; + if (stored_object instanceof ObjectImpl) + { + // If the delegate is available, search by delegate. + Delegate d = ((ObjectImpl) stored_object)._get_delegate(); + Delegate d2; + + if (d != null) + { + iter = objects.entrySet().iterator(); + while (iter.hasNext()) + { + item = (Map.Entry) iter.next(); + ref = (Obj) item.getValue(); + d2 = ref.object._get_delegate(); + + if (d == d2 || (d2 != null && d2.equals(d))) + return ref; + } + } + } + + // For other objects (or if not possible to get the delegate), + // search by .equals + iter = objects.entrySet().iterator(); while (iter.hasNext()) { item = (Map.Entry) iter.next(); @@ -187,12 +212,11 @@ public class AOM } /** - * Find the reference info for the given servant. - * If the servant is mapped to several objects, this - * returns the first found occurence. - * + * Find the reference info for the given servant. If the servant is mapped to + * several objects, this returns the first found occurence. + * * @param servant a servant to find. - * + * * @return the servant/object/POA binding or null if no such found. */ public Obj findServant(Servant servant) @@ -257,7 +281,7 @@ public class AOM * * @return the newly created object record. */ - public Obj add(org.omg.CORBA.Object object, Servant servant, gnuPOA poa) + public Obj add(gnuServantObject object, Servant servant, gnuPOA poa) { return add(generateObjectKey(object), object, servant, poa); } @@ -270,7 +294,7 @@ public class AOM * @param servant a servant, serving the given object. * @param poa the POA, where the object is connected. */ - public Obj add(byte[] key, org.omg.CORBA.Object object, Servant servant, + public Obj add(byte[] key, gnuServantObject object, Servant servant, gnuPOA poa ) { diff --git a/libjava/classpath/gnu/CORBA/Poa/gnuPOA.java b/libjava/classpath/gnu/CORBA/Poa/gnuPOA.java index 6f2a019a3bb..460a0a6029a 100644 --- a/libjava/classpath/gnu/CORBA/Poa/gnuPOA.java +++ b/libjava/classpath/gnu/CORBA/Poa/gnuPOA.java @@ -1052,12 +1052,12 @@ public class gnuPOA /** * Returns the servant that is serving this object. - * - * @return if the RETAIN policy applies and the object is in the Active - * Object Map, the method returns the servant, associated with this object. + * + * @return if the RETAIN policy applies and the object is in the Active Object + * Map, the method returns the servant, associated with this object. * Otherwise, if the USE_DEFAULT_SERVANT policy applies, the method returns * the default servant (if one was set). - * + * * @throws ObjectNotActive if none of the conditions above are satisfied. * @throws WrongAdapter if the object reference was not created with this POA. * @throws WrongPolicy. This method requires either RETAIN or @@ -1065,14 +1065,26 @@ public class gnuPOA * apply to this POA. */ public Servant reference_to_servant(org.omg.CORBA.Object the_Object) - throws ObjectNotActive, WrongPolicy, - WrongAdapter + throws ObjectNotActive, WrongPolicy, WrongAdapter { if (applies(ServantRetentionPolicyValue.RETAIN)) { AOM.Obj ref = aom.findObject(the_Object); if (ref == null) - throw new WrongAdapter(); + { + String object; + if (the_Object == null) + object = "null passed"; + else if (the_Object instanceof gnuServantObject) + { + gnuServantObject gs = (gnuServantObject) the_Object; + object = "Wrong owner POA " + gs.poa.the_name(); + } + else + object = "Unknown " + the_Object.getClass().getName(); + + throw new WrongAdapter(object + " for '" + the_name() + "'"); + } else if (ref.isDeactiveted() || ref.servant == null) { if (default_servant != null) @@ -1092,32 +1104,30 @@ public class gnuPOA } /** - * Returns the id of the object, served by the given servant - * (assuming that the servant serves only one object). - * The id is found in one of the following ways. - * - * @throws ServantNotActive in all cases, not listed in the list above. - * @throws WrongPolicy The method requres USE_DEFAULT_SERVANT policy or - * a combination of the RETAIN policy and either the UNIQUE_ID or - * IMPLICIT_ACTIVATION policies and throws the WrongPolicy if these conditions - * are not satisfied. - */ + * Returns the id of the object, served by the given servant (assuming that + * the servant serves only one object). The id is found in one of the + * following ways. + * + * + * @throws ServantNotActive in all cases, not listed in the list above. + * @throws WrongPolicy The method requres USE_DEFAULT_SERVANT policy or a + * combination of the RETAIN policy and either the UNIQUE_ID or + * IMPLICIT_ACTIVATION policies and throws the WrongPolicy if these conditions + * are not satisfied. + */ public byte[] servant_to_id(Servant the_Servant) throws ServantNotActive, WrongPolicy { diff --git a/libjava/classpath/gnu/classpath/jdwp/Jdwp.java b/libjava/classpath/gnu/classpath/jdwp/Jdwp.java index bb8f60224ce..43a37de2435 100644 --- a/libjava/classpath/gnu/classpath/jdwp/Jdwp.java +++ b/libjava/classpath/gnu/classpath/jdwp/Jdwp.java @@ -42,6 +42,7 @@ package gnu.classpath.jdwp; import gnu.classpath.jdwp.event.Event; import gnu.classpath.jdwp.event.EventManager; import gnu.classpath.jdwp.event.EventRequest; +import gnu.classpath.jdwp.exception.JdwpException; import gnu.classpath.jdwp.id.ThreadId; import gnu.classpath.jdwp.processor.PacketProcessor; import gnu.classpath.jdwp.transport.ITransport; @@ -206,7 +207,20 @@ public class Jdwp EventManager em = EventManager.getDefault (); EventRequest request = em.getEventRequest (event); if (request != null) - sendEvent (request, event); + { + try + { + System.out.println ("Jdwp.notify: sending event " + event); + sendEvent (request, event); + jdwp._enforceSuspendPolicy (request.getSuspendPolicy ()); + } + catch (Exception e) + { + /* Really not much we can do. For now, just print out + a warning to the user. */ + System.out.println ("Jdwp.notify: caught exception: " + e); + } + } } } @@ -217,32 +231,25 @@ public class Jdwp * * @param request the debugger request for the event * @param event the event to send + * @throws IOException if a communications failure occurs */ public static void sendEvent (EventRequest request, Event event) + throws IOException { Jdwp jdwp = getDefault (); if (jdwp != null) { - try - { - // !! May need to implement send queue? - synchronized (jdwp._connection) - { - jdwp._connection.sendEvent (request, event); - } - - // Follow suspend policy - jdwp._enforceSuspendPolicy (request.getSuspendPolicy ()); - } - catch (IOException ie) + // !! May need to implement send queue? + synchronized (jdwp._connection) { - System.out.println ("Jdwp.notify: caught exception: " + ie); + jdwp._connection.sendEvent (request, event); } } } // Helper function to enforce suspend policies on event notification private void _enforceSuspendPolicy (byte suspendPolicy) + throws JdwpException { switch (suspendPolicy) { diff --git a/libjava/classpath/gnu/classpath/jdwp/event/EventManager.java b/libjava/classpath/gnu/classpath/jdwp/event/EventManager.java index 436a544eb3c..82d1d71e41e 100644 --- a/libjava/classpath/gnu/classpath/jdwp/event/EventManager.java +++ b/libjava/classpath/gnu/classpath/jdwp/event/EventManager.java @@ -41,6 +41,7 @@ package gnu.classpath.jdwp.event; import gnu.classpath.jdwp.VMVirtualMachine; import gnu.classpath.jdwp.exception.InvalidEventTypeException; +import gnu.classpath.jdwp.exception.JdwpException; import java.util.Collection; import java.util.Hashtable; @@ -133,7 +134,7 @@ public class EventManager EventRequest.EVENT_VM_DEATH, EventRequest.SUSPEND_NONE)); } - catch (InvalidEventTypeException e) + catch (JdwpException e) { // This can't happen } @@ -187,9 +188,10 @@ public class EventManager * * @param request the request to monitor * @throws InvalidEventTypeException for invalid event kind + * @throws JdwpException for other errors involving request */ public void requestEvent (EventRequest request) - throws InvalidEventTypeException + throws JdwpException { // Add request to request list Hashtable requests; @@ -212,8 +214,10 @@ public class EventManager * @param kind the event kind * @param id the ID of the request to delete * @throws IllegalArgumentException for invalid event kind + * @throws JdwpException for other errors deleting request */ public void deleteRequest (byte kind, int id) + throws JdwpException { Hashtable requests; requests = (Hashtable) _requests.get (new Byte (kind)); @@ -237,8 +241,10 @@ public class EventManager * * @param kind the event kind * @throws IllegalArgumentException for invalid event kind + * @throws JdwpException for error clearing events */ public void clearRequests (byte kind) + throws JdwpException { Hashtable requests = (Hashtable) _requests.get (new Byte (kind)); if (requests == null) diff --git a/libjava/classpath/gnu/java/awt/image/ImageDecoder.java b/libjava/classpath/gnu/java/awt/image/ImageDecoder.java index 141c8541794..8e8eecb2599 100644 --- a/libjava/classpath/gnu/java/awt/image/ImageDecoder.java +++ b/libjava/classpath/gnu/java/awt/image/ImageDecoder.java @@ -40,6 +40,8 @@ package gnu.java.awt.image; import java.awt.image.ImageConsumer; import java.awt.image.ImageProducer; import java.io.ByteArrayInputStream; +import java.io.DataInput; +import java.io.EOFException; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; @@ -55,6 +57,7 @@ public abstract class ImageDecoder implements ImageProducer int offset; int length; InputStream input; + DataInput datainput; static { @@ -79,6 +82,11 @@ public abstract class ImageDecoder implements ImageProducer this.input = is; } + public ImageDecoder (DataInput datainput) + { + this.datainput = datainput; + } + public ImageDecoder (byte[] imagedata, int imageoffset, int imagelength) { data = imagedata; @@ -119,6 +127,8 @@ public abstract class ImageDecoder implements ImageProducer { if (url != null) input = url.openStream(); + else if (datainput != null) + input = new DataInputStreamWrapper(datainput); else { if (filename != null) @@ -153,4 +163,26 @@ public abstract class ImageDecoder implements ImageProducer } public abstract void produce (Vector v, InputStream is) throws IOException; + + private static class DataInputStreamWrapper extends InputStream + { + private final DataInput datainput; + + DataInputStreamWrapper(DataInput datainput) + { + this.datainput = datainput; + } + + public int read() throws IOException + { + try + { + return datainput.readByte() & 0xFF; + } + catch (EOFException eofe) + { + return -1; + } + } + } } diff --git a/libjava/classpath/gnu/java/awt/peer/gtk/GdkGraphics.java b/libjava/classpath/gnu/java/awt/peer/gtk/GdkGraphics.java index d80306c8a82..d7217aa7a3f 100644 --- a/libjava/classpath/gnu/java/awt/peer/gtk/GdkGraphics.java +++ b/libjava/classpath/gnu/java/awt/peer/gtk/GdkGraphics.java @@ -48,7 +48,6 @@ import java.awt.Graphics; import java.awt.Image; import java.awt.Rectangle; import java.awt.Shape; -import java.awt.SystemColor; import java.awt.image.ImageObserver; import java.text.AttributedCharacterIterator; @@ -81,18 +80,23 @@ public class GdkGraphics extends Graphics native void initStateUnlocked (GtkComponentPeer component); native void initState (int width, int height); native void initFromImage (GtkImage image); - native void copyState (GdkGraphics g); + native void nativeCopyState (GdkGraphics g); + + /** + * A cached instance that is used by {@link #create} in order to avoid + * massive allocation of graphics contexts. + */ + GdkGraphics cached = null; + + /** + * A link to the parent context. This is used in {@link #dispose} to put + * this graphics context into the cache. + */ + GdkGraphics parent = null; GdkGraphics (GdkGraphics g) { - color = g.color; - xorColor = g.xorColor; - font = g.font; - if (font == null) - font = new Font ("Dialog", Font.PLAIN, 12); - clip = new Rectangle (g.clip); - component = g.component; - + parent = g; copyState (g); } @@ -162,12 +166,54 @@ public class GdkGraphics extends Graphics public native void copyArea(int x, int y, int width, int height, int dx, int dy); - public Graphics create () + /** + * Creates a copy of this GdkGraphics instance. This implementation can + * reuse a cached instance to avoid massive instantiation of Graphics objects + * during painting. + * + * @return a copy of this graphics context + */ + public Graphics create() + { + GdkGraphics copy = cached; + if (copy == null) + copy = new GdkGraphics(this); + else + { + copy.copyState(this); + cached = null; + } + return copy; + } + + public native void nativeDispose(); + + /** + * Disposes this graphics object. This puts this graphics context into the + * cache of its parent graphics if there is one. + */ + public void dispose() { - return new GdkGraphics (this); + if (parent != null) + { + parent.cached = this; + parent = null; + } + else + nativeDispose(); } - public native void dispose(); + /** + * This is called when this object gets finalized by the garbage collector. + * In addition to {@link Graphics#finalize()} this calls nativeDispose() to + * make sure the native resources are freed before the graphics context is + * thrown away. + */ + public void finalize() + { + super.finalize(); + nativeDispose(); + } public boolean drawImage (Image img, int x, int y, Color bgcolor, ImageObserver observer) @@ -248,9 +294,8 @@ public class GdkGraphics extends Graphics public void drawString (String str, int x, int y) { drawString(getFontPeer(), str, x, y); - } + } - public void drawString (AttributedCharacterIterator ci, int x, int y) { throw new Error ("not implemented"); @@ -419,4 +464,23 @@ public class GdkGraphics extends Graphics translateNative (x, y); } + + /** + * Copies over the state of another GdkGraphics to this instance. This is + * used by the {@link #GdkGraphics(GdkGraphics)} constructor and the + * {@link #create()} method. + * + * @param g the GdkGraphics object to copy the state from + */ + private void copyState(GdkGraphics g) + { + color = g.color; + xorColor = g.xorColor; + font = g.font; + if (font == null) + font = new Font ("Dialog", Font.PLAIN, 12); + clip = new Rectangle (g.clip); + component = g.component; + nativeCopyState(g); + } } diff --git a/libjava/classpath/gnu/java/awt/peer/gtk/GdkGraphics2D.java b/libjava/classpath/gnu/java/awt/peer/gtk/GdkGraphics2D.java index c9ed3012658..195304dcee2 100644 --- a/libjava/classpath/gnu/java/awt/peer/gtk/GdkGraphics2D.java +++ b/libjava/classpath/gnu/java/awt/peer/gtk/GdkGraphics2D.java @@ -180,11 +180,14 @@ public class GdkGraphics2D extends Graphics2D else fg = new Color(g.fg.getRGB()); - if (g.bg.getAlpha() != -1) - bg = new Color(g.bg.getRed(), g.bg.getGreen(), g.bg.getBlue(), - g.bg.getAlpha()); - else - bg = new Color(g.bg.getRGB()); + if (g.bg != null) + { + if (g.bg.getAlpha() != -1) + bg = new Color(g.bg.getRed(), g.bg.getGreen(), g.bg.getBlue(), + g.bg.getAlpha()); + else + bg = new Color(g.bg.getRGB()); + } if (g.clip == null) clip = null; @@ -1088,6 +1091,8 @@ public class GdkGraphics2D extends Graphics2D public void setBackground(Color c) { + if (c == null) + c = Color.WHITE; bg = c; } diff --git a/libjava/classpath/gnu/java/awt/peer/gtk/GdkPixbufDecoder.java b/libjava/classpath/gnu/java/awt/peer/gtk/GdkPixbufDecoder.java index 85cb1e47a9e..054ebaaaef3 100644 --- a/libjava/classpath/gnu/java/awt/peer/gtk/GdkPixbufDecoder.java +++ b/libjava/classpath/gnu/java/awt/peer/gtk/GdkPixbufDecoder.java @@ -47,6 +47,7 @@ import java.awt.image.ImageConsumer; import java.awt.image.ImageProducer; import java.awt.image.Raster; import java.awt.image.RenderedImage; +import java.io.DataInput; import java.io.DataOutput; import java.io.IOException; import java.io.InputStream; @@ -102,6 +103,11 @@ public class GdkPixbufDecoder extends gnu.java.awt.image.ImageDecoder 0x00ff0000, 0x0000ff00, 0x000000ff); + public GdkPixbufDecoder (DataInput datainput) + { + super (datainput); + } + public GdkPixbufDecoder (InputStream in) { super (in); @@ -630,7 +636,14 @@ public class GdkPixbufDecoder extends gnu.java.awt.image.ImageDecoder boolean ignoreMetadata) { super.setInput(input, seekForwardOnly, ignoreMetadata); - dec = new GdkPixbufDecoder((InputStream) getInput()); + Object get = getInput(); + if (get instanceof InputStream) + dec = new GdkPixbufDecoder((InputStream) get); + else if (get instanceof DataInput) + dec = new GdkPixbufDecoder((DataInput) get); + else + throw new IllegalArgumentException("input object not supported: " + + get); } public BufferedImage read(int imageIndex, ImageReadParam param) diff --git a/libjava/classpath/gnu/java/beans/encoder/ArrayPersistenceDelegate.java b/libjava/classpath/gnu/java/beans/encoder/ArrayPersistenceDelegate.java new file mode 100644 index 00000000000..12d757e2dde --- /dev/null +++ b/libjava/classpath/gnu/java/beans/encoder/ArrayPersistenceDelegate.java @@ -0,0 +1,153 @@ +/* ArrayPersistenceDelegate.java - A PersistenceDelegate that handles arrays. + Copyright (C) 2005 Free Software Foundation, Inc. + + This file is part of GNU Classpath. + + GNU Classpath is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + GNU Classpath is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNU Classpath; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. + + Linking this library statically or dynamically with other modules is + making a combined work based on this library. Thus, the terms and + conditions of the GNU General Public License cover the whole + combination. + + As a special exception, the copyright holders of this library give you + permission to link this library with independent modules to produce an + executable, regardless of the license terms of these independent + modules, and to copy and distribute the resulting executable under + terms of your choice, provided that you also meet, for each linked + independent module, the terms and conditions of the license of that + module. An independent module is a module which is not derived from + or based on this library. If you modify this library, you may extend + this exception to your version of the library, but you are not + obligated to do so. If you do not wish to do so, delete this + exception statement from your version. */ + + +package gnu.java.beans.encoder; + +import java.beans.Encoder; +import java.beans.Expression; +import java.beans.PersistenceDelegate; +import java.beans.Statement; + +import java.lang.reflect.Array; +import java.util.HashMap; + +public class ArrayPersistenceDelegate extends PersistenceDelegate +{ + private static final HashMap NULL_VALUES = new HashMap(); + + static + { + NULL_VALUES.put(Boolean.TYPE, Boolean.FALSE); + NULL_VALUES.put(Byte.TYPE, Byte.valueOf((byte) 0)); + NULL_VALUES.put(Short.TYPE, Short.valueOf((short) 0)); + NULL_VALUES.put(Integer.TYPE, Integer.valueOf(0)); + NULL_VALUES.put(Long.TYPE, Long.valueOf(0)); + NULL_VALUES.put(Float.TYPE, Float.valueOf(0.0f)); + NULL_VALUES.put(Double.TYPE, Double.valueOf(0.0)); + } + + protected Expression instantiate(Object oldInstance, Encoder out) + { + Class type = oldInstance.getClass().getComponentType(); + + // oldInstance is expected to be an array, then + // getClass().getComponentType() should lead + // to its component type. + assert (type != null); + + // Not handling primitive types in a special way here + // causes that Class.forName("int") is built as an Expression + // later which would cause an exception if executed. A special + // handling to avoid the execution for primitive types can be + // java.beans.Encoder.writeExpression() . + return new Expression( + oldInstance, + Array.class, + "newInstance", + new Object[] { + type, + new Integer(Array.getLength(oldInstance)) }); + } + + protected void initialize(Class type, Object oldInstance, Object newInstance, + Encoder out) + { + int length = Array.getLength(oldInstance); + + // Compares the array value against a prototypical + // null value of the array's component type in order to skip + // writing the default values of an array. + + // Note: I have no idea why the persistence delegate for arrays writes + // an Expression that reads the value and then writes a Statement that sets + // the value. However it turned out that object arrays work better with the + // get-Expression and primitive array work fine with the set-Statement. + + type = type.getComponentType(); + if (type.isPrimitive()) + { + Object nullValue = NULL_VALUES.get(type); + + for (int i = 0; i < length; i++) + { + Object oldValue = Array.get(oldInstance, i); + + if (!oldValue.equals(nullValue)) + { + out.writeExpression(new Expression(Array.class, "get", + new Object[] { oldInstance, + Integer.valueOf(i), + })); + + out.writeStatement(new Statement(Array.class, "set", + new Object[] { + oldInstance, + Integer.valueOf(i), + oldValue + })); + } + } + + } + else + { + + for (int i = 0; i < length; i++) + { + Object oldValue = Array.get(oldInstance, i); + + if (oldValue != null) + { + out.writeExpression(new Expression(Array.class, "get", + new Object[] { oldInstance, + Integer.valueOf(i), + })); + + out.writeStatement(new Statement(Array.class, "set", + new Object[] { + oldInstance, + Integer.valueOf(i), + oldValue + })); + } + } + } + + } + +} diff --git a/libjava/classpath/gnu/java/beans/encoder/ClassPersistenceDelegate.java b/libjava/classpath/gnu/java/beans/encoder/ClassPersistenceDelegate.java new file mode 100644 index 00000000000..7b0656a5d35 --- /dev/null +++ b/libjava/classpath/gnu/java/beans/encoder/ClassPersistenceDelegate.java @@ -0,0 +1,80 @@ +/* ClassPersistenceDelegate.java - A PersistenceDelegate for the Class type. + Copyright (C) 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.java.beans.encoder; + +import java.beans.Encoder; +import java.beans.Expression; +import java.beans.PersistenceDelegate; + +/**

The ClassPersistenceDelegate creates + * Expression instances which denote class resolutions.

+ * + *

The class resolution is always the last step when serializing a tree + * of objects. Due to the recursive nature of the algorithm we need a way + * to end the recursion. This is achieved by the implementation of this + * {@link instantiate} method. Arbitrary classes are described with a call + * to Class.forName. However for the Class class + * we call getClass() on a String.class instance. + * This in turn lead to the resolution of the String class which is always + * encoded as "".getClass(). Finally the Encoder + * treats strings in a special way so that the recursion ends here. + * + * @author Robert Schuster (robertschuster@fsfe.org) + */ +public class ClassPersistenceDelegate extends PersistenceDelegate +{ + + protected Expression instantiate(Object oldInstance, Encoder out) + { + Class oldClass = (Class) oldInstance; + + // Due to the special handling of String instances in the Encoder + // this Expression does not lead to further class resolutions. + if (oldClass == String.class) + return new Expression(oldClass, "", "getClass", null); + + // This Expression will lead to the class resolution of String.class. + if (oldClass == Class.class) + return new Expression(oldClass, String.class, "getClass", null); + + // This Expression will lead to the class resolution of Class.class. + return new Expression(oldClass, Class.class, "forName", + new Object[] { oldClass.getName() }); + } + +} diff --git a/libjava/classpath/gnu/java/beans/encoder/CollectionPersistenceDelegate.java b/libjava/classpath/gnu/java/beans/encoder/CollectionPersistenceDelegate.java new file mode 100644 index 00000000000..f1375d2ec18 --- /dev/null +++ b/libjava/classpath/gnu/java/beans/encoder/CollectionPersistenceDelegate.java @@ -0,0 +1,84 @@ +/* CollectionPersistenceDelegate.java - A PersistenceDelegate for Collection subclasses. + Copyright (C) 2005 Free Software Foundation, Inc. + + This file is part of GNU Classpath. + + GNU Classpath is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + GNU Classpath is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNU Classpath; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. + + Linking this library statically or dynamically with other modules is + making a combined work based on this library. Thus, the terms and + conditions of the GNU General Public License cover the whole + combination. + + As a special exception, the copyright holders of this library give you + permission to link this library with independent modules to produce an + executable, regardless of the license terms of these independent + modules, and to copy and distribute the resulting executable under + terms of your choice, provided that you also meet, for each linked + independent module, the terms and conditions of the license of that + module. An independent module is a module which is not derived from + or based on this library. If you modify this library, you may extend + this exception to your version of the library, but you are not + obligated to do so. If you do not wish to do so, delete this + exception statement from your version. */ + + +package gnu.java.beans.encoder; + +import java.util.Collection; +import java.beans.Encoder; +import java.beans.Expression; +import java.beans.PersistenceDelegate; +import java.beans.Statement; + +import java.util.Iterator; + +/**

A PersistenceDelegate implementation that calls + * the no-argument constructor to create the Collection instance and + * uses an iterator to add all the objects it reaches through it.

+ * + *

It is used for Set and List + * implementations.

+ * + * @author Robert Schuster (robertschuster@fsfe.org) + */ +public class CollectionPersistenceDelegate extends PersistenceDelegate +{ + + protected Expression instantiate(Object oldInstance, Encoder out) + { + return new Expression( + oldInstance, + oldInstance.getClass(), + "new", + null); + } + + protected void initialize(Class type, Object oldInstance, Object newInstance, + Encoder out) + { + Iterator ite = ((Collection) oldInstance).iterator(); + + while (ite.hasNext()) + { + out.writeStatement(new Statement(oldInstance, "add", + new Object[] { ite.next() })); + + } + + } + +} diff --git a/libjava/classpath/gnu/java/beans/encoder/Context.java b/libjava/classpath/gnu/java/beans/encoder/Context.java new file mode 100644 index 00000000000..9126d496fc5 --- /dev/null +++ b/libjava/classpath/gnu/java/beans/encoder/Context.java @@ -0,0 +1,88 @@ +/* Context.java -- Provides calling context information to ScannerStates. + Copyright (C) 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.java.beans.encoder; + +/** A Contect object describes the current state + * and the call number while processing the original object + * tree in the {@link ScanEngine}. + * + *

The class allows to distinguish the different calling states + * and is neccessary for the child element skipping feature of + * the {@link GenericScannerState}.

+ * + * @author Robert Schuster (robertschuster@fsfe.org) + * + */ +public class Context +{ + private String state; + + private int call; + + Context(String newState, int newCall) + { + state = newState; + call = newCall; + } + + public int hashCode() + { + int hc = 7; + hc = 31 * hc + state.hashCode(); + hc = 31 * hc + call; + + return hc; + } + + public boolean equals(Object o) + { + if (!(o instanceof Context)) + return false; + + Context that = (Context) o; + + return state.equals(that.state) + && call == that.call; + } + + public String toString() + { + return "Context [state=" + state + ", call=" + call + "]"; + } +} diff --git a/libjava/classpath/gnu/java/beans/encoder/GenericScannerState.java b/libjava/classpath/gnu/java/beans/encoder/GenericScannerState.java new file mode 100644 index 00000000000..3c3f8a3d2e7 --- /dev/null +++ b/libjava/classpath/gnu/java/beans/encoder/GenericScannerState.java @@ -0,0 +1,257 @@ +/* GenericScannerState.java + Copyright (C) 2005 Free Software Foundation, Inc. + + This file is part of GNU Classpath. + + GNU Classpath is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + GNU Classpath is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNU Classpath; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. + + Linking this library statically or dynamically with other modules is + making a combined work based on this library. Thus, the terms and + conditions of the GNU General Public License cover the whole + combination. + + As a special exception, the copyright holders of this library give you + permission to link this library with independent modules to produce an + executable, regardless of the license terms of these independent + modules, and to copy and distribute the resulting executable under + terms of your choice, provided that you also meet, for each linked + independent module, the terms and conditions of the license of that + module. An independent module is a module which is not derived from + or based on this library. If you modify this library, you may extend + this exception to your version of the library, but you are not + obligated to do so. If you do not wish to do so, delete this + exception statement from your version. */ + + +package gnu.java.beans.encoder; + +import java.util.HashMap; + +import gnu.java.beans.encoder.elements.ArrayInstantiation; +import gnu.java.beans.encoder.elements.Array_Get; +import gnu.java.beans.encoder.elements.Array_Set; +import gnu.java.beans.encoder.elements.ClassResolution; +import gnu.java.beans.encoder.elements.Element; +import gnu.java.beans.encoder.elements.List_Get; +import gnu.java.beans.encoder.elements.List_Set; +import gnu.java.beans.encoder.elements.MethodInvocation; +import gnu.java.beans.encoder.elements.NullObject; +import gnu.java.beans.encoder.elements.ObjectInstantiation; +import gnu.java.beans.encoder.elements.ObjectReference; +import gnu.java.beans.encoder.elements.PrimitiveInstantiation; +import gnu.java.beans.encoder.elements.StaticFieldAccess; +import gnu.java.beans.encoder.elements.StaticMethodInvocation; +import gnu.java.beans.encoder.elements.StringReference; + +/** + * This class is a {@link ScannerState} implementation that creates + * suitable {@link gnu.java.beans.encoder.elements.Element} instances + * for each transition variant. + * + *

Furthermore it can optionally skip a certain number of child + * elements. The algorithm can cope with the fact that one + * GenericScannerState instance may be called at + * different levels of recursions.

+ * + * @author Robert Schuster (robertschuster@fsfe.org) + */ +class GenericScannerState extends ScannerState +{ + private int skipElements, initialSkipElements; + + final Root root; + + HashMap skipValues; + + GenericScannerState(Root newRoot) + { + root = newRoot; + } + + GenericScannerState(Root root, int skipElements) + { + this(root); + this.skipElements = initialSkipElements = skipElements; + + if (skipElements > 0) + skipValues = new HashMap(); + } + + protected void enterImpl(Context ctx) + { + if (skipValues != null) + { + Integer skip = (Integer) skipValues.get(ctx); + + if (skip == null) + { + skip = Integer.valueOf(initialSkipElements); + skipValues.put(ctx, skip); + } + + skipElements = skip.intValue(); + } + } + + void methodInvocation(String methodName) + { + if (skipValues != null && skipElements > 0) + return; + + root.addChild(new MethodInvocation(methodName)); + } + + void staticMethodInvocation(String className, String methodName) + { + if (skipValues != null && skipElements > 0) + return; + + root.addChild(new StaticMethodInvocation(className, methodName)); + } + + void staticFieldAccess(String className, String fieldName) + { + if (skipValues != null && skipElements > 0) + return; + + root.addChild(new StaticFieldAccess(className, fieldName)); + } + + void classResolution(String className) + { + if (skipValues != null && skipElements > 0) + return; + + root.addChild(new ClassResolution(className)); + } + + void objectInstantiation(String className, ObjectId objectId) + { + if (skipValues != null && skipElements > 0) + return; + + Element elem = new ObjectInstantiation(className); + elem.initId(objectId); + + root.addChild(elem); + } + + void primitiveInstantiation(String primitiveName, String valueAsString) + { + if (skipValues != null && skipElements > 0) + return; + + root.addChild(new PrimitiveInstantiation(primitiveName, valueAsString)); + } + + void objectArrayInstantiation(String arrayClassName, String lengthAsString, + ObjectId objectId) + { + if (skipValues != null && skipElements > 0) + return; + + Element elem = new ArrayInstantiation(arrayClassName, lengthAsString); + elem.initId(objectId); + + root.addChild(elem); + } + + void primitiveArrayInstantiation(String arrayClassName, String lengthAsString, + ObjectId objectId) + { + objectArrayInstantiation(arrayClassName, lengthAsString, objectId); + } + + void arraySet(String indexAsString) + { + if (skipValues != null && skipElements > 0) + return; + + root.addChild(new Array_Set(indexAsString)); + } + + void arrayGet(String indexAsString) + { + if (skipValues != null && skipElements > 0) + return; + + root.addChild(new Array_Get(indexAsString)); + } + + void listGet() + { + if (skipValues != null && skipElements > 0) + return; + + root.addChild(new List_Get()); + } + + void listSet() + { + if (skipValues != null && skipElements > 0) + return; + + root.addChild(new List_Set()); + } + + void nullObject() + { + if (skipValues != null && skipElements > 0) + return; + + root.addChild(new NullObject()); + } + + void stringReference(String string) + { + if (skipValues != null && skipElements > 0) + return; + + root.addChild(new StringReference(string)); + } + + void objectReference(ObjectId id) + { + if (skipValues != null && skipElements > 0) + return; + + root.addChild(new ObjectReference(id)); + } + + void end() + { + if (skipValues != null) + { + if (skipElements > 0) + skipElements--; + else + { + // Finishes the Element we are constructing. + root.end(); + } + skipValues.put(context(), Integer.valueOf(skipElements)); + } + else + root.end(); + + } + + void enter() + { + + } + +} diff --git a/libjava/classpath/gnu/java/beans/encoder/IgnoringScannerState.java b/libjava/classpath/gnu/java/beans/encoder/IgnoringScannerState.java new file mode 100644 index 00000000000..054f1f0051c --- /dev/null +++ b/libjava/classpath/gnu/java/beans/encoder/IgnoringScannerState.java @@ -0,0 +1,133 @@ +/* IgnoringScannerState.java -- A ScannerState that does nothing. + Copyright (C) 2005 Free Software Foundation, Inc. + + This file is part of GNU Classpath. + + GNU Classpath is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + GNU Classpath is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNU Classpath; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. + + Linking this library statically or dynamically with other modules is + making a combined work based on this library. Thus, the terms and + conditions of the GNU General Public License cover the whole + combination. + + As a special exception, the copyright holders of this library give you + permission to link this library with independent modules to produce an + executable, regardless of the license terms of these independent + modules, and to copy and distribute the resulting executable under + terms of your choice, provided that you also meet, for each linked + independent module, the terms and conditions of the license of that + module. An independent module is a module which is not derived from + or based on this library. If you modify this library, you may extend + this exception to your version of the library, but you are not + obligated to do so. If you do not wish to do so, delete this + exception statement from your version. */ + + +package gnu.java.beans.encoder; + +/** A special {@link ScannerState} implementation that ignores all child + * elements. + * + *

Consider the call hierarchy: + * + * methodInvocation + * objectInstantiation + * classResolution* + * objectInstantiation + * classResolution + * + *

+ * + *

When the ignoring state is active one can filter the elements of + * one level. One has to set up the state machine that a transition + * via "class resolution" from a state that was reached via "object + * instantation" reaches an IgnoringScannerState.

+ * + *

Setting the default successor of a IgnoringScannerState + * to itself causes all elements of the call hierarchy to be skipped + * until another state is reached by going back.

+ * + * @author Robert Schuster (robertschuster@fsfe.org) + * + */ +class IgnoringScannerState extends ScannerState +{ + + void methodInvocation(String methodName) + { + } + + void staticMethodInvocation(String className, String methodName) + { + } + + void staticFieldAccess(String className, String fieldName) + { + } + + void classResolution(String className) + { + } + + void objectInstantiation(String className, ObjectId objectId) + { + } + + void primitiveInstantiation(String primitiveName, String valueAsString) + { + } + + void objectArrayInstantiation(String arrayClassName, String lengthAsString, ObjectId objectId) + { + } + + void primitiveArrayInstantiation(String arrayClassName, String lengthAsString, ObjectId objectId) + { + } + + void arraySet(String indexAsString) + { + } + + void arrayGet(String indexAsString) + { + } + + void listGet() + { + } + + void listSet() + { + } + + void nullObject() + { + } + + void stringReference(String string) + { + } + + void objectReference(ObjectId id) + { + } + + void end() + { + } + +} diff --git a/libjava/classpath/gnu/java/beans/encoder/MapPersistenceDelegate.java b/libjava/classpath/gnu/java/beans/encoder/MapPersistenceDelegate.java new file mode 100644 index 00000000000..84cdce94dbe --- /dev/null +++ b/libjava/classpath/gnu/java/beans/encoder/MapPersistenceDelegate.java @@ -0,0 +1,81 @@ +/* MapPersistenceDelegate.java -- A PersistenceDelegate for Map subclasses. + + Copyright (C) 2005 Free Software Foundation, Inc. + + This file is part of GNU Classpath. + + GNU Classpath is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + GNU Classpath is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNU Classpath; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. + + Linking this library statically or dynamically with other modules is + making a combined work based on this library. Thus, the terms and + conditions of the GNU General Public License cover the whole + combination. + + As a special exception, the copyright holders of this library give you + permission to link this library with independent modules to produce an + executable, regardless of the license terms of these independent + modules, and to copy and distribute the resulting executable under + terms of your choice, provided that you also meet, for each linked + independent module, the terms and conditions of the license of that + module. An independent module is a module which is not derived from + or based on this library. If you modify this library, you may extend + this exception to your version of the library, but you are not + obligated to do so. If you do not wish to do so, delete this + exception statement from your version. */ + + +package gnu.java.beans.encoder; + +import java.util.Map; +import java.beans.Encoder; +import java.beans.Expression; +import java.beans.PersistenceDelegate; +import java.beans.Statement; + +import java.util.Iterator; + +/** + * @author Robert Schuster (robertschuster@fsfe.org) + */ +public class MapPersistenceDelegate extends PersistenceDelegate +{ + + protected Expression instantiate(Object oldInstance, Encoder out) + { + return new Expression( + oldInstance, + oldInstance.getClass(), + "new", + null); + } + + protected void initialize(Class type, Object oldInstance, Object newInstance, + Encoder out) + { + Map map = (Map) oldInstance; + Iterator ite = map.keySet().iterator(); + + while (ite.hasNext()) + { + Object key = ite.next(); + out.writeStatement(new Statement(oldInstance, "put", + new Object[] { key, map.get(key) })); + + } + + } + +} diff --git a/libjava/classpath/gnu/java/beans/encoder/ObjectId.java b/libjava/classpath/gnu/java/beans/encoder/ObjectId.java new file mode 100644 index 00000000000..eca5c3da477 --- /dev/null +++ b/libjava/classpath/gnu/java/beans/encoder/ObjectId.java @@ -0,0 +1,132 @@ +/* ObjectId.java -- Simple object identification mechanism for XML encoding. + Copyright (C) 2005 Free Software Foundation, Inc. + + This file is part of GNU Classpath. + + GNU Classpath is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + GNU Classpath is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNU Classpath; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. + + Linking this library statically or dynamically with other modules is + making a combined work based on this library. Thus, the terms and + conditions of the GNU General Public License cover the whole + combination. + + As a special exception, the copyright holders of this library give you + permission to link this library with independent modules to produce an + executable, regardless of the license terms of these independent + modules, and to copy and distribute the resulting executable under + terms of your choice, provided that you also meet, for each linked + independent module, the terms and conditions of the license of that + module. An independent module is a module which is not derived from + or based on this library. If you modify this library, you may extend + this exception to your version of the library, but you are not + obligated to do so. If you do not wish to do so, delete this + exception statement from your version. */ + + +package gnu.java.beans.encoder; + +import java.util.HashMap; + +/** + *

+ * ObjectId provides an object identification mechanism which gives each object + * a name in the form <class><Nameindex>. + *

+ * + *

+ * Each id can be in an unused state which means that only one instance of the + * object is in use and a special id is not needed. Certain {@link + * gnu.java.beans.encoder.elements.Element} subclasses use this feature to find + * out whether they write the "id" attribute or not. + *

+ *

+ * An ObjectId instance is typically given to multiple objects. + * The second user should then invoke the {@link #init} method to generate the + * identification string and bring the id in the 'used' state. + *

+ * + * @author Robert Schuster (robertschuster@fsfe.org) + */ +public class ObjectId +{ + /** + * Stores the index an object of a specific type should be given. + */ + private static HashMap nameIndices = new HashMap(); + + private String id; + + private Class klass; + + ObjectId(Class klass) + { + this.klass = klass; + } + + public boolean isUnused() + { + return id == null; + } + + public String toString() + { + return (id != null) ? id : ""; + } + + /** + *

+ * Generates a simple Id by concatenating a class name with a self-increasing + * number. + *

+ */ + public void init() + { + assert (klass != null); + + if (id != null) + return; + + Integer count = (Integer) nameIndices.get(klass); + if (count == null) + { + count = Integer.valueOf(0); + } + + if (klass.isArray()) + { + Class ct = klass.getComponentType(); + if (ct == Boolean.TYPE) + id = "booleanArray" + count.intValue(); + else if (ct == Byte.TYPE) + id = "byteArray" + count.intValue(); + else if (ct == Short.TYPE) + id = "shortArray" + count.intValue(); + else if (ct == Integer.TYPE) + id = "intArray" + count.intValue(); + else if (ct == Long.TYPE) + id = "longArray" + count.intValue(); + else if (ct == Float.TYPE) + id = "floatArray" + count.intValue(); + else if (ct == Double.TYPE) + id = "doubleArray" + count.intValue(); + } + else + id = klass.getName() + count.intValue(); + + nameIndices.put(klass, Integer.valueOf(count.intValue() + 1)); + } + +} diff --git a/libjava/classpath/gnu/java/beans/encoder/PrimitivePersistenceDelegate.java b/libjava/classpath/gnu/java/beans/encoder/PrimitivePersistenceDelegate.java new file mode 100644 index 00000000000..8cb3705b4d8 --- /dev/null +++ b/libjava/classpath/gnu/java/beans/encoder/PrimitivePersistenceDelegate.java @@ -0,0 +1,74 @@ +/* PrimitivePersistenceDelegate.java + -- A PersistenceDelegate for primitive data types. + Copyright (C) 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.java.beans.encoder; + +import java.beans.Encoder; +import java.beans.Expression; +import java.beans.PersistenceDelegate; + +/** + * A shared PersistenceDelegate implementation for all primitive types. + * + * @author Robert Schuster (robertschuster@fsfe.org) + */ +public class PrimitivePersistenceDelegate extends PersistenceDelegate +{ + + protected Expression instantiate(Object oldInstance, Encoder out) + { + // The implementation relies on the fact that every primitive + // wrapper class has a constructor accepting a String argument. + // By using these constructors creating a primitive instance + // depends on the String class only. + return new Expression(oldInstance, oldInstance.getClass(), "new", + new Object[] { oldInstance.toString() }); + } + + protected void initialize(Class type, Object oldInstance, Object newInstance, Encoder out) + { + // This is a hack to make serializing primitive arrays work correctly. + // Instead of modifying an existing primitive instance to make it equal + // with another instance (which is not possible because primitives are + // immutable) we create a new instance. This is against the specification + // of the initialize method but make things work fine. + out.writeExpression(new Expression(oldInstance, oldInstance.getClass(), "new", + new Object[] { oldInstance.toString() })); + } + +} diff --git a/libjava/classpath/gnu/java/beans/encoder/ReportingScannerState.java b/libjava/classpath/gnu/java/beans/encoder/ReportingScannerState.java new file mode 100644 index 00000000000..fb6e061ae4d --- /dev/null +++ b/libjava/classpath/gnu/java/beans/encoder/ReportingScannerState.java @@ -0,0 +1,131 @@ +/* ReportingScannerState.java -- A state for debugging purposes. + Copyright (C) 2005 Free Software Foundation, Inc. + + This file is part of GNU Classpath. + + GNU Classpath is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + GNU Classpath is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNU Classpath; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. + + Linking this library statically or dynamically with other modules is + making a combined work based on this library. Thus, the terms and + conditions of the GNU General Public License cover the whole + combination. + + As a special exception, the copyright holders of this library give you + permission to link this library with independent modules to produce an + executable, regardless of the license terms of these independent + modules, and to copy and distribute the resulting executable under + terms of your choice, provided that you also meet, for each linked + independent module, the terms and conditions of the license of that + module. An independent module is a module which is not derived from + or based on this library. If you modify this library, you may extend + this exception to your version of the library, but you are not + obligated to do so. If you do not wish to do so, delete this + exception statement from your version. */ + + +package gnu.java.beans.encoder; + +/** + * A ScannerState implementation that prints useful details + * about its arguments. Use it when the XML encoding does not work correctly + * and you want to find out how things relate to each other. + * + * @author Robert Schuster (robertschuster@fsfe.org) + */ +class ReportingScannerState extends ScannerState +{ + + void methodInvocation(String methodName) + { + System.out.println("methodInvocation: " + methodName + "()"); + } + + void staticMethodInvocation(String className, String methodName) + { + System.out.println("staticMethodInvocation: " + className + "." + methodName + "()"); + } + + void staticFieldAccess(String className, String fieldName) + { + System.out.println("staticFieldAccess: " + className + "." + fieldName); + } + + void classResolution(String className) + { + System.out.println("classResolution: " + className); + } + + void objectInstantiation(String className, ObjectId objectId) + { + System.out.println("objectInstantiation: " + className); + } + + void primitiveInstantiation(String primitiveName, String valueAsString) + { + System.out.println("primitiveInstantiation: (" + primitiveName + ") " + valueAsString); + } + + void objectArrayInstantiation(String arrayClassName, String lengthAsString, ObjectId objectId) + { + System.out.println("objectArrayInstantiation: new " + arrayClassName + "[" + lengthAsString + "]"); + } + + void primitiveArrayInstantiation(String arrayClassName, String lengthAsString, ObjectId objectId) + { + System.out.println("primitiveArrayInstantiation: new " + arrayClassName + "[" + lengthAsString + "]"); + } + + void arraySet(String indexAsString) + { + System.out.println("arraySet: " + indexAsString); + } + + void arrayGet(String indexAsString) + { + System.out.println("arrayGet: " + indexAsString); + } + + void listGet() + { + System.out.println("listGet"); + } + + void listSet() + { + System.out.println("listSet"); + } + + void nullObject() + { + System.out.println("nullObject"); + } + + void stringReference(String string) + { + System.out.println("stringReference: " + string); + } + + void objectReference(ObjectId id) + { + System.out.println("objectReference: " + id); + } + + void end() + { + System.out.println("-close"); + } + +} diff --git a/libjava/classpath/gnu/java/beans/encoder/Root.java b/libjava/classpath/gnu/java/beans/encoder/Root.java new file mode 100644 index 00000000000..f4eade1939d --- /dev/null +++ b/libjava/classpath/gnu/java/beans/encoder/Root.java @@ -0,0 +1,198 @@ +/* Root.java -- The root of an object tree. + Copyright (C) 2005 Free Software Foundation, Inc. + + This file is part of GNU Classpath. + + GNU Classpath is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + GNU Classpath is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNU Classpath; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. + + Linking this library statically or dynamically with other modules is + making a combined work based on this library. Thus, the terms and + conditions of the GNU General Public License cover the whole + combination. + + As a special exception, the copyright holders of this library give you + permission to link this library with independent modules to produce an + executable, regardless of the license terms of these independent + modules, and to copy and distribute the resulting executable under + terms of your choice, provided that you also meet, for each linked + independent module, the terms and conditions of the license of that + module. An independent module is a module which is not derived from + or based on this library. If you modify this library, you may extend + this exception to your version of the library, but you are not + obligated to do so. If you do not wish to do so, delete this + exception statement from your version. */ + + +package gnu.java.beans.encoder; + +import java.beans.XMLEncoder; +import java.util.Iterator; +import java.util.Stack; + +import gnu.java.beans.encoder.elements.Element; + +/**

Root provides a simple interface to a tree of + * objects.

+ * + *

Using an instance of this class a logical representation of + * the real object tree that is serialized can be built. When the + * actual data should be written as XML Root and + * {@link gnu.java.beans.encoder.elements.Element} class can provide + * context information which is used to write the best fitting + * XML representation.

+ * + * @author Robert Schuster (robertschuster@fsfe.org) + */ +public class Root +{ + private Stack parents = new Stack(); + + private Element rootElement, current; + + private boolean started; + + public Root() + { + rootElement = current = new RootElement(); + } + + /**

Adds another child element to the tree.

+ * + *

The new element automatically becomes the current + * element.

+ * + * @param elem The new child element. + */ + public void addChild(Element elem) + { + current.addChild(elem); + + parents.push(current); + current = elem; + } + + /** + *

Marks that the end of the current element + * is reached and that no more childs are added to + * it.

+ * + *

The behavior is to return to the nearest parent + * element.

+ */ + public void end() + { + current = (Element) parents.pop(); + } + + /** + *

Goes back to the nearest parent element but + * deletes the just created child.

+ * + *

This is used if something went wrong while + * processing the child element's {@link java.beans.Expression} + * or {@link java.beans.Statement}.

+ * + */ + public void deleteLast() + { + current = (Element) parents.pop(); + + current.removeLast(); + } + + /** + *

Traverses the elements in the object tree + * and creates their XML representation in the output + * stream of the given {@link Writer}.

+ * + *

Finally the Writer is flushed.

+ * + * @param writer The Writer instance that generates the XML representation. + */ + public void traverse(Writer writer) + { + if (!started) + { + writer.writePreamble(); + rootElement.writeStart(writer); + } + started = true; + + traverse(writer, rootElement.iterator()); + + rootElement.clear(); + + writer.flush(); + } + + /** Writes the closing element and closes the {@link Writer} + * + * @param writer The Writer instance that generates the XML representation. + */ + public void close(Writer writer) + { + rootElement.writeEnd(writer); + writer.close(); + } + + /** Recursively traverses the object tree. + * + * @param writer The Writer instance that generates the XML representation. + * @param ite An Iterator returning Element instances. + */ + private void traverse(Writer writer, Iterator ite) + { + while (ite.hasNext()) + { + Element e = (Element) ite.next(); + e.writeStart(writer); + + traverse(writer, e.iterator()); + + e.writeEnd(writer); + + e.clear(); + } + } + + /**

A special Element implementation that represents the + * encoder's context.

+ * + *

This element is written only once per Writer.

+ * + *

It is assumed that this element is never empty to simplify + * the implementation.

+ * + * @author Robert Schuster (robertschuster@fsfe.org); + * + */ + static class RootElement extends Element + { + public void writeStart(Writer writer) + { + writer.write("java", new String[] { "version", "class" }, + new String[] { System.getProperty("java.version"), + XMLEncoder.class.getName() }, false); + } + + public void writeEnd(Writer writer) + { + writer.writeEnd(false); + } + + } + +} diff --git a/libjava/classpath/gnu/java/beans/encoder/ScanEngine.java b/libjava/classpath/gnu/java/beans/encoder/ScanEngine.java new file mode 100644 index 00000000000..edf07d760d3 --- /dev/null +++ b/libjava/classpath/gnu/java/beans/encoder/ScanEngine.java @@ -0,0 +1,860 @@ +/* ScanEngine.java + -- Scans the input and generates an object tree that can be written as XML. + Copyright (C) 2005 Free Software Foundation, Inc. + + This file is part of GNU Classpath. + + GNU Classpath is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + GNU Classpath is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNU Classpath; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. + + Linking this library statically or dynamically with other modules is + making a combined work based on this library. Thus, the terms and + conditions of the GNU General Public License cover the whole + combination. + + As a special exception, the copyright holders of this library give you + permission to link this library with independent modules to produce an + executable, regardless of the license terms of these independent + modules, and to copy and distribute the resulting executable under + terms of your choice, provided that you also meet, for each linked + independent module, the terms and conditions of the license of that + module. An independent module is a module which is not derived from + or based on this library. If you modify this library, you may extend + this exception to your version of the library, but you are not + obligated to do so. If you do not wish to do so, delete this + exception statement from your version. */ + + +package gnu.java.beans.encoder; + +import java.beans.Expression; +import java.beans.Statement; +import java.io.OutputStream; +import java.lang.reflect.Array; +import java.util.HashMap; +import java.util.IdentityHashMap; +import java.util.List; +import java.util.Stack; + +/**

The ScanEngine is the main class of the backend of the + * XML persistence algorithm. It scans {@link java.beans.Expression} and + * {@link java.beans.Statement} instances and some raw objects via the + * {@link #writeObject} method and feeds it to a state machine. The + * state machine then constructs and object tree which is finally + * written as XML by a {@link Writer} implementation.

+ * + *

How does it work?

+ *

The ScanEngine sits below the {@link java.beans.XMLEncoder} + * class and is called by it exclusively. The XMLEncoder sends + * interpretive data by invoking {@link #writeExpression}, {@link #writeStatement} + * and {@link #writeObject}. The invocations of writeExpression and + * writeStatement are usually nested into each other and provide + * more information then necessary to generate the XML representation. + * Furthermore the meaning of certain Expressions differs + * depending on the enclosing elements or the inner elements have to be + * simply discarded.

+ * + *

To cope with this state dependant nature the ScanEngine + * contains a state machine which is programmed statically (no adjustments are + * needed, all ScanEngine engines use the same setup). The + * ScanEngine's job is to decode the Expressions, + * Statements and certain objects (namely String, + * null objects and instances which are repeatedly provided to + * the encoder) into 13 low-level (event) methods, which denote the meaning of the + * argument. For example an Expression can be an array + * instantiation which provokes a call to {@link arrayInstantiation} or + * it can be a class resolution leading to a call to {@link #classResolution}. + * For the state machione the 13 methods are the distinct way to transit + * from one state to another. Whenever the ScanEngine calls + * one of the event methods the current's state successor for that event + * is fetched from the state machine configuration, the successpr becomes + * the current state and then the event method is called in the new current + * state. The last step allows the state instance to do something meaningful + * to the object tree.

+ * + *

The state machine knows the concept of returning to the previous + * state. This is done using a stack of states which is popped every + * time a call to writeStatement, writeExpression + * in the XMLEncoder ends by calling the {@link #end} method. + * Note that due to the inheritance relationship of Encoder + * and XMLEncoder it is impossible for the + * ScanEngine itself to decide when an expression or statement + * ended. This can only be done in case of {@link #writeObject} calls because + * they are not nested.

+ * + *

When the XML persistence mechanism reaches an object twice (and more) + * it should generate an XML element using the "idref" attribute and add + * an "id" attribute to its first instantiation. This complicates things a bit + * because the first instantiation will always be part of the object tree + * as some {@link gnu.java.beans.encoder.elements.Element} subclass instance when the + * second and further objects accesses are written. Therefore the {@link ObjectId} + * class was introduced which is shared between all the object tree elements + * and has the notion of an "unused" state meaning that no identification + * is needed. The relationship between an object and its ObjectId + * instance is stored in the ScanEngine and gets cleared whenever + * the {@link #flush} method is called. This method also writes the currently + * built object tree and generates the XML representation.

+ * + * @author Robert Schuster (robertschuster@fsfe.org) + */ +public class ScanEngine +{ + + /** Change this to true to let the ScanEngine print state transition + * information. + */ + boolean DEBUG = false; + + /** + * Stores the scanner engine states as values and their names as keys. + */ + HashMap states = new HashMap(); + + /** + * Stores former scanner state and makes it possible to come back to them. + */ + Stack parents = new Stack(); + + /** + * The currently active scanner state. + */ + ScannerState current; + + /** + * The root of an object tree that is later written to XML. + */ + Root root; + + /** + * The Writer used to generate the XML output. + */ + Writer writer; + + /** Stores the relationship between objects and their {@link ObjectId} instance. + */ + IdentityHashMap objects = new IdentityHashMap(); + + public ScanEngine(OutputStream os) + { + // TODO: Provide another Writer implementation (e.g. one that does not use + // the XML APIs at all). + writer = new StAXWriter(os); + root = new Root(); + + final ScannerState start = current = new GenericScannerState(root);; + ScannerState conf; + + // Use the ReportingScannerState to debug serialization issues. + register(ScannerState.DEFAULT_STATE_NAME, new IgnoringScannerState()); + + register("start", start); + + // Special dead-end state where all transitions are ignored. + register("ignoreAll", new IgnoringScannerState()) + .setDefaultSuccessor("ignoreAll"); + + // Object reference, string reference, null object + start.putSuccessor(ScannerState.TRANSITION_OBJECT_REFERENCE, "simple"); + start.putSuccessor(ScannerState.TRANSITION_STRING_REFERENCE, "simple"); + start.putSuccessor(ScannerState.TRANSITION_NULL_OBJECT, "simple"); + register("simple", new GenericScannerState(root)) + .setDefaultSuccessor("ignoreAll"); + + // Class resolution. + start.putSuccessor(ScannerState.TRANSITION_CLASS_RESOLUTION, "classRes0"); + register("classRes0", + new GenericScannerState(root)).setDefaultSuccessor("ignoreAll"); + + // Object instantiation. + start.putSuccessor(ScannerState.TRANSITION_OBJECT_INSTANTIATION, + "newObj0"); + conf = register("newObj0", new GenericScannerState(root)); + conf.setDefaultSuccessor("ignoreAll"); + + // Simply use the start state to encode method invocations inside of + // objects. + conf.putSuccessor(ScannerState.TRANSITION_METHOD_INVOCATION, "start"); + + // Primitive instantiations. + start.putSuccessor(ScannerState.TRANSITION_PRIMITIVE_INSTANTIATION, + "newPrimitive0"); + register("newPrimitive0", + new GenericScannerState(root)).setDefaultSuccessor("ignoreAll"); + + // Object arrays use the ARRAY_GET transition to create setting the + // array values. + start.putSuccessor(ScannerState.TRANSITION_OBJECT_ARRAY_INSTANTIATION, + "newObjectArray"); + conf = register("newObjectArray", new GenericScannerState(root)); + conf.putSuccessor(ScannerState.TRANSITION_ARRAY_GET, "newOArrayGet"); + conf.putSuccessor(ScannerState.TRANSITION_ARRAY_SET, "ignoreAll"); + conf.putSuccessor(ScannerState.TRANSITION_CLASS_RESOLUTION, "ignoreAll"); + conf.putSuccessor(ScannerState.TRANSITION_PRIMITIVE_INSTANTIATION, + "ignoreAll"); + + // Get here when a value is set in the array. + register("newOArrayGet", + conf = new GenericScannerState(root)); + + conf.putSuccessor(ScannerState.TRANSITION_PRIMITIVE_INSTANTIATION, + "newOArrayGet_ignoreFirstInteger"); + + // "newArrayGet_ignoreFirstInteger" is set up mostly identical like the "start" + // state. Otherwise things would not behave the same when done inside + // arrays. + conf.putSuccessor(ScannerState.TRANSITION_OBJECT_REFERENCE, "simple"); + conf.putSuccessor(ScannerState.TRANSITION_STRING_REFERENCE, "simple"); + conf.putSuccessor(ScannerState.TRANSITION_NULL_OBJECT, "simple"); + conf.putSuccessor(ScannerState.TRANSITION_CLASS_RESOLUTION, "classRes0"); + conf.putSuccessor(ScannerState.TRANSITION_OBJECT_INSTANTIATION, "newObj0"); + conf.putSuccessor(ScannerState.TRANSITION_PRIMITIVE_ARRAY_INSTANTIATION, + "newPrimitiveArray"); + conf.putSuccessor(ScannerState.TRANSITION_OBJECT_ARRAY_INSTANTIATION, + "newObjectArray"); + + conf = register("newOArrayGet_ignoreFirstInteger", + new GenericScannerState(root, 1)); + + // In non-int primitive arrays class resolutions can happen + // but they should be ignored. + conf.putSuccessor(ScannerState.TRANSITION_CLASS_RESOLUTION, "ignoreAll"); + + // Spurious object and string references occur when setting array + // elements. This suppresses them. + conf.putSuccessor(ScannerState.TRANSITION_PRIMITIVE_INSTANTIATION, + "ignoreAll"); + conf.putSuccessor(ScannerState.TRANSITION_OBJECT_REFERENCE, "ignoreAll"); + conf.putSuccessor(ScannerState.TRANSITION_STRING_REFERENCE, "ignoreAll"); + + conf.setDefaultSuccessor("start"); + + // Primitive arrays use the ARRAY_SET transition to create setting the + // array values. This turned out to be the only working solution. + // When primitive arrays were handled by ARRAY_GET the values in boolean + // arrays were always skipped. + start.putSuccessor(ScannerState.TRANSITION_PRIMITIVE_ARRAY_INSTANTIATION, + "newPrimitiveArray"); + conf = register("newPrimitiveArray", new GenericScannerState(root)); + conf.putSuccessor(ScannerState.TRANSITION_ARRAY_GET, "ignoreAll"); + conf.putSuccessor(ScannerState.TRANSITION_ARRAY_SET, "newPArraySet"); + conf.putSuccessor(ScannerState.TRANSITION_CLASS_RESOLUTION, "ignoreAll"); + conf.putSuccessor(ScannerState.TRANSITION_PRIMITIVE_INSTANTIATION, + "ignoreAll"); + + conf = register("newPArraySet", new GenericScannerState(root)); + conf.putSuccessor(ScannerState.TRANSITION_PRIMITIVE_INSTANTIATION, + "newPArraySet_ignoreFirstInteger"); + + // Primitive arrays ignore all kinds of non-primitive object information. + conf.putSuccessor(ScannerState.TRANSITION_OBJECT_REFERENCE, + "ignoreAll"); + conf.putSuccessor(ScannerState.TRANSITION_STRING_REFERENCE, "ignoreAll"); + conf.putSuccessor(ScannerState.TRANSITION_NULL_OBJECT, "ignoreAll"); + conf.putSuccessor(ScannerState.TRANSITION_CLASS_RESOLUTION, "ingoreAll"); + conf.putSuccessor(ScannerState.TRANSITION_OBJECT_INSTANTIATION, "ignoreAll"); + conf.putSuccessor(ScannerState.TRANSITION_PRIMITIVE_ARRAY_INSTANTIATION, + "ignoreAll"); + conf.putSuccessor(ScannerState.TRANSITION_OBJECT_ARRAY_INSTANTIATION, + "ignoreAll"); + + conf = register("newPArraySet_ignoreFirstInteger", + new GenericScannerState(root, 1)); + + // In non-int primitive arrays class resolutions can happen + // but they should be ignored. + conf.putSuccessor(ScannerState.TRANSITION_CLASS_RESOLUTION, "ignoreAll"); + + // Spurious object and string references occur when setting array + // elements. This suppresses them. + conf.putSuccessor(ScannerState.TRANSITION_PRIMITIVE_INSTANTIATION, + "ignoreAll"); + conf.putSuccessor(ScannerState.TRANSITION_OBJECT_REFERENCE, "ignoreAll"); + conf.putSuccessor(ScannerState.TRANSITION_STRING_REFERENCE, "ignoreAll"); + conf.setDefaultSuccessor("start"); + + } + + /** Registers a ScannerState under a certain name. + * + * @param name Name of the state + * @param state The ScannerState instance. + * @return The second argument. + */ + private ScannerState register(String name, ScannerState state) + { + state.init(name); + + states.put(name, state); + + return state; + } + + /** Generates or returns an id for the given object which can be activated + * later if the object is suitable. + * + *

Objects are unsuitable if they are an instance of a primitive wrapper + * or String.

+ * + * @param value The object to retrieve an id for. + * @return The id for the object or null. + */ + private ObjectId retrieveId(Object value) + { + Class valueClass = value.getClass(); + ObjectId id = null; + + // Although multiple accesses to Class objects are not handled + // through ids we generate one for them, too. This allows us to detect + // second time references to such objects in the writeObject method + // and handle them in a special way. + if (valueClass != String.class + && valueClass.getSuperclass() != Number.class + && valueClass != Boolean.class) + { + if ((id = (ObjectId) objects.get(value)) == null) + { + id = new ObjectId(valueClass); + objects.put(value, id); + } + } + + return id; + } + + /** Scans the argument and calls one of event methods. See + * the introduction of this class for details. + * + * @param expr The expression to serialize. + */ + public void writeExpression(Expression expr) + { + String methodName = expr.getMethodName(); + Object[] args = expr.getArguments(); + Object target = expr.getTarget(); + Object value = null; + + try + { + value = expr.getValue(); + } + catch (Exception e) + { + throw (InternalError) + new InternalError( + "The Expression's value should be available at this point.") + .initCause(e); + } + + // TODO: What if the value is null? + ObjectId id; + Class valueClass = value.getClass(); + + if (target == Array.class) + { + if (methodName.equals("newInstance")) + { + id = retrieveId(value); + + Class ct = (Class) args[0]; + + if (ct.isPrimitive() || ct == Boolean.class || ct == Byte.class + || ct == Short.class || ct == Integer.class || ct == Long.class + || ct == Float.class || ct == Double.class) + primitiveArrayInstantiation(ct.getName(), + args[1].toString(), + id); + else + objectArrayInstantiation(ct.getName(), + args[1].toString(), + id); + + return; + } + else if (methodName.equals("get")) + { + arrayGet(args[1].toString()); + + // The encoder does not call the ScanEngine + // when an object is serialized that we already know. + // We test for this situation and insert the object reference + // manually. + // Since there is already a workaround for the Class class + // in writeObject we have to except it from this behavior. + id = (ObjectId) objects.get(value); + if (id != null && valueClass != Class.class) + { + objectReference(id); + end(); + } + + return; + } + else if (methodName.equals("set")) + { + arraySet(args[1].toString()); + return; + } + } + + id = retrieveId(value); + + if (target instanceof Class) + { + if (methodName.equals("new")) + { + Class targetClass = (Class) target; + + // All primitive types have short-hand forms for their + // constructors. + if (valueClass == Boolean.class) + primitiveInstantiation("boolean", args[0].toString()); + else if (valueClass == Byte.class) + primitiveInstantiation("byte", args[0].toString()); + else if (valueClass == Short.class) + primitiveInstantiation("short", args[0].toString()); + else if (valueClass == Integer.class) + primitiveInstantiation("int", args[0].toString()); + else if (valueClass == Long.class) + primitiveInstantiation("long", args[0].toString()); + else if (valueClass == Float.class) + primitiveInstantiation("float", args[0].toString()); + else if (valueClass == Double.class) + primitiveInstantiation("double", args[0].toString()); + else + objectInstantiation(targetClass.getName(), id); + + return; + } + else if (value instanceof Class) + { + String className = ((Class) value).getName(); + + // At this point we know that some *static* method will be called. + + if (methodName.equals("forName")) + { + // However "Class.forName" represents class resolution and has a + // special syntax. + classResolution(className); + return; + } + else if (methodName.equals("getField")) + { + // The same goes for "Class.getField". + // Note: The name of the wanted field is given in + // the argument array. + staticFieldAccess(className, args[0].toString()); + return; + } + else + { + // If nothing fits it is just a static method + // invocation which we decode as such. + staticMethodInvocation(className, methodName); + return; + } + } + } + else if (target instanceof List) + { + // Special behavior for indexed get and set method for list-style + // classes. + // The arguments are in the args array but we need them as subelements. + if (methodName.equals("get")) + { + listGet(); + return; + } + else if (methodName.equals("set")) + { + listSet(); + return; + } + } + + // If nothing else could be used then this is a normal + // method invocation. + methodInvocation(methodName); + } + + /** + * Ends the current state and returns to the last one. + */ + public void end() + { + current.end(); + + if (DEBUG) System.err.print("back from " + current.getName()); + + ScannerState oldCurrent = current; + current = (ScannerState) parents.pop(); + + if (DEBUG) System.err.println(" to " + current.getName()); + } + + /** + * Returns to the last state and deletes the last element in the object tree. + */ + public void revoke() + { + ScannerState oldCurrent = current; + current = (ScannerState) parents.pop(); + + root.deleteLast(); + } + + /** Scans the argument and calls one of event methods. See + * the introduction of this class for details. + * + * @param stmt The statement to serialize. + */ + public void writeStatement(Statement stmt) + { + // This is a simplified version of writeExpression. Everything + // that would not create something that is embedded in a tag + // is left out (instantiation, getters, ...). + // TODO: Is this the right thing to do? + + String methodName = stmt.getMethodName(); + Object target = stmt.getTarget(); + Object[] args = stmt.getArguments(); + + if (target == Array.class && methodName.equals("set")) + { + arraySet(args[1].toString()); + return; + } + + if (target instanceof List) + { + if (methodName.equals("set")) + { + listSet(); + return; + } + } + + // If nothing else could be used then this is a normal + // method invocation. + methodInvocation(methodName); + } + + /** Scans the argument and calls one of event methods. See + * the introduction of this class for details. + * + * @param o The object to serialize. + */ + public boolean writeObject(Object o) + { + ObjectId id = null; + + if (o == null) + { + // Handle null objects which have a special syntax. + nullObject(); + end(); + } + else if (o.getClass() == String.class) + { + // Handle strings which are treated extremely special + // in the encoder (they are never converted into a + // Expression). + stringReference((String) o); + end(); + } + else if ((id = (ObjectId) objects.get(o)) != null) + { + // Multiple references to a Class object do not generate + // an object reference but we use the id to detect that + // situation. + if (o.getClass() == Class.class) + { + classResolution(((Class) o).getName()); + end(); + return false; + } + + // If our object has a corresponding ObjectId instance + // then generate an objectReference. This will + // initialize the id (= brings it in the "used" state) + // when this is the first referal. + objectReference(id); + end(); + return false; + } + + return true; + } + + /** + * Writes the currently constructed object tree out as + * XML and clears the object to {@link ObjectId} relations. + */ + public void flush() + { + // Make all references unreachable. That means we have to generate + // new object ids. + objects.clear(); + + root.traverse(writer); + } + + /** Writes the final bits if the object tree and closes the stream + * afterwards. + */ + public void close() + { + flush(); + root.close(writer); + } + + /** + * Does a transition from one state to another using the given event. + * + *

This involves saving the current state, retrieving it's + * successor and setting it as the current state.

+ * + * @param transition One of {@link ScannerStates]'s transition constants. + */ + private void transition(int transition) + { + parents.push(current); + + String stateName = current.getSuccessor(transition); + + if (DEBUG) + { + System.err.println("from state: " + current.getName() + "\n\troute: " + + ScannerState.transitionNames[transition] + + "\n\t\tto state: " + + stateName); + } + + ScannerState newState = (ScannerState) states.get(stateName); + + newState.enter(new Context(current.getName(), current.getCalls())); + + assert (newState != null) : "State '" + stateName + "' was not defined."; + + current = newState; + } + + /** Event method that denotes a (non-static) method invocation. + * + *

More details about this method can be found in this + * class' introduction.

+ * + * @param methodName The name of the method which is called. + */ + void methodInvocation(String methodName) + { + transition(ScannerState.TRANSITION_METHOD_INVOCATION); + + current.methodInvocation(methodName); + } + + /** Event method that denotes a static method invocation. + * + *

More details about this method can be found in this + * class' introduction.

+ * + * @param methodName The name of the method which is called. + * @param className The name of the class in which the method is called. + */ + void staticMethodInvocation(String className, String methodName) + { + transition(ScannerState.TRANSITION_STATIC_METHOD_INVOCATION); + + current.staticMethodInvocation(className, methodName); + } + + /** Event method that denotes the retrieval of a static field's value. + * + *

More details about this method can be found in this + * class' introduction.

+ * + * @param fieldName The name of the field whose value is retrieved. + * @param className The name of the class in which the method is called. + */ + void staticFieldAccess(String className, String fieldName) + { + transition(ScannerState.TRANSITION_STATIC_FIELD_ACCESS); + + current.staticFieldAccess(className, fieldName); + } + + /** Event method that denotes the resolution of a class. + * + *

More details about this method can be found in this + * class' introduction.

+ * + * @param className The name of the class in which the method is called. + */ + void classResolution(String className) + { + transition(ScannerState.TRANSITION_CLASS_RESOLUTION); + + current.classResolution(className); + } + + /** Event method that denotes the instantiation of an object. + * + *

More details about this method can be found in this + * class' introduction.

+ * + * @param className The name of the class in which the method is called. + * @param objectId An ObjectId instance which can be activated later. + */ + void objectInstantiation(String className, ObjectId objectId) + { + transition(ScannerState.TRANSITION_OBJECT_INSTANTIATION); + + current.objectInstantiation(className, objectId); + } + + /** Event method that denotes the instantiation of a primitive. + * + *

More details about this method can be found in this + * class' introduction.

+ * + * @param primitiveName One of "boolean, "byte", "short", "int", "long" + * , "float" or "double" + * @param valueAsString The value of the primitive as a String. + */ + void primitiveInstantiation(String primitiveName, String valueAsString) + { + transition(ScannerState.TRANSITION_PRIMITIVE_INSTANTIATION); + + current.primitiveInstantiation(primitiveName, valueAsString); + } + + /** Event method that denotes the instantiation of an object array. + * + *

More details about this method can be found in this + * class' introduction.

+ * + * @param arrayClassName The array's class name. + * @param objectId An ObjectId instance which can be activated later. + * @param lengthAsString The array's length as String. + */ + void objectArrayInstantiation(String arrayClassName, String lengthAsString, + ObjectId objectId) + { + transition(ScannerState.TRANSITION_OBJECT_ARRAY_INSTANTIATION); + + current.objectArrayInstantiation(arrayClassName, lengthAsString, objectId); + } + + /** Event method that denotes the instantiation of a primitive array. + * + *

More details about this method can be found in this + * class' introduction.

+ * + * @param arrayClassName The array's class name. + * @param objectId An ObjectId instance which can be activated later. + * @param lengthAsString The array's length as String. + */ + void primitiveArrayInstantiation(String arrayClassName, String lengthAsString, + ObjectId objectId) + { + transition(ScannerState.TRANSITION_PRIMITIVE_ARRAY_INSTANTIATION); + + current.objectArrayInstantiation(arrayClassName, lengthAsString, objectId); + } + + /** Event method that denotes the setting of a value in an array. + * + *

More details about this method can be found in this + * class' introduction.

+ * + * @param indexAsString The index to as a String. + */ + void arraySet(String indexAsString) + { + transition(ScannerState.TRANSITION_ARRAY_SET); + + current.arraySet(indexAsString); + } + + /** Event method that denotes the retrieval of a value in an array. + * + *

More details about this method can be found in this + * class' introduction.

+ * + * @param indexAsString The index to as a String. + */ + void arrayGet(String indexAsString) + { + transition(ScannerState.TRANSITION_ARRAY_GET); + + current.arrayGet(indexAsString); + } + + /** Event method that denotes the setting of a value in a list. + * + *

More details about this method can be found in this + * class' introduction.

+ */ + void listSet() + { + transition(ScannerState.TRANSITION_LIST_SET); + + current.listSet(); + } + + /** Event method that denotes the retrieval of a value in a list. + * + *

More details about this method can be found in this + * class' introduction.

+ */ + void listGet() + { + transition(ScannerState.TRANSITION_LIST_GET); + + current.listGet(); + } + + /** Event method that denotes the null value. + */ + void nullObject() + { + transition(ScannerState.TRANSITION_NULL_OBJECT); + + current.nullObject(); + } + + /** Event method that denotes a string. + * + * @param string The string that should be written. + */ + void stringReference(String string) + { + transition(ScannerState.TRANSITION_STRING_REFERENCE); + + current.stringReference(string); + } + + /** Event method that denotes a reference to an existing object. + * + * @param id The ObjectId to be used. + */ + void objectReference(ObjectId id) + { + transition(ScannerState.TRANSITION_OBJECT_REFERENCE); + + current.objectReference(id); + } + +} diff --git a/libjava/classpath/gnu/java/beans/encoder/ScannerState.java b/libjava/classpath/gnu/java/beans/encoder/ScannerState.java new file mode 100644 index 00000000000..888478a8efd --- /dev/null +++ b/libjava/classpath/gnu/java/beans/encoder/ScannerState.java @@ -0,0 +1,236 @@ +/* ScannerState.java + Copyright (C) 2005 Free Software Foundation, Inc. + + This file is part of GNU Classpath. + + GNU Classpath is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + GNU Classpath is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNU Classpath; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. + + Linking this library statically or dynamically with other modules is + making a combined work based on this library. Thus, the terms and + conditions of the GNU General Public License cover the whole + combination. + + As a special exception, the copyright holders of this library give you + permission to link this library with independent modules to produce an + executable, regardless of the license terms of these independent + modules, and to copy and distribute the resulting executable under + terms of your choice, provided that you also meet, for each linked + independent module, the terms and conditions of the license of that + module. An independent module is a module which is not derived from + or based on this library. If you modify this library, you may extend + this exception to your version of the library, but you are not + obligated to do so. If you do not wish to do so, delete this + exception statement from your version. */ + + +package gnu.java.beans.encoder; + +import java.util.HashMap; + +/**

Provides the infrastructure for the state machine and the transition + * mechanism.

+ * + *

Each states knows a set of successor. There can be one successor for + * every transition variant. Furthermore a state knows about a default + * successor which is taken when there is no special setup for a + * transition.

+ * + * @author Robert Schuster (robertschuster@fsfe.org) + * + */ +public abstract class ScannerState +{ + + static final int TRANSITION_METHOD_INVOCATION = 0; + + static final int TRANSITION_STATIC_METHOD_INVOCATION = 1; + + static final int TRANSITION_STATIC_FIELD_ACCESS = 2; + + static final int TRANSITION_CLASS_RESOLUTION = 3; + + static final int TRANSITION_OBJECT_INSTANTIATION = 4; + + static final int TRANSITION_PRIMITIVE_INSTANTIATION = 5; + + static final int TRANSITION_OBJECT_ARRAY_INSTANTIATION = 6; + + static final int TRANSITION_PRIMITIVE_ARRAY_INSTANTIATION = 7; + + static final int TRANSITION_ARRAY_SET = 8; + + static final int TRANSITION_ARRAY_GET = 9; + + static final int TRANSITION_LIST_SET = 10; + + static final int TRANSITION_LIST_GET = 11; + + static final int TRANSITION_NULL_OBJECT = 12; + + static final int TRANSITION_STRING_REFERENCE = 13; + + static final int TRANSITION_OBJECT_REFERENCE = 14; + + static final int TRANSITION_FIRST = 0; + + static final int TRANSITION_LAST = 14; + + static final String DEFAULT_STATE_NAME = "default"; + + String defaultSuccessor = DEFAULT_STATE_NAME; + + static String[] transitionNames = { "METHOD_INVOCATION", "STATIC_METHOD_INVOCATION", + "STATIC_FIELD_ACCESS", "CLASS_RESOLUTION", + "OBJECT_INSTANTIATION", + "PRIMITIVE_INSTANTIATION", "OBJECT_ARRAY_INSTANTIATION", + "PRIMITIVE_ARRAY_INSTANTIATION", + "ARRAY_SET", "ARRAY_GET", "LIST_SET", "LIST_GET", + "NULL_OBJECT", "STRING_REFERENCE", "OBJECT_REFERENCE" }; + + /** + * Stores the transition setup as the relation + * transition->successor's state name. + */ + HashMap transitions = new HashMap(); + + int calls; + + Context context; + + String name; + + final void init(String newName) + { + assert (name == null); + + name = newName; + } + + final String getName() + { + return name; + } + + final void enter(Context ctx) + { + calls++; + context = ctx; + + enterImpl(ctx); + } + + protected void enterImpl(Context ctx) + { + } + + final Context context() + { + return context; + } + + final int getCalls() + { + return calls; + } + + /** + *

Stores a successor's state name for a certain transition.

+ * + *

This method is only used at the configuration time of the state + * machine.

+ * + * @param transition One of the transition constants. + * @param stateName The state name of the successor. + */ + final void putSuccessor(int transition, String stateName) + { + assert (transition >= TRANSITION_FIRST && transition <= TRANSITION_LAST) : + "Transition identifier '" + transition + "' is unknown."; + + transitions.put(new Integer(transition), stateName); + } + + /**

Retrieves a the state name of a successor for the given transition + * constant.

+ * + *

Returns the default successor's state name if no special setup was + * prepared.

+ * + * @param transition One of the transition constants. + * @return The state name of the successor. + */ + final String getSuccessor(int transition) + { + String state = (String) transitions.get(new Integer(transition)); + + return (state == null) ? defaultSuccessor : state; + } + + /** + * Sets the name for the default successor state. + * + * @param newDefaultSuccessor The default successor's state name. + */ + final void setDefaultSuccessor(String newDefaultSuccessor) + { + defaultSuccessor = newDefaultSuccessor; + } + + abstract void methodInvocation(String methodName); + + abstract void staticMethodInvocation(String className, String methodName); + + abstract void staticFieldAccess(String className, String fieldName); + + abstract void classResolution(String className); + + abstract void objectInstantiation(String className, ObjectId objectId); + + abstract void primitiveInstantiation(String primitiveName, + String valueAsString); + + abstract void objectArrayInstantiation(String arrayClassName, String lengthAsString, ObjectId objectId); + + abstract void primitiveArrayInstantiation(String arrayClassName, String lengthAsString, ObjectId objectId); + + abstract void arraySet(String indexAsString); + + abstract void arrayGet(String indexAsString); + + abstract void listGet(); + + abstract void listSet(); + + abstract void nullObject(); + + abstract void stringReference(String string); + + abstract void objectReference(ObjectId id); + + /** + *

A special event that does not provoke a direct transition.

+ * + *

Instead the transition is done by the ScanEngine: It goes + * back to the previous state and just uses this method to inform the state + * about this happening.

+ */ + abstract void end(); + + void enter() + { + } + +} diff --git a/libjava/classpath/gnu/java/beans/encoder/StAXWriter.java b/libjava/classpath/gnu/java/beans/encoder/StAXWriter.java new file mode 100644 index 00000000000..fdb5f7d4571 --- /dev/null +++ b/libjava/classpath/gnu/java/beans/encoder/StAXWriter.java @@ -0,0 +1,233 @@ +/* StAXWriter.java + Copyright (C) 2005 Free Software Foundation, Inc. + + This file is part of GNU Classpath. + + GNU Classpath is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + GNU Classpath is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNU Classpath; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. + + Linking this library statically or dynamically with other modules is + making a combined work based on this library. Thus, the terms and + conditions of the GNU General Public License cover the whole + combination. + + As a special exception, the copyright holders of this library give you + permission to link this library with independent modules to produce an + executable, regardless of the license terms of these independent + modules, and to copy and distribute the resulting executable under + terms of your choice, provided that you also meet, for each linked + independent module, the terms and conditions of the license of that + module. An independent module is a module which is not derived from + or based on this library. If you modify this library, you may extend + this exception to your version of the library, but you are not + obligated to do so. If you do not wish to do so, delete this + exception statement from your version. */ + + +package gnu.java.beans.encoder; + +import java.io.OutputStream; + +import javax.xml.stream.XMLOutputFactory; +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.XMLStreamWriter; + +/** A {@link Writer} implementation based on the StAX API. + * + * @author Robert Schuster (robertschuster@fsfe.org) + * + */ +public class StAXWriter implements Writer +{ + XMLStreamWriter writer; + + int indent = 0; + + public StAXWriter(OutputStream os) + { + try + { + XMLOutputFactory factory = XMLOutputFactory.newInstance(); + writer = factory.createXMLStreamWriter(os); + } + catch (XMLStreamException se) + { + throw (InternalError) + new InternalError( + "Could not instantiate a streaming XML writer.") + .initCause(se); + } + + } + + public void flush() + { + if (writer != null) + { + try + { + writer.flush(); + } + catch (XMLStreamException xse) + { + // TODO: find out + } + } + + } + + public void close() + { + if (writer != null) + { + try + { + writer.close(); + } + catch (XMLStreamException xse) + { + // TODO: find out + } + writer = null; + } + + } + + public void writePreamble() + { + try + { + writer.writeStartDocument("UTF-8", "1.0"); + } + catch (XMLStreamException xmlse) + { + + } + } + + public void writeEnd(boolean wasEmpty) + { + try + { + indent -= 2; + + if (wasEmpty) + return; + + for (int i = 0; i < indent; i++) + writer.writeCharacters(" "); + + writer.writeEndElement(); + + writer.writeCharacters("\n"); + } + catch (XMLStreamException xmlse) + { + + } + } + + public void writeEndNoChildren() + { + try + { + writer.writeEndElement(); + writer.writeCharacters("\n"); + } + catch (XMLStreamException xmlse) + { + + } + } + + public void write(String tagName, boolean empty) + { + write(tagName, null, null, null, empty); + } + + public void write(String tagName, String value) + { + write(tagName, value, null, null, value == null); + } + + public void writeNoChildren(String tagName, String value) + { + try + { + for (int i = 0; i < indent; i++) + writer.writeCharacters(" "); + + writer.writeStartElement(tagName); + + writer.writeCharacters(value); + } + catch (XMLStreamException xmlse) + { + + } + } + + public void write(String tagName, String attributeName, + String attributeValue, boolean empty) + { + write(tagName, null, new String[] { attributeName }, + new String[] { attributeValue }, empty); + } + + public void write(String tagName, String value, String[] attributeNames, + String[] attributeValues, boolean empty) + { + try + { + for (int i = 0; i < indent; i++) + + writer.writeCharacters(" "); + + if (empty) + writer.writeEmptyElement(tagName); + else + writer.writeStartElement(tagName); + + if (attributeNames != null) + for (int i = 0; i < attributeNames.length; i++) + writer.writeAttribute(attributeNames[i], attributeValues[i]); + + writer.writeCharacters("\n"); + + indent += 2; + + if (value != null) + { + for (int i = 0; i < indent; i++) + writer.writeCharacters(" "); + + writer.writeCharacters(value); + + writer.writeCharacters("\n"); + } + } + catch (XMLStreamException xmlse) + { + + } + } + + public void write(String tagName, String[] attributeNames, + String[] attributeValues, boolean empty) + { + write(tagName, null, attributeNames, attributeValues, empty); + } + +} diff --git a/libjava/classpath/gnu/java/beans/encoder/Writer.java b/libjava/classpath/gnu/java/beans/encoder/Writer.java new file mode 100644 index 00000000000..57203d23e82 --- /dev/null +++ b/libjava/classpath/gnu/java/beans/encoder/Writer.java @@ -0,0 +1,174 @@ +/* Writer.java -- Writing interface for XML persistence. + Copyright (C) 2005 Free Software Foundation, Inc. + + This file is part of GNU Classpath. + + GNU Classpath is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + GNU Classpath is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNU Classpath; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. + + Linking this library statically or dynamically with other modules is + making a combined work based on this library. Thus, the terms and + conditions of the GNU General Public License cover the whole + combination. + + As a special exception, the copyright holders of this library give you + permission to link this library with independent modules to produce an + executable, regardless of the license terms of these independent + modules, and to copy and distribute the resulting executable under + terms of your choice, provided that you also meet, for each linked + independent module, the terms and conditions of the license of that + module. An independent module is a module which is not derived from + or based on this library. If you modify this library, you may extend + this exception to your version of the library, but you are not + obligated to do so. If you do not wish to do so, delete this + exception statement from your version. */ + + +package gnu.java.beans.encoder; + +/** A Writer represents a simplified interface to an XML + * writer that is used for the XML persistence mechanism. + * + *

Its sole purpose is to allow multiple backends which may remove + * the need to have certain APIs in the classpath. Eg. it is possible + * to write a stripped down XML Writer that does not rely on SAX, StAX + * or DOM APIs.

+ * + *

The caller may assume that every action is done immediately. However + * it is possible that the underlying implementation uses buffering streams. + * To make sure the data is written call the {@link flush} method.

+ * + *

The Writer implementation should care about the formatting + * of the XML stream making it possible to generate three types of formats using + * a special method invocation chain.

+ * + *

Write + * + * <element/> + * + * by issuing write("element", true) (or any of the other + * write-variants that allows specifying the isEmpty argument) + * and writeEnd(true).

+ * + *

Write + * + * <element>body</element> + * + * by issuing writeNoChildren("element", "body") and writeNoChildrenEnd().

+ * + *

+ * Write + * + * <element> + * <child1/> + * <child2/> + * ... + * <element/> + * + * by issuing write("element", false) (or any of the other + * write-variants that allows specifying the isEmpty argument) + * and writeEnd(false).

+ * + *

Note: It is important that the values of isEmpty and + * wasEmpty match. Otherwise strange things might happen to + * the layout.

+ * + * @author Robert Schuster (robertschuster@fsfe.org) + * + */ +public interface Writer +{ + // TODO: This interface's design is not the best. Feel free to + // improve it as you like. + + /** Writes the XML preamble. */ + void writePreamble(); + + /** Writes the end of an XML tag. + * + *

If your tag has not generated any body text or child + * elements provide true as the argument to generate + * more space efficient variant of the tag.>/p> + * + * @param wasEmpty Whether the tag was empty or not. + */ + void writeEnd(boolean wasEmpty); + + /** Writes an XML tag without any attributes. + * + * @param tagName The name of the tag to write. + * @param empty Whether the element has child elements. + */ + void write(String tagName, boolean empty); + + /** Writes an XML tag with one attribute name and value. + * + * @param tagName The name of the tag to write. + * @param attributeName The name of attribute. + * @param attributeValue The attribute's value. + * @param empty Whether the element has child elements. + */ + void write(String tagName, String attributeName, String attributeValue, boolean empty); + + /** Writes an XML tag with multiple attributes and a body text. + * + * @param tagName The name of the tag to write. + * @param value The element's body content. + * @param attributeNames A set of attribute names. + * @param attributeValues A set of attribute values. + * @param empty Whether the element has child elements. + */ + void write(String tagName, String value, String[] attributeNames, + String[] attributeValues, boolean empty); + + /** Writes an XML tag with multiple attributes without a body text. + * + * @param tagName The name of the tag to write. + * @param attributeNames A set of attribute names. + * @param attributeValues A set of attribute values. + * @param empty Whether the element has child elements. + */ + void write(String tagName, String[] attributeNames, String[] attributeValues, boolean empty); + + /** Writes an XML tag with no attributes but with a body text + * that may have child elements. + * + * @param tagName The name of the tag to write. + * @param value The element's body content. + */ + void write(String tagName, String value); + + /** Writes an XML tag with no attributes but with a body text + * that does not have child elements. + * + * @param tagName The name of the tag to write. + * @param value The element's body content. + */ + void writeNoChildren(String tagName, String value); + + /** Writes the end of an XML tag that has no child elements. + * + *

Must be used in combination with {@link writeNoChildren} only.

+ */ + void writeEndNoChildren(); + + /** Forces the implementation to write some data. + */ + void flush(); + + /** Closes the writer. + */ + void close(); +} diff --git a/libjava/classpath/gnu/java/beans/encoder/elements/ArrayInstantiation.java b/libjava/classpath/gnu/java/beans/encoder/elements/ArrayInstantiation.java new file mode 100644 index 00000000000..a9aef89c772 --- /dev/null +++ b/libjava/classpath/gnu/java/beans/encoder/elements/ArrayInstantiation.java @@ -0,0 +1,74 @@ +/* ArrayInstantiation.java + Copyright (C) 2005 Free Software Foundation, Inc. + + This file is part of GNU Classpath. + + GNU Classpath is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + GNU Classpath is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNU Classpath; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. + + Linking this library statically or dynamically with other modules is + making a combined work based on this library. Thus, the terms and + conditions of the GNU General Public License cover the whole + combination. + + As a special exception, the copyright holders of this library give you + permission to link this library with independent modules to produce an + executable, regardless of the license terms of these independent + modules, and to copy and distribute the resulting executable under + terms of your choice, provided that you also meet, for each linked + independent module, the terms and conditions of the license of that + module. An independent module is a module which is not derived from + or based on this library. If you modify this library, you may extend + this exception to your version of the library, but you are not + obligated to do so. If you do not wish to do so, delete this + exception statement from your version. */ + + +package gnu.java.beans.encoder.elements; + +import gnu.java.beans.encoder.ObjectId; +import gnu.java.beans.encoder.Writer; + +/** Generates an XML element denoting the instantiation of an array. + * + * @author Robert Schuster (robertschuster@fsfe.org) + * + */ +public class ArrayInstantiation extends Element +{ + final String className; + + final String lengthAsString; + + public ArrayInstantiation(String newClassName, String newLengthAsString) + { + className = newClassName; + lengthAsString = newLengthAsString; + } + + public void writeStart(Writer writer) + { + ObjectId objectId = getId(); + if (objectId.isUnused()) + writer.write("array", new String[] { "class", "length" }, + new String[] { className, lengthAsString }, isEmpty()); + else + writer.write("array", new String[] { "id", "class", "length" }, + new String[] { objectId.toString(), className, + lengthAsString }, isEmpty()); + + } + +} diff --git a/libjava/classpath/gnu/java/beans/encoder/elements/Array_Get.java b/libjava/classpath/gnu/java/beans/encoder/elements/Array_Get.java new file mode 100644 index 00000000000..ca2ce0fa663 --- /dev/null +++ b/libjava/classpath/gnu/java/beans/encoder/elements/Array_Get.java @@ -0,0 +1,62 @@ +/* Array_Get.java + Copyright (C) 2005 Free Software Foundation, Inc. + + This file is part of GNU Classpath. + + GNU Classpath is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + GNU Classpath is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNU Classpath; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. + + Linking this library statically or dynamically with other modules is + making a combined work based on this library. Thus, the terms and + conditions of the GNU General Public License cover the whole + combination. + + As a special exception, the copyright holders of this library give you + permission to link this library with independent modules to produce an + executable, regardless of the license terms of these independent + modules, and to copy and distribute the resulting executable under + terms of your choice, provided that you also meet, for each linked + independent module, the terms and conditions of the license of that + module. An independent module is a module which is not derived from + or based on this library. If you modify this library, you may extend + this exception to your version of the library, but you are not + obligated to do so. If you do not wish to do so, delete this + exception statement from your version. */ + + +package gnu.java.beans.encoder.elements; + +import gnu.java.beans.encoder.Writer; + +/** + * Generates an XML element denoting the retrieval of an array value. + * + * @author Robert Schuster (robertschuster@fsfe.org) + */ +public class Array_Get extends Element +{ + final String indexAsString; + + public Array_Get(String newIndexAsString) + { + indexAsString = newIndexAsString; + } + + public void writeStart(Writer writer) + { + writer.write("void", "index", indexAsString, isEmpty()); + } + +} diff --git a/libjava/classpath/gnu/java/beans/encoder/elements/Array_Set.java b/libjava/classpath/gnu/java/beans/encoder/elements/Array_Set.java new file mode 100644 index 00000000000..09623205548 --- /dev/null +++ b/libjava/classpath/gnu/java/beans/encoder/elements/Array_Set.java @@ -0,0 +1,57 @@ +/* Array_Set.java -- FIXME: briefly describe file purpose + Copyright (C) 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.java.beans.encoder.elements; + +import gnu.java.beans.encoder.Writer; + +public class Array_Set extends Element +{ + final String indexAsString; + + public Array_Set(String newIndexAsString) + { + indexAsString = newIndexAsString; + } + + public void writeStart(Writer writer) + { + writer.write("void", "index", indexAsString, isEmpty()); + } + +} diff --git a/libjava/classpath/gnu/java/beans/encoder/elements/ClassResolution.java b/libjava/classpath/gnu/java/beans/encoder/elements/ClassResolution.java new file mode 100644 index 00000000000..8e640d18350 --- /dev/null +++ b/libjava/classpath/gnu/java/beans/encoder/elements/ClassResolution.java @@ -0,0 +1,67 @@ +/* ClassResolution.java + Copyright (C) 2005 Free Software Foundation, Inc. + + This file is part of GNU Classpath. + + GNU Classpath is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + GNU Classpath is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNU Classpath; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. + + Linking this library statically or dynamically with other modules is + making a combined work based on this library. Thus, the terms and + conditions of the GNU General Public License cover the whole + combination. + + As a special exception, the copyright holders of this library give you + permission to link this library with independent modules to produce an + executable, regardless of the license terms of these independent + modules, and to copy and distribute the resulting executable under + terms of your choice, provided that you also meet, for each linked + independent module, the terms and conditions of the license of that + module. An independent module is a module which is not derived from + or based on this library. If you modify this library, you may extend + this exception to your version of the library, but you are not + obligated to do so. If you do not wish to do so, delete this + exception statement from your version. */ + + +package gnu.java.beans.encoder.elements; + +import gnu.java.beans.encoder.Writer; + +/** Generates an XML element denoting the resolution of a class. + * + * @author Robert Schuster (robertschuster@fsfe.org) + * + */ +public class ClassResolution extends Element +{ + final String className; + + public ClassResolution(String newClassName) + { + className = newClassName; + } + + public void writeStart(Writer writer) + { + writer.writeNoChildren("class", className); + } + + public void writeEnd(Writer writer) + { + writer.writeEndNoChildren(); + } + +} diff --git a/libjava/classpath/gnu/java/beans/encoder/elements/Element.java b/libjava/classpath/gnu/java/beans/encoder/elements/Element.java new file mode 100644 index 00000000000..5681d2b76a0 --- /dev/null +++ b/libjava/classpath/gnu/java/beans/encoder/elements/Element.java @@ -0,0 +1,157 @@ +/* Element.java -- Base class for object tree elements. + Copyright (C) 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.java.beans.encoder.elements; + +import java.util.Iterator; +import java.util.LinkedList; + +import gnu.java.beans.encoder.ObjectId; +import gnu.java.beans.encoder.Writer; + +/** Element is the base class for the object tree elements. + * + *

It provides the neccessary infrastructure every element subclass + * needs in order to interact with the {@link gnu.java.beans.encoder.Root} + * class.

+ * + * @author Robert Schuster (robertschuster@fsfe.org) + */ +public abstract class Element +{ + /** + * Stores the child elements. + */ + private LinkedList children = new LinkedList(); + + /** + * An optional ObjectId instance which is needed for certain subclasses + * only. + */ + private ObjectId objectId; + + /** Sets an {@link gnu.java.beans.encoder.ObjectId} instance in this + * Element. + * + *

This can only be done once.

+ * + * @param objectId An ObjectId instance. + */ + public final void initId(ObjectId objectId) + { + assert (this.objectId == null); + assert (objectId != null); + + this.objectId = objectId; + } + + /** Adds a child element to this Element. + * + * @param elem The new child. + */ + public final void addChild(Element elem) + { + children.add(elem); + } + + /** Removes the child element added last. + */ + public final void removeLast() + { + children.removeLast(); + } + + /** Provides access to the child elements via an iterator. + * + * @return An iterator for the child elements. + */ + public final Iterator iterator(){ + return children.iterator(); + } + + /** Clears all the stored child elements. + * + */ + public final void clear() + { + children.clear(); + } + + /** Returns whether this element contains child elements. + * + *

This method is useful to decide which formatting variant + * for the XML element can be chosen.

+ * + * @return Whether the element has child elements. + */ + public final boolean isEmpty() + { + return children.isEmpty(); + } + + /** Retrieves the element's {@link gnu.java.beans.encoder.ObjectId} instance + * if it has one. + * + * @return The ObjectId instance or null. + */ + public final ObjectId getId() + { + return objectId; + } + + /** Writes the opening XML tag. + * + * @param writer The writer to be used for XML writing. + */ + public abstract void writeStart(Writer writer); + + /** Writes the closing XML tag. + * + *

By default this does writer.writeEnd(children.isEmpty()). + * Override if neccessary, for example when using the + * {@link gnu.java.beans.encoder.Writer#writeNoChildren} method + * variants. + * + * @param writer The writer to be used for XML writing. + */ + public void writeEnd(Writer writer) + { + writer.writeEnd(children.isEmpty()); + } + +} diff --git a/libjava/classpath/gnu/java/beans/encoder/elements/List_Get.java b/libjava/classpath/gnu/java/beans/encoder/elements/List_Get.java new file mode 100644 index 00000000000..e73afa8e917 --- /dev/null +++ b/libjava/classpath/gnu/java/beans/encoder/elements/List_Get.java @@ -0,0 +1,56 @@ +/* List_Get.java + Copyright (C) 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.java.beans.encoder.elements; + +import gnu.java.beans.encoder.Writer; + +/** Generates an XML element denoting the retrieval of a list's element. + * + * @author Robert Schuster (robertschuster@fsfe.org) + * + */ +public class List_Get extends Element +{ + + public void writeStart(Writer writer) + { + writer.write("object", "get"); + } + +} diff --git a/libjava/classpath/gnu/java/beans/encoder/elements/List_Set.java b/libjava/classpath/gnu/java/beans/encoder/elements/List_Set.java new file mode 100644 index 00000000000..03c73fd2089 --- /dev/null +++ b/libjava/classpath/gnu/java/beans/encoder/elements/List_Set.java @@ -0,0 +1,56 @@ +/* List_Set.java + Copyright (C) 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.java.beans.encoder.elements; + +import gnu.java.beans.encoder.Writer; + +/** Generates an XML element denoting the setting of a list's element. + * + * @author Robert Schuster (robertschuster@fsfe.org) + * + */ +public class List_Set extends Element +{ + + public void writeStart(Writer writer) + { + writer.write("object", "set"); + } + +} diff --git a/libjava/classpath/gnu/java/beans/encoder/elements/MethodInvocation.java b/libjava/classpath/gnu/java/beans/encoder/elements/MethodInvocation.java new file mode 100644 index 00000000000..d5b317496d5 --- /dev/null +++ b/libjava/classpath/gnu/java/beans/encoder/elements/MethodInvocation.java @@ -0,0 +1,62 @@ +/* MethodCall.java + Copyright (C) 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.java.beans.encoder.elements; + +import gnu.java.beans.encoder.Writer; + +/** Generates an XML element denoting a non-static method call. + * + * @author Robert Schuster (robertschuster@fsfe.org) + * + */ +public class MethodInvocation extends Element +{ + final String methodName; + + public MethodInvocation(String newMethodName) + { + methodName = newMethodName; + } + + public void writeStart(Writer writer) + { + writer.write("void", "method", methodName, isEmpty()); + } + +} diff --git a/libjava/classpath/gnu/java/beans/encoder/elements/NullObject.java b/libjava/classpath/gnu/java/beans/encoder/elements/NullObject.java new file mode 100644 index 00000000000..599c4d85fc3 --- /dev/null +++ b/libjava/classpath/gnu/java/beans/encoder/elements/NullObject.java @@ -0,0 +1,61 @@ +/* NullObject.java + Copyright (C) 2005 Free Software Foundation, Inc. + + This file is part of GNU Classpath. + + GNU Classpath is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + GNU Classpath is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNU Classpath; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. + + Linking this library statically or dynamically with other modules is + making a combined work based on this library. Thus, the terms and + conditions of the GNU General Public License cover the whole + combination. + + As a special exception, the copyright holders of this library give you + permission to link this library with independent modules to produce an + executable, regardless of the license terms of these independent + modules, and to copy and distribute the resulting executable under + terms of your choice, provided that you also meet, for each linked + independent module, the terms and conditions of the license of that + module. An independent module is a module which is not derived from + or based on this library. If you modify this library, you may extend + this exception to your version of the library, but you are not + obligated to do so. If you do not wish to do so, delete this + exception statement from your version. */ + + +package gnu.java.beans.encoder.elements; + +import gnu.java.beans.encoder.Writer; + +/** Generates an XML element denoting the null value. + * + * @author Robert Schuster (robertschuster@fsfe.org) + * + */ +public class NullObject extends Element +{ + + public void writeStart(Writer writer) + { + writer.write("null", true); + } + + public void writeEnd(Writer writer) + { + writer.writeEnd(true); + } + +} diff --git a/libjava/classpath/gnu/java/beans/encoder/elements/ObjectInstantiation.java b/libjava/classpath/gnu/java/beans/encoder/elements/ObjectInstantiation.java new file mode 100644 index 00000000000..692227d59c5 --- /dev/null +++ b/libjava/classpath/gnu/java/beans/encoder/elements/ObjectInstantiation.java @@ -0,0 +1,68 @@ +/* ObjectInstantiation.java + Copyright (C) 2005 Free Software Foundation, Inc. + + This file is part of GNU Classpath. + + GNU Classpath is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + GNU Classpath is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNU Classpath; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. + + Linking this library statically or dynamically with other modules is + making a combined work based on this library. Thus, the terms and + conditions of the GNU General Public License cover the whole + combination. + + As a special exception, the copyright holders of this library give you + permission to link this library with independent modules to produce an + executable, regardless of the license terms of these independent + modules, and to copy and distribute the resulting executable under + terms of your choice, provided that you also meet, for each linked + independent module, the terms and conditions of the license of that + module. An independent module is a module which is not derived from + or based on this library. If you modify this library, you may extend + this exception to your version of the library, but you are not + obligated to do so. If you do not wish to do so, delete this + exception statement from your version. */ + + +package gnu.java.beans.encoder.elements; + +import gnu.java.beans.encoder.ObjectId; +import gnu.java.beans.encoder.Writer; + +/** Generates an XML element denoting the instantiation of an object. + * + * @author Robert Schuster (robertschuster@fsfe.org) + * + */ +public class ObjectInstantiation extends Element +{ + final String className; + + public ObjectInstantiation(String newClassName) + { + className = newClassName; + } + + public void writeStart(Writer writer) + { + ObjectId objectId = getId(); + if (objectId.isUnused()) + writer.write("object", "class", className, isEmpty()); + else + writer.write("object", new String[] { "id", "class" }, + new String[] { objectId.toString(), className }, isEmpty()); + } + +} diff --git a/libjava/classpath/gnu/java/beans/encoder/elements/ObjectReference.java b/libjava/classpath/gnu/java/beans/encoder/elements/ObjectReference.java new file mode 100644 index 00000000000..a44c2ee6055 --- /dev/null +++ b/libjava/classpath/gnu/java/beans/encoder/elements/ObjectReference.java @@ -0,0 +1,68 @@ +/* StringInstantiation.java + Copyright (C) 2005 Free Software Foundation, Inc. + + This file is part of GNU Classpath. + + GNU Classpath is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + GNU Classpath is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNU Classpath; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. + + Linking this library statically or dynamically with other modules is + making a combined work based on this library. Thus, the terms and + conditions of the GNU General Public License cover the whole + combination. + + As a special exception, the copyright holders of this library give you + permission to link this library with independent modules to produce an + executable, regardless of the license terms of these independent + modules, and to copy and distribute the resulting executable under + terms of your choice, provided that you also meet, for each linked + independent module, the terms and conditions of the license of that + module. An independent module is a module which is not derived from + or based on this library. If you modify this library, you may extend + this exception to your version of the library, but you are not + obligated to do so. If you do not wish to do so, delete this + exception statement from your version. */ + + +package gnu.java.beans.encoder.elements; + +import gnu.java.beans.encoder.ObjectId; +import gnu.java.beans.encoder.Writer; + +/** Generates an XML element denoting referencing an existing object. + * + * @author Robert Schuster (robertschuster@fsfe.org) + * + */ +public class ObjectReference extends Element +{ + final ObjectId id; + + public ObjectReference(ObjectId newId) + { + id = newId; + + // Initializing the Id here is making sure it gets + // actually used. This step modifies the Id instance + // in other elements. + id.init(); + } + + public void writeStart(Writer writer) + { + writer.write("object", "idref", id.toString(), isEmpty()); + } + +} diff --git a/libjava/classpath/gnu/java/beans/encoder/elements/PrimitiveInstantiation.java b/libjava/classpath/gnu/java/beans/encoder/elements/PrimitiveInstantiation.java new file mode 100644 index 00000000000..db08edbf8a0 --- /dev/null +++ b/libjava/classpath/gnu/java/beans/encoder/elements/PrimitiveInstantiation.java @@ -0,0 +1,69 @@ +/* PrimitiveInstantiation.java + Copyright (C) 2005 Free Software Foundation, Inc. + + This file is part of GNU Classpath. + + GNU Classpath is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + GNU Classpath is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNU Classpath; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. + + Linking this library statically or dynamically with other modules is + making a combined work based on this library. Thus, the terms and + conditions of the GNU General Public License cover the whole + combination. + + As a special exception, the copyright holders of this library give you + permission to link this library with independent modules to produce an + executable, regardless of the license terms of these independent + modules, and to copy and distribute the resulting executable under + terms of your choice, provided that you also meet, for each linked + independent module, the terms and conditions of the license of that + module. An independent module is a module which is not derived from + or based on this library. If you modify this library, you may extend + this exception to your version of the library, but you are not + obligated to do so. If you do not wish to do so, delete this + exception statement from your version. */ + + +package gnu.java.beans.encoder.elements; + +import gnu.java.beans.encoder.Writer; + +/** Generates an XML element denoting a primitive data value. + * + * @author Robert Schuster (robertschuster@fsfe.org) + * + */ +public class PrimitiveInstantiation extends Element +{ + final String primitiveName; + + final String valueAsString; + + public PrimitiveInstantiation(String newPrimitiveName, String newValueAsString) + { + primitiveName = newPrimitiveName; + valueAsString = newValueAsString; + } + + public void writeStart(Writer writer) + { + writer.writeNoChildren(primitiveName, valueAsString); + } + + public void writeEnd(Writer writer) + { + writer.writeEndNoChildren(); + } +} diff --git a/libjava/classpath/gnu/java/beans/encoder/elements/StaticFieldAccess.java b/libjava/classpath/gnu/java/beans/encoder/elements/StaticFieldAccess.java new file mode 100644 index 00000000000..7ed935d49a0 --- /dev/null +++ b/libjava/classpath/gnu/java/beans/encoder/elements/StaticFieldAccess.java @@ -0,0 +1,66 @@ +/* StaticFieldAccess.java + Copyright (C) 2005 Free Software Foundation, Inc. + + This file is part of GNU Classpath. + + GNU Classpath is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + GNU Classpath is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNU Classpath; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. + + Linking this library statically or dynamically with other modules is + making a combined work based on this library. Thus, the terms and + conditions of the GNU General Public License cover the whole + combination. + + As a special exception, the copyright holders of this library give you + permission to link this library with independent modules to produce an + executable, regardless of the license terms of these independent + modules, and to copy and distribute the resulting executable under + terms of your choice, provided that you also meet, for each linked + independent module, the terms and conditions of the license of that + module. An independent module is a module which is not derived from + or based on this library. If you modify this library, you may extend + this exception to your version of the library, but you are not + obligated to do so. If you do not wish to do so, delete this + exception statement from your version. */ + + +package gnu.java.beans.encoder.elements; + +import gnu.java.beans.encoder.Writer; + +/** Generates an XML element denoting a static method call. + * + * @author Robert Schuster (robertschuster@fsfe.org) + * + */ +public class StaticFieldAccess extends Element +{ + final String className; + + final String fieldName; + + public StaticFieldAccess(String newClassName, String newFieldName) + { + className = newClassName; + fieldName = newFieldName; + } + + public void writeStart(Writer writer) + { + writer.write("object", new String[] { "class", "field" }, + new String[] { className, fieldName }, isEmpty()); + } + +} diff --git a/libjava/classpath/gnu/java/beans/encoder/elements/StaticMethodInvocation.java b/libjava/classpath/gnu/java/beans/encoder/elements/StaticMethodInvocation.java new file mode 100644 index 00000000000..40c46a5346b --- /dev/null +++ b/libjava/classpath/gnu/java/beans/encoder/elements/StaticMethodInvocation.java @@ -0,0 +1,67 @@ +/* StaticMethodCall.java + -- A class denoting an XML element which makes up a static method call. + Copyright (C) 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.java.beans.encoder.elements; + +import gnu.java.beans.encoder.Writer; + +/** + * + * @author Robert Schuster (robertschuster@fsfe.org) + * + */ +public class StaticMethodInvocation extends Element +{ + final String className; + + final String methodName; + + public StaticMethodInvocation(String newClassName, String newMethodName) + { + className = newClassName; + methodName = newMethodName; + } + + public void writeStart(Writer writer) + { + writer.write("void", new String[] { "class", "method" }, + new String[] { className, methodName }, isEmpty()); + } + +} diff --git a/libjava/classpath/gnu/java/beans/encoder/elements/StringReference.java b/libjava/classpath/gnu/java/beans/encoder/elements/StringReference.java new file mode 100644 index 00000000000..c368e652851 --- /dev/null +++ b/libjava/classpath/gnu/java/beans/encoder/elements/StringReference.java @@ -0,0 +1,63 @@ +/* StringInstantiation.java + -- A class denoting an XML element which retrieves an array element. + Copyright (C) 2005 Free Software Foundation, Inc. + + This file is part of GNU Classpath. + + GNU Classpath is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + GNU Classpath is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNU Classpath; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. + + Linking this library statically or dynamically with other modules is + making a combined work based on this library. Thus, the terms and + conditions of the GNU General Public License cover the whole + combination. + + As a special exception, the copyright holders of this library give you + permission to link this library with independent modules to produce an + executable, regardless of the license terms of these independent + modules, and to copy and distribute the resulting executable under + terms of your choice, provided that you also meet, for each linked + independent module, the terms and conditions of the license of that + module. An independent module is a module which is not derived from + or based on this library. If you modify this library, you may extend + this exception to your version of the library, but you are not + obligated to do so. If you do not wish to do so, delete this + exception statement from your version. */ + + +package gnu.java.beans.encoder.elements; + +import gnu.java.beans.encoder.Writer; + +public class StringReference extends Element +{ + final String string; + + public StringReference(String newString) + { + string = newString; + } + + public void writeStart(Writer writer) + { + writer.writeNoChildren("string", string); + } + + public void writeEnd(Writer writer) + { + writer.writeEndNoChildren(); + } + +} diff --git a/libjava/classpath/gnu/java/net/CRLFInputStream.java b/libjava/classpath/gnu/java/net/CRLFInputStream.java index d0f9e8c4130..91fd840b862 100644 --- a/libjava/classpath/gnu/java/net/CRLFInputStream.java +++ b/libjava/classpath/gnu/java/net/CRLFInputStream.java @@ -128,7 +128,7 @@ public class CRLFInputStream in.reset(); if (i != -1) { - l = in.read(b, off, i + 1); // read to CR + l = in.read(b, off, (i + 1) - off); // read to CR in.read(); // skip LF b[i] = LF; // fix CR as LF } diff --git a/libjava/classpath/gnu/java/net/PlainDatagramSocketImpl.java b/libjava/classpath/gnu/java/net/PlainDatagramSocketImpl.java index 339b5561cf2..0fcd780dfb9 100644 --- a/libjava/classpath/gnu/java/net/PlainDatagramSocketImpl.java +++ b/libjava/classpath/gnu/java/net/PlainDatagramSocketImpl.java @@ -38,8 +38,6 @@ exception statement from your version. */ package gnu.java.net; -import gnu.classpath.Configuration; - import java.io.IOException; import java.net.DatagramPacket; import java.net.DatagramSocketImpl; @@ -64,20 +62,7 @@ import java.net.SocketException; */ public final class PlainDatagramSocketImpl extends DatagramSocketImpl { - // Static initializer to load native library - static - { - if (Configuration.INIT_LOAD_LIBRARY) - { - System.loadLibrary("javanet"); - } - } - - /** - * Option id for the IP_TTL (time to live) value. - */ - private static final int IP_TTL = 0x1E61; // 7777 - + /** * This is the actual underlying file descriptor */ @@ -98,6 +83,7 @@ public final class PlainDatagramSocketImpl extends DatagramSocketImpl */ public PlainDatagramSocketImpl() { + // Nothing to do here. } protected void finalize() throws Throwable @@ -123,15 +109,48 @@ public final class PlainDatagramSocketImpl extends DatagramSocketImpl * * @exception SocketException If an error occurs */ - protected synchronized native void bind(int port, InetAddress addr) - throws SocketException; + protected synchronized void bind(int port, InetAddress addr) + throws SocketException + { + VMPlainDatagramSocketImpl.bind(this, port, addr); + } /** * Creates a new datagram socket * * @exception SocketException If an error occurs */ - protected synchronized native void create() throws SocketException; + protected synchronized void create() throws SocketException + { + VMPlainDatagramSocketImpl.create(this); + } + + /** + * Connects to the remote address and port specified as arguments. + * + * @param addr The remote address to connect to + * @param port The remote port to connect to + * + * @exception SocketException If an error occurs + */ + protected void connect(InetAddress addr, int port) throws SocketException + { + VMPlainDatagramSocketImpl.connect(this, addr, port); + } + + /** + * Disconnects the socket. + * + * @since 1.4 + */ + protected void disconnect() + { + synchronized (this) + { + if (native_fd != -1) + close(); + } + } /** * Sets the Time to Live value for the socket @@ -142,7 +161,7 @@ public final class PlainDatagramSocketImpl extends DatagramSocketImpl */ protected synchronized void setTimeToLive(int ttl) throws IOException { - setOption(IP_TTL, new Integer(ttl)); + setOption(VMPlainDatagramSocketImpl.IP_TTL, new Integer(ttl)); } /** @@ -154,7 +173,7 @@ public final class PlainDatagramSocketImpl extends DatagramSocketImpl */ protected synchronized int getTimeToLive() throws IOException { - Object obj = getOption(IP_TTL); + Object obj = getOption(VMPlainDatagramSocketImpl.IP_TTL); if (! (obj instanceof Integer)) throw new IOException("Internal Error"); @@ -162,20 +181,6 @@ public final class PlainDatagramSocketImpl extends DatagramSocketImpl return ((Integer) obj).intValue(); } - /** - * Sends a packet of data to a remote host - * - * @param addr The address to send to - * @param port The port to send to - * @param buf The buffer to send - * @param offset The offset of the data in the buffer to send - * @param len The length of the data to send - * - * @exception IOException If an error occurs - */ - private native void sendto (InetAddress addr, int port, - byte[] buf, int offset, int len) - throws IOException; /** * Sends a packet of data to a remote host @@ -186,12 +191,13 @@ public final class PlainDatagramSocketImpl extends DatagramSocketImpl */ protected void send(DatagramPacket packet) throws IOException { - synchronized(SEND_LOCK) + if (native_fd != -1) { - sendto(packet.getAddress(), packet.getPort(), packet.getData(), - packet.getOffset(), packet.getLength()); - } - + synchronized(SEND_LOCK) + { + VMPlainDatagramSocketImpl.send(this, packet); + } + } } /** @@ -206,18 +212,10 @@ public final class PlainDatagramSocketImpl extends DatagramSocketImpl { synchronized(RECEIVE_LOCK) { - receive0(packet); + VMPlainDatagramSocketImpl.receive(this, packet); } } - /** - * Native call to receive a UDP packet from the network - * - * @param packet The packet to fill in with the data received - * - * @exception IOException IOException If an error occurs - */ - private native void receive0(DatagramPacket packet) throws IOException; /** * Sets the value of an option on the socket @@ -227,8 +225,11 @@ public final class PlainDatagramSocketImpl extends DatagramSocketImpl * * @exception SocketException If an error occurs */ - public synchronized native void setOption(int option_id, Object val) - throws SocketException; + public synchronized void setOption(int option_id, Object val) + throws SocketException + { + VMPlainDatagramSocketImpl.setOption(this, option_id, val); + } /** * Retrieves the value of an option on the socket @@ -239,13 +240,19 @@ public final class PlainDatagramSocketImpl extends DatagramSocketImpl * * @exception SocketException If an error occurs */ - public synchronized native Object getOption(int option_id) - throws SocketException; + public synchronized Object getOption(int option_id) + throws SocketException + { + return VMPlainDatagramSocketImpl.getOption(this, option_id); + } /** * Closes the socket */ - protected synchronized native void close(); + protected synchronized void close() + { + VMPlainDatagramSocketImpl.close(this); + } /** * Gets the Time to Live value for the socket @@ -282,7 +289,10 @@ public final class PlainDatagramSocketImpl extends DatagramSocketImpl * * @exception IOException If an error occurs */ - protected synchronized native void join(InetAddress addr) throws IOException; + protected synchronized void join(InetAddress addr) throws IOException + { + VMPlainDatagramSocketImpl.join(this,addr); + } /** * Leaves a multicast group @@ -291,7 +301,10 @@ public final class PlainDatagramSocketImpl extends DatagramSocketImpl * * @exception IOException If an error occurs */ - protected synchronized native void leave(InetAddress addr) throws IOException; + protected synchronized void leave(InetAddress addr) throws IOException + { + VMPlainDatagramSocketImpl.leave(this, addr); + } /** * What does this method really do? @@ -308,14 +321,14 @@ public final class PlainDatagramSocketImpl extends DatagramSocketImpl } public void joinGroup(SocketAddress address, NetworkInterface netIf) + throws IOException { - throw new InternalError - ("PlainDatagramSocketImpl::joinGroup is not implemented"); + VMPlainDatagramSocketImpl.joinGroup(this, address, netIf); } public void leaveGroup(SocketAddress address, NetworkInterface netIf) + throws IOException { - throw new InternalError - ("PlainDatagramSocketImpl::leaveGroup is not implemented"); + VMPlainDatagramSocketImpl.leaveGroup(this, address, netIf); } } diff --git a/libjava/classpath/gnu/java/net/PlainSocketImpl.java b/libjava/classpath/gnu/java/net/PlainSocketImpl.java index 05221ff88e9..47d05aa410d 100644 --- a/libjava/classpath/gnu/java/net/PlainSocketImpl.java +++ b/libjava/classpath/gnu/java/net/PlainSocketImpl.java @@ -39,17 +39,13 @@ exception statement from your version. */ package gnu.java.net; -import gnu.classpath.Configuration; - -import java.io.IOException; import java.io.InputStream; +import java.io.IOException; import java.io.OutputStream; import java.net.InetAddress; -import java.net.InetSocketAddress; import java.net.SocketAddress; import java.net.SocketException; import java.net.SocketImpl; -import java.net.SocketOptions; /** * Written using on-line Java Platform 1.2 API Specification, as well @@ -69,14 +65,6 @@ import java.net.SocketOptions; */ public final class PlainSocketImpl extends SocketImpl { - // Static initializer to load native library. - static - { - if (Configuration.INIT_LOAD_LIBRARY) - { - System.loadLibrary("javanet"); - } - } /** * The OS file handle representing the socket. @@ -125,10 +113,11 @@ public final class PlainSocketImpl extends SocketImpl } /** - * Default do nothing constructor + * Default do nothing constructor. */ public PlainSocketImpl() { + // Nothing to do here. } protected void finalize() throws Throwable @@ -142,6 +131,7 @@ public final class PlainSocketImpl extends SocketImpl } catch (IOException ex) { + // Nothing we can do about it. } } super.finalize(); @@ -158,121 +148,111 @@ public final class PlainSocketImpl extends SocketImpl * Integer. The option_id parameter is one of the defined constants in * this interface. * - * @param option_id The identifier of the option - * @param val The value to set the option to + * @param optionId The identifier of the option + * @param value The value to set the option to * - * @exception SocketException If an error occurs + * @throws SocketException if an error occurs */ - public native void setOption(int optID, Object value) throws SocketException; + public void setOption(int optionId, Object value) throws SocketException + { + VMPlainSocketImpl.setOption(this, optionId, value); + } /** * Returns the current setting of the specified option. The Object returned * will be an Integer for options that have integer values. The option_id * is one of the defined constants in this interface. * - * @param option_id The option identifier + * @param optionId the option identifier * - * @return The current value of the option + * @return the current value of the option * - * @exception SocketException If an error occurs + * @throws SocketException if an error occurs */ - public native Object getOption(int optID) throws SocketException; + public Object getOption(int optionId) throws SocketException + { + return VMPlainSocketImpl.getOption(this, optionId); + } - /** - * Flushes the input stream and closes it. If you read from the input stream - * after calling this method a IOException will be thrown. - * - * @throws IOException if an error occurs - */ - public native void shutdownInput() throws IOException; + public void shutdownInput() throws IOException + { + VMPlainSocketImpl.shutdownInput(this); + } - /** - * Flushes the output stream and closes it. If you write to the output stream - * after calling this method a IOException will be thrown. - * - * @throws IOException if an error occurs - */ - public native void shutdownOutput() throws IOException; + public void shutdownOutput() throws IOException + { + VMPlainSocketImpl.shutdownOutput(this); + } /** * Creates a new socket that is not bound to any local address/port and - * is not connected to any remote address/port. This will be created as - * a stream socket if the stream parameter is true, or a datagram socket - * if the stream parameter is false. + * is not connected to any remote address/port. The stream parameter will be + * ignored since PlainSocketImpl always is a stream socket. Datagram sockets + * are handled by PlainDatagramSocketImpl. * - * @param stream true for a stream socket, false for a datagram socket + * @param stream true for stream sockets, false for + * datagram sockets */ - protected synchronized native void create(boolean stream) throws IOException; + protected synchronized void create(boolean stream) throws IOException + { + VMPlainSocketImpl.create(this); + } /** * Connects to the remote hostname and port specified as arguments. * - * @param hostname The remote hostname to connect to - * @param port The remote port to connect to + * @param hostname the remote hostname to connect to + * @param port the remote port to connect to * - * @exception IOException If an error occurs + * @throws IOException If an error occurs */ - protected synchronized void connect(String host, int port) throws IOException + protected synchronized void connect(String hostname, int port) + throws IOException { - connect(InetAddress.getByName(host), port); + connect(InetAddress.getByName(hostname), port); } /** * Connects to the remote address and port specified as arguments. * - * @param addr The remote address to connect to - * @param port The remote port to connect to + * @param addr the remote address to connect to + * @param port the remote port to connect to * - * @exception IOException If an error occurs + * @throws IOException If an error occurs */ - protected native void connect(InetAddress addr, int port) throws IOException; + protected void connect(InetAddress addr, int port) throws IOException + { + VMPlainSocketImpl.connect(this, addr, port); + } /** * Connects to the remote socket address with a specified timeout. * - * @param timeout The timeout to use for this connect, 0 means infinite. + * @param address the remote address to connect to + * @param timeout the timeout to use for this connect, 0 means infinite. * - * @exception IOException If an error occurs + * @throws IOException If an error occurs */ - protected synchronized void connect(SocketAddress address, int timeout) throws IOException + protected synchronized void connect(SocketAddress address, int timeout) + throws IOException { - InetSocketAddress sockAddr = (InetSocketAddress) address; - InetAddress addr = sockAddr.getAddress(); - - if (addr == null) - throw new IllegalArgumentException("address is unresolved: " + sockAddr); - - int port = sockAddr.getPort(); - - if (timeout < 0) - throw new IllegalArgumentException("negative timeout"); - - Object oldTimeoutObj = null; - - try - { - oldTimeoutObj = this.getOption (SocketOptions.SO_TIMEOUT); - this.setOption (SocketOptions.SO_TIMEOUT, new Integer (timeout)); - connect (addr, port); - } - finally - { - if (oldTimeoutObj != null) - this.setOption (SocketOptions.SO_TIMEOUT, oldTimeoutObj); - } + VMPlainSocketImpl.connect(this, address, timeout); } /** * Binds to the specified port on the specified addr. Note that this addr * must represent a local IP address. **** How bind to INADDR_ANY? **** * - * @param addr The address to bind to - * @param port The port number to bind to + * @param addr the address to bind to + * @param port the port number to bind to * - * @exception IOException If an error occurs + * @throws IOException if an error occurs */ - protected synchronized native void bind(InetAddress addr, int port) - throws IOException; + protected synchronized void bind(InetAddress addr, int port) + throws IOException + { + VMPlainSocketImpl.bind(this, addr, port); + } /** * Starts listening for connections on a socket. The queuelen parameter @@ -282,10 +262,13 @@ public final class PlainSocketImpl extends SocketImpl * * @param queuelen The length of the pending connection queue * - * @exception IOException If an error occurs + * @throws IOException If an error occurs */ - protected synchronized native void listen(int queuelen) - throws IOException; + protected synchronized void listen(int queuelen) + throws IOException + { + VMPlainSocketImpl.listen(this, queuelen); + } /** * Accepts a new connection on this socket and returns in in the @@ -293,33 +276,44 @@ public final class PlainSocketImpl extends SocketImpl * * @param impl The SocketImpl object to accept this connection. */ - protected synchronized native void accept(SocketImpl impl) - throws IOException; + protected synchronized void accept(SocketImpl impl) + throws IOException + { + VMPlainSocketImpl.accept(this, impl); + } /** * Returns the number of bytes that the caller can read from this socket * without blocking. * - * @return The number of readable bytes before blocking + * @return the number of readable bytes before blocking * - * @exception IOException If an error occurs + * @throws IOException if an error occurs */ - protected native int available() throws IOException; + protected int available() throws IOException + { + return VMPlainSocketImpl.available(this); + } /** * Closes the socket. This will cause any InputStream or OutputStream * objects for this Socket to be closed as well. + * *

* Note that if the SO_LINGER option is set on this socket, then the * operation could block. + *

* - * @exception IOException If an error occurs + * @throws IOException if an error occurs */ - protected native void close() throws IOException; + protected void close() throws IOException + { + VMPlainSocketImpl.close(this); + } public void sendUrgentData(int data) { - throw new InternalError ("PlainSocketImpl::sendUrgentData not implemented"); + VMPlainSocketImpl.sendUrgendData(this, data); } /** @@ -327,22 +321,53 @@ public final class PlainSocketImpl extends SocketImpl * the connection. Reads up to len bytes of data into the buffer * buf starting at offset bytes into the buffer. * - * @return The actual number of bytes read or -1 if end of stream. + * @return the actual number of bytes read or -1 if end of stream. * - * @exception IOException If an error occurs + * @throws IOException if an error occurs + */ + protected int read(byte[] buf, int offset, int len) + throws IOException + { + return VMPlainSocketImpl.read(this, buf, offset, len); + } + + /** + * Internal method used by SocketInputStream for reading data from + * the connection. Reads and returns one byte of data. + * + * @return the read byte + * + * @throws IOException if an error occurs */ - protected native int read(byte[] buf, int offset, int len) - throws IOException; + protected int read() + throws IOException + { + return VMPlainSocketImpl.read(this); + } /** * Internal method used by SocketOuputStream for writing data to * the connection. Writes up to len bytes of data from the buffer * buf starting at offset bytes into the buffer. * - * @exception IOException If an error occurs + * @throws IOException If an error occurs + */ + protected void write(byte[] buf, int offset, int len) + throws IOException + { + VMPlainSocketImpl.write(this, buf, offset, len); + } + + /** + * Internal method used by SocketOuputStream for writing data to + * the connection. Writes up one byte to the socket. + * + * @throws IOException If an error occurs */ - protected native void write(byte[] buf, int offset, int len) - throws IOException; + protected void write(int data) throws IOException + { + VMPlainSocketImpl.write(this, data); + } /** * Returns an InputStream object for reading from this socket. This will @@ -356,7 +381,7 @@ public final class PlainSocketImpl extends SocketImpl { if (in == null) in = new SocketInputStream(); - + return in; } @@ -372,7 +397,7 @@ public final class PlainSocketImpl extends SocketImpl { if (out == null) out = new SocketOutputStream(); - + return out; } @@ -380,7 +405,7 @@ public final class PlainSocketImpl extends SocketImpl * This class contains an implementation of InputStream for * sockets. It in an internal only class used by PlainSocketImpl. * - * @author Nic Ferrier (nferrier@tapsellferrier.co.uk) + * @author Nic Ferrier */ final class SocketInputStream extends InputStream @@ -412,13 +437,7 @@ public final class PlainSocketImpl extends SocketImpl */ public int read() throws IOException { - byte buf[] = new byte [1]; - int bytes_read = read(buf, 0, 1); - - if (bytes_read == -1) - return -1; - - return buf[0] & 0xFF; + return PlainSocketImpl.this.read(); } /** @@ -450,7 +469,7 @@ public final class PlainSocketImpl extends SocketImpl * getOutputStream method. It expects only to be used in that * context. * - * @author Nic Ferrier (nferrier@tapsellferrier.co.uk) + * @author Nic Ferrier */ final class SocketOutputStream extends OutputStream @@ -476,8 +495,7 @@ public final class PlainSocketImpl extends SocketImpl */ public void write(int b) throws IOException { - byte buf[] = { (byte) b }; - write(buf, 0, 1); + PlainSocketImpl.this.write(b); } /** diff --git a/libjava/classpath/gnu/java/nio/channels/FileChannelImpl.java b/libjava/classpath/gnu/java/nio/channels/FileChannelImpl.java index 466f3dd5587..a557c7d3ba6 100644 --- a/libjava/classpath/gnu/java/nio/channels/FileChannelImpl.java +++ b/libjava/classpath/gnu/java/nio/channels/FileChannelImpl.java @@ -1,5 +1,5 @@ /* FileChannelImpl.java -- - Copyright (C) 2002, 2004, 2005 Free Software Foundation, Inc. + Copyright (C) 2002, 2004, 2005, 2006 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -301,8 +301,10 @@ public final class FileChannelImpl extends FileChannel else if (mode == MapMode.READ_WRITE || mode == MapMode.PRIVATE) { nmode = mode == MapMode.READ_WRITE ? '+' : 'c'; - if ((this.mode & (READ|WRITE)) != (READ|WRITE)) + if ((this.mode & WRITE) != WRITE) throw new NonWritableChannelException(); + if ((this.mode & READ) != READ) + throw new NonReadableChannelException(); } else throw new IllegalArgumentException ("mode: " + mode); diff --git a/libjava/classpath/gnu/java/nio/charset/Provider.java b/libjava/classpath/gnu/java/nio/charset/Provider.java index 3f25c598851..01c2650a81b 100644 --- a/libjava/classpath/gnu/java/nio/charset/Provider.java +++ b/libjava/classpath/gnu/java/nio/charset/Provider.java @@ -1,5 +1,5 @@ /* Provider.java -- - Copyright (C) 2002, 2005 Free Software Foundation, Inc. + Copyright (C) 2002, 2005, 2006 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -39,6 +39,8 @@ package gnu.java.nio.charset; import java.nio.charset.Charset; import java.nio.charset.spi.CharsetProvider; +import java.security.AccessController; +import java.security.PrivilegedAction; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; @@ -47,6 +49,11 @@ import java.util.Iterator; * Charset provider for the required charsets. Used by * {@link Charset#charsetForName} and * {@link Charset#availableCharsets}. * + * Note: This class is a privileged class, because it can be instantiated without + * requiring the RuntimePermission("charsetProvider"). There is a check in + * java.nio.charset.spi.CharsetProvider to skip the security check if the provider + * is an instance of this class. + * * @author Jesse Rosenstock * @author Robert Schuster (thebohemian@gmx.net) * @see Charset @@ -55,14 +62,6 @@ public final class Provider extends CharsetProvider { private static Provider singleton; - static - { - synchronized (Provider.class) - { - singleton = null; - } - } - /** * Map from charset name to charset canonical name. The strings * are all lower-case to allow case-insensitive retrieval of @@ -232,8 +231,16 @@ public final class Provider extends CharsetProvider public static synchronized Provider provider () { + // The default provider is safe to instantiate. if (singleton == null) - singleton = new Provider (); + singleton = (Provider) AccessController.doPrivileged + (new PrivilegedAction() + { + public Object run() + { + return new Provider(); + } + }); return singleton; } } diff --git a/libjava/classpath/gnu/java/nio/charset/iconv/IconvProvider.java b/libjava/classpath/gnu/java/nio/charset/iconv/IconvProvider.java index 873e9ecda41..6fd8b744082 100644 --- a/libjava/classpath/gnu/java/nio/charset/iconv/IconvProvider.java +++ b/libjava/classpath/gnu/java/nio/charset/iconv/IconvProvider.java @@ -1,5 +1,5 @@ /* IconvProvider.java -- - Copyright (C) 2005 Free Software Foundation, Inc. + Copyright (C) 2005, 2006 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -48,20 +48,17 @@ import java.util.Vector; /** * Charset provider wrapping iconv. * + * Note: This class is a privileged class, because it can be instantiated without + * requiring the RuntimePermission("charsetProvider"). There is a check in + * java.nio.charset.spi.CharsetProvider to skip the security check if the provider + * is an instance of this class. + * * @author Sven de Marothy */ public final class IconvProvider extends CharsetProvider { private static IconvProvider singleton; - static - { - synchronized (IconvProvider.class) - { - singleton = null; - } - } - // Declaring the construtor public may violate the use of singleton. // But it must be public so that an instance of this class can be // created by Class.newInstance(), which is the case when this provider is diff --git a/libjava/classpath/gnu/java/rmi/server/UnicastRef.java b/libjava/classpath/gnu/java/rmi/server/UnicastRef.java index c1b871fb8e5..8097a04663c 100644 --- a/libjava/classpath/gnu/java/rmi/server/UnicastRef.java +++ b/libjava/classpath/gnu/java/rmi/server/UnicastRef.java @@ -47,6 +47,7 @@ import java.io.ObjectOutput; import java.io.ObjectOutputStream; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; +import java.rmi.ConnectException; import java.rmi.Remote; import java.rmi.RemoteException; import java.rmi.server.ObjID; @@ -195,7 +196,7 @@ public RemoteCall newCall(RemoteObject obj, Operation[] op, int opnum, long hash conn = manager.getConnection(); } catch (IOException e1) { - throw new RemoteException("connection failed to host: " + manager.serverName, e1); + throw new ConnectException("connection failed to host: " + manager.serverName, e1); } //obj: useless? diff --git a/libjava/classpath/gnu/java/security/Engine.java b/libjava/classpath/gnu/java/security/Engine.java index b75de5caa95..4b6bd10d9dd 100644 --- a/libjava/classpath/gnu/java/security/Engine.java +++ b/libjava/classpath/gnu/java/security/Engine.java @@ -1,5 +1,5 @@ /* Engine -- generic getInstance method. - Copyright (C) 2003 Free Software Foundation, Inc. + Copyright (C) 2003, 2006 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -130,7 +130,14 @@ public final class Engine Provider provider, Object[] initArgs) throws InvocationTargetException, NoSuchAlgorithmException { - if (service == null || algorithm == null + if (service != null) + service = service.trim(); + + if (algorithm != null) + algorithm = algorithm.trim(); + + if (service == null || service.length() == 0 + || algorithm == null || algorithm.length() == 0 || provider == null || initArgs == null) throw new IllegalArgumentException(); diff --git a/libjava/classpath/gnu/java/util/WeakIdentityHashMap.java b/libjava/classpath/gnu/java/util/WeakIdentityHashMap.java new file mode 100644 index 00000000000..210a3d8c58b --- /dev/null +++ b/libjava/classpath/gnu/java/util/WeakIdentityHashMap.java @@ -0,0 +1,862 @@ +/* WeakIdentityHashMap -- an identity hashtable that keeps only weak references + to its keys, allowing the virtual machine to reclaim them + Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.java.util; + +import java.lang.ref.ReferenceQueue; +import java.lang.ref.WeakReference; +import java.util.AbstractMap; +import java.util.AbstractSet; +import java.util.Collection; +import java.util.ConcurrentModificationException; +import java.util.Iterator; +import java.util.Map; +import java.util.NoSuchElementException; +import java.util.Set; + +/** + * A weak hash map has only weak references to the key. This means that it + * allows the key to be garbage collected if it is not used otherwise. If + * this happens, the entry will eventually disappear from the map, + * asynchronously. + * + *

Other strange behaviors to be aware of: The size of this map may + * spontaneously shrink (even if you use a synchronized map and synchronize + * it); it behaves as if another thread removes entries from this table + * without synchronization. The entry set returned by entrySet + * has similar phenomenons: The size may spontaneously shrink, or an + * entry, that was in the set before, suddenly disappears. + * + *

A weak hash map is not meant for caches; use a normal map, with + * soft references as values instead, or try {@link LinkedHashMap}. + * + *

The weak hash map supports null values and null keys. The null key + * is never deleted from the map (except explictly of course). The + * performance of the methods are similar to that of a hash map. + * + *

The value objects are strongly referenced by this table. So if a + * value object maintains a strong reference to the key (either direct + * or indirect) the key will never be removed from this map. According + * to Sun, this problem may be fixed in a future release. It is not + * possible to do it with the jdk 1.2 reference model, though. + * + * @author Jochen Hoenicke + * @author Eric Blake (ebb9@email.byu.edu) + * @author Jeroen Frijters + * + * @see HashMap + * @see WeakReference + * @see WeakHashMap + * @see IdentityHashMap + * @see LinkedHashMap + */ +public class WeakIdentityHashMap extends AbstractMap implements Map +{ + /** + * The default capacity for an instance of HashMap. + * Sun's documentation mildly suggests that this (11) is the correct + * value. + */ + private static final int DEFAULT_CAPACITY = 11; + + /** + * The default load factor of a HashMap. + */ + private static final float DEFAULT_LOAD_FACTOR = 0.75F; + + /** + * This is used instead of the key value null. It is needed + * to distinguish between an null key and a removed key. + */ + // Package visible for use by nested classes. + static final Object NULL_KEY = new Object(); + + /** + * The reference queue where our buckets (which are WeakReferences) are + * registered to. + */ + private final ReferenceQueue queue; + + /** + * The number of entries in this hash map. + */ + // Package visible for use by nested classes. + int size; + + /** + * The load factor of this WeakIdentityHashMap. This is the maximum ratio of + * size versus number of buckets. If size grows the number of buckets + * must grow, too. + */ + private float loadFactor; + + /** + * The rounded product of the capacity (i.e. number of buckets) and + * the load factor. When the number of elements exceeds the + * threshold, the HashMap calls rehash(). + */ + private int threshold; + + /** + * The number of structural modifications. This is used by + * iterators, to see if they should fail. This doesn't count + * the silent key removals, when a weak reference is cleared + * by the garbage collection. Instead the iterators must make + * sure to have strong references to the entries they rely on. + */ + // Package visible for use by nested classes. + int modCount; + + /** + * The entry set. There is only one instance per hashmap, namely + * theEntrySet. Note that the entry set may silently shrink, just + * like the WeakIdentityHashMap. + */ + private final class WeakEntrySet extends AbstractSet + { + /** + * Non-private constructor to reduce bytecode emitted. + */ + WeakEntrySet() + { + } + + /** + * Returns the size of this set. + * + * @return the set size + */ + public int size() + { + return size; + } + + /** + * Returns an iterator for all entries. + * + * @return an Entry iterator + */ + public Iterator iterator() + { + return new Iterator() + { + /** + * The entry that was returned by the last + * next() call. This is also the entry whose + * bucket should be removed by the remove call.
+ * + * It is null, if the next method wasn't + * called yet, or if the entry was already removed.
+ * + * Remembering this entry here will also prevent it from + * being removed under us, since the entry strongly refers + * to the key. + */ + WeakBucket.WeakEntry lastEntry; + + /** + * The entry that will be returned by the next + * next() call. It is null if there + * is no further entry.
+ * + * Remembering this entry here will also prevent it from + * being removed under us, since the entry strongly refers + * to the key. + */ + WeakBucket.WeakEntry nextEntry = findNext(null); + + /** + * The known number of modification to the list, if it differs + * from the real number, we throw an exception. + */ + int knownMod = modCount; + + /** + * Check the known number of modification to the number of + * modifications of the table. If it differs from the real + * number, we throw an exception. + * @throws ConcurrentModificationException if the number + * of modifications doesn't match. + */ + private void checkMod() + { + // This method will get inlined. + cleanQueue(); + if (knownMod != modCount) + throw new ConcurrentModificationException(knownMod + " != " + + modCount); + } + + /** + * Get a strong reference to the next entry after + * lastBucket. + * @param lastEntry the previous bucket, or null if we should + * get the first entry. + * @return the next entry. + */ + private WeakBucket.WeakEntry findNext(WeakBucket.WeakEntry lastEntry) + { + int slot; + WeakBucket nextBucket; + if (lastEntry != null) + { + nextBucket = lastEntry.getBucket().next; + slot = lastEntry.getBucket().slot; + } + else + { + nextBucket = buckets[0]; + slot = 0; + } + + while (true) + { + while (nextBucket != null) + { + WeakBucket.WeakEntry entry = nextBucket.getEntry(); + if (entry != null) + // This is the next entry. + return entry; + + // Entry was cleared, try next. + nextBucket = nextBucket.next; + } + + slot++; + if (slot == buckets.length) + // No more buckets, we are through. + return null; + + nextBucket = buckets[slot]; + } + } + + /** + * Checks if there are more entries. + * @return true, iff there are more elements. + * @throws ConcurrentModificationException if the hash map was + * modified. + */ + public boolean hasNext() + { + checkMod(); + return nextEntry != null; + } + + /** + * Returns the next entry. + * @return the next entry. + * @throws ConcurrentModificationException if the hash map was + * modified. + * @throws NoSuchElementException if there is no entry. + */ + public Object next() + { + checkMod(); + if (nextEntry == null) + throw new NoSuchElementException(); + lastEntry = nextEntry; + nextEntry = findNext(lastEntry); + return lastEntry; + } + + /** + * Removes the last returned entry from this set. This will + * also remove the bucket of the underlying weak hash map. + * @throws ConcurrentModificationException if the hash map was + * modified. + * @throws IllegalStateException if next() was + * never called or the element was already removed. + */ + public void remove() + { + checkMod(); + if (lastEntry == null) + throw new IllegalStateException(); + modCount++; + internalRemove(lastEntry.getBucket()); + lastEntry = null; + knownMod++; + } + }; + } + } + + /** + * A bucket is a weak reference to the key, that contains a strong + * reference to the value, a pointer to the next bucket and its slot + * number.
+ * + * It would be cleaner to have a WeakReference as field, instead of + * extending it, but if a weak reference gets cleared, we only get + * the weak reference (by queue.poll) and wouldn't know where to + * look for this reference in the hashtable, to remove that entry. + * + * @author Jochen Hoenicke + */ + private static class WeakBucket extends WeakReference + { + /** + * The value of this entry. The key is stored in the weak + * reference that we extend. + */ + Object value; + + /** + * The next bucket describing another entry that uses the same + * slot. + */ + WeakBucket next; + + /** + * The slot of this entry. This should be + * Math.abs(key.hashCode() % buckets.length). + * + * But since the key may be silently removed we have to remember + * the slot number. + * + * If this bucket was removed the slot is -1. This marker will + * prevent the bucket from being removed twice. + */ + int slot; + + /** + * Creates a new bucket for the given key/value pair and the specified + * slot. + * @param key the key + * @param queue the queue the weak reference belongs to + * @param value the value + * @param slot the slot. This must match the slot where this bucket + * will be enqueued. + */ + public WeakBucket(Object key, ReferenceQueue queue, Object value, + int slot) + { + super(key, queue); + this.value = value; + this.slot = slot; + } + + /** + * This class gives the Entry representation of the + * current bucket. It also keeps a strong reference to the + * key; bad things may happen otherwise. + */ + class WeakEntry implements Map.Entry + { + /** + * The strong ref to the key. + */ + Object key; + + /** + * Creates a new entry for the key. + * @param key the key + */ + public WeakEntry(Object key) + { + this.key = key; + } + + /** + * Returns the underlying bucket. + * @return the owning bucket + */ + public WeakBucket getBucket() + { + return WeakBucket.this; + } + + /** + * Returns the key. + * @return the key + */ + public Object getKey() + { + return key == NULL_KEY ? null : key; + } + + /** + * Returns the value. + * @return the value + */ + public Object getValue() + { + return value; + } + + /** + * This changes the value. This change takes place in + * the underlying hash map. + * @param newVal the new value + * @return the old value + */ + public Object setValue(Object newVal) + { + Object oldVal = value; + value = newVal; + return oldVal; + } + + /** + * The hashCode as specified in the Entry interface. + * @return the hash code + */ + public int hashCode() + { + return System.identityHashCode(key) + ^ (value == null ? 0 : value.hashCode()); + } + + /** + * The equals method as specified in the Entry interface. + * @param o the object to compare to + * @return true iff o represents the same key/value pair + */ + public boolean equals(Object o) + { + if (o instanceof Map.Entry) + { + Map.Entry e = (Map.Entry) o; + return getKey() == e.getKey() + && (value == null ? e.getValue() == null + : value.equals(e.getValue())); + } + return false; + } + + public String toString() + { + return getKey() + "=" + value; + } + } + + /** + * This returns the entry stored in this bucket, or null, if the + * bucket got cleared in the mean time. + * @return the Entry for this bucket, if it exists + */ + WeakEntry getEntry() + { + final Object key = this.get(); + if (key == null) + return null; + return new WeakEntry(key); + } + } + + /** + * The entry set returned by entrySet(). + */ + private final WeakEntrySet theEntrySet; + + /** + * The hash buckets. These are linked lists. Package visible for use in + * nested classes. + */ + WeakBucket[] buckets; + + /** + * Creates a new weak hash map with default load factor and default + * capacity. + */ + public WeakIdentityHashMap() + { + this(DEFAULT_CAPACITY, DEFAULT_LOAD_FACTOR); + } + + /** + * Creates a new weak hash map with default load factor and the given + * capacity. + * @param initialCapacity the initial capacity + * @throws IllegalArgumentException if initialCapacity is negative + */ + public WeakIdentityHashMap(int initialCapacity) + { + this(initialCapacity, DEFAULT_LOAD_FACTOR); + } + + /** + * Creates a new weak hash map with the given initial capacity and + * load factor. + * @param initialCapacity the initial capacity. + * @param loadFactor the load factor (see class description of HashMap). + * @throws IllegalArgumentException if initialCapacity is negative, or + * loadFactor is non-positive + */ + public WeakIdentityHashMap(int initialCapacity, float loadFactor) + { + // Check loadFactor for NaN as well. + if (initialCapacity < 0 || ! (loadFactor > 0)) + throw new IllegalArgumentException(); + if (initialCapacity == 0) + initialCapacity = 1; + this.loadFactor = loadFactor; + threshold = (int) (initialCapacity * loadFactor); + theEntrySet = new WeakEntrySet(); + queue = new ReferenceQueue(); + buckets = new WeakBucket[initialCapacity]; + } + + /** + * Construct a new WeakIdentityHashMap with the same mappings as the given map. + * The WeakIdentityHashMap has a default load factor of 0.75. + * + * @param m the map to copy + * @throws NullPointerException if m is null + * @since 1.3 + */ + public WeakIdentityHashMap(Map m) + { + this(m.size(), DEFAULT_LOAD_FACTOR); + putAll(m); + } + + /** + * Simply hashes a non-null Object to its array index. + * @param key the key to hash + * @return its slot number + */ + private int hash(Object key) + { + return Math.abs(System.identityHashCode(key) % buckets.length); + } + + /** + * Cleans the reference queue. This will poll all references (which + * are WeakBuckets) from the queue and remove them from this map. + * This will not change modCount, even if it modifies the map. The + * iterators have to make sure that nothing bad happens.
+ * + * Currently the iterator maintains a strong reference to the key, so + * that is no problem. + */ + // Package visible for use by nested classes. + void cleanQueue() + { + Object bucket = queue.poll(); + while (bucket != null) + { + internalRemove((WeakBucket) bucket); + bucket = queue.poll(); + } + } + + /** + * Rehashes this hashtable. This will be called by the + * add() method if the size grows beyond the threshold. + * It will grow the bucket size at least by factor two and allocates + * new buckets. + */ + private void rehash() + { + WeakBucket[] oldBuckets = buckets; + int newsize = buckets.length * 2 + 1; // XXX should be prime. + threshold = (int) (newsize * loadFactor); + buckets = new WeakBucket[newsize]; + + // Now we have to insert the buckets again. + for (int i = 0; i < oldBuckets.length; i++) + { + WeakBucket bucket = oldBuckets[i]; + WeakBucket nextBucket; + while (bucket != null) + { + nextBucket = bucket.next; + + Object key = bucket.get(); + if (key == null) + { + // This bucket should be removed; it is probably + // already on the reference queue. We don't insert it + // at all, and mark it as cleared. + bucket.slot = -1; + size--; + } + else + { + // Add this bucket to its new slot. + int slot = hash(key); + bucket.slot = slot; + bucket.next = buckets[slot]; + buckets[slot] = bucket; + } + bucket = nextBucket; + } + } + } + + /** + * Finds the entry corresponding to key. Since it returns an Entry + * it will also prevent the key from being removed under us. + * @param key the key, may be null + * @return The WeakBucket.WeakEntry or null, if the key wasn't found. + */ + private WeakBucket.WeakEntry internalGet(Object key) + { + if (key == null) + key = NULL_KEY; + int slot = hash(key); + WeakBucket bucket = buckets[slot]; + while (bucket != null) + { + WeakBucket.WeakEntry entry = bucket.getEntry(); + if (entry != null && key == entry.key) + return entry; + + bucket = bucket.next; + } + return null; + } + + /** + * Adds a new key/value pair to the hash map. + * @param key the key. This mustn't exists in the map. It may be null. + * @param value the value. + */ + private void internalAdd(Object key, Object value) + { + if (key == null) + key = NULL_KEY; + int slot = hash(key); + WeakBucket bucket = new WeakBucket(key, queue, value, slot); + bucket.next = buckets[slot]; + buckets[slot] = bucket; + size++; + } + + /** + * Removes a bucket from this hash map, if it wasn't removed before + * (e.g. one time through rehashing and one time through reference queue). + * Package visible for use in nested classes. + * + * @param bucket the bucket to remove. + */ + void internalRemove(WeakBucket bucket) + { + int slot = bucket.slot; + if (slot == -1) + // This bucket was already removed. + return; + + // Mark the bucket as removed. This is necessary, since the + // bucket may be enqueued later by the garbage collection, and + // internalRemove will be called a second time. + bucket.slot = -1; + + WeakBucket prev = null; + WeakBucket next = buckets[slot]; + while (next != bucket) + { + if (next == null) + throw new InternalError("WeakIdentityHashMap in inconsistent state"); + prev = next; + next = prev.next; + } + if (prev == null) + buckets[slot] = bucket.next; + else + prev.next = bucket.next; + + size--; + } + + /** + * Returns the size of this hash map. Note that the size() may shrink + * spontaneously, if the some of the keys were only weakly reachable. + * @return the number of entries in this hash map. + */ + public int size() + { + cleanQueue(); + return size; + } + + /** + * Tells if the map is empty. Note that the result may change + * spontanously, if all of the keys were only weakly reachable. + * @return true, iff the map is empty. + */ + public boolean isEmpty() + { + cleanQueue(); + return size == 0; + } + + /** + * Tells if the map contains the given key. Note that the result + * may change spontanously, if the key was only weakly + * reachable. + * @param key the key to look for + * @return true, iff the map contains an entry for the given key. + */ + public boolean containsKey(Object key) + { + cleanQueue(); + return internalGet(key) != null; + } + + /** + * Gets the value the key is mapped to. + * @return the value the key was mapped to. It returns null if + * the key wasn't in this map, or if the mapped value was + * explicitly set to null. + */ + public Object get(Object key) + { + cleanQueue(); + WeakBucket.WeakEntry entry = internalGet(key); + return entry == null ? null : entry.getValue(); + } + + /** + * Adds a new key/value mapping to this map. + * @param key the key, may be null + * @param value the value, may be null + * @return the value the key was mapped to previously. It returns + * null if the key wasn't in this map, or if the mapped value + * was explicitly set to null. + */ + public Object put(Object key, Object value) + { + cleanQueue(); + WeakBucket.WeakEntry entry = internalGet(key); + if (entry != null) + return entry.setValue(value); + + modCount++; + if (size >= threshold) + rehash(); + + internalAdd(key, value); + return null; + } + + /** + * Removes the key and the corresponding value from this map. + * @param key the key. This may be null. + * @return the value the key was mapped to previously. It returns + * null if the key wasn't in this map, or if the mapped value was + * explicitly set to null. + */ + public Object remove(Object key) + { + cleanQueue(); + WeakBucket.WeakEntry entry = internalGet(key); + if (entry == null) + return null; + + modCount++; + internalRemove(entry.getBucket()); + return entry.getValue(); + } + + /** + * Returns a set representation of the entries in this map. This + * set will not have strong references to the keys, so they can be + * silently removed. The returned set has therefore the same + * strange behaviour (shrinking size(), disappearing entries) as + * this weak hash map. + * @return a set representation of the entries. + */ + public Set entrySet() + { + cleanQueue(); + return theEntrySet; + } + + /** + * Clears all entries from this map. + */ + public void clear() + { + super.clear(); + } + + /** + * Returns true if the map contains at least one key which points to + * the specified object as a value. Note that the result + * may change spontanously, if its key was only weakly reachable. + * @param value the value to search for + * @return true if it is found in the set. + */ + public boolean containsValue(Object value) + { + cleanQueue(); + return super.containsValue(value); + } + + /** + * Returns a set representation of the keys in this map. This + * set will not have strong references to the keys, so they can be + * silently removed. The returned set has therefore the same + * strange behaviour (shrinking size(), disappearing entries) as + * this weak hash map. + * @return a set representation of the keys. + */ + public Set keySet() + { + cleanQueue(); + return super.keySet(); + } + + /** + * Puts all of the mappings from the given map into this one. If the + * key already exists in this map, its value is replaced. + * @param m the map to copy in + */ + public void putAll(Map m) + { + super.putAll(m); + } + + /** + * Returns a collection representation of the values in this map. This + * collection will not have strong references to the keys, so mappings + * can be silently removed. The returned collection has therefore the same + * strange behaviour (shrinking size(), disappearing entries) as + * this weak hash map. + * @return a collection representation of the values. + */ + public Collection values() + { + cleanQueue(); + return super.values(); + } +} // class WeakIdentityHashMap diff --git a/libjava/classpath/gnu/regexp/RE.java b/libjava/classpath/gnu/regexp/RE.java index 16427c7566d..9ac9b53d1a9 100644 --- a/libjava/classpath/gnu/regexp/RE.java +++ b/libjava/classpath/gnu/regexp/RE.java @@ -374,6 +374,9 @@ public class RE extends REToken { // // OPEN QUESTION: // what is proper interpretation of '{' at start of string? + // + // This method used to check "repeat.empty.token" to avoid such regexp + // as "(a*){2,}", but now "repeat.empty.token" is allowed. else if ((unit.ch == '{') && syntax.get(RESyntax.RE_INTERVALS) && (syntax.get(RESyntax.RE_NO_BK_BRACES) ^ (unit.bk || quot))) { int newIndex = getMinMax(pattern,index,minMax,syntax); @@ -386,8 +389,6 @@ public class RE extends REToken { throw new REException(getLocalizedMessage("repeat.chained"),REException.REG_BADRPT,newIndex); if (currentToken instanceof RETokenWordBoundary || currentToken instanceof RETokenWordBoundary) throw new REException(getLocalizedMessage("repeat.assertion"),REException.REG_BADRPT,newIndex); - if ((currentToken.getMinimumLength() == 0) && (minMax.second == Integer.MAX_VALUE)) - throw new REException(getLocalizedMessage("repeat.empty.token"),REException.REG_BADRPT,newIndex); index = newIndex; currentToken = setRepeated(currentToken,minMax.first,minMax.second,index); } @@ -550,13 +551,50 @@ public class RE extends REToken { int nested = 0; while ( ((nextIndex = getCharUnit(pattern,endIndex,unit,false)) > 0) - && !(nested == 0 && (unit.ch == ')') && (syntax.get(RESyntax.RE_NO_BK_PARENS) ^ (unit.bk || quot))) ) + && !(nested == 0 && (unit.ch == ')') && (syntax.get(RESyntax.RE_NO_BK_PARENS) ^ (unit.bk || quot))) ) { if ((endIndex = nextIndex) >= pLength) throw new REException(getLocalizedMessage("subexpr.no.end"),REException.REG_ESUBREG,nextIndex); + else if ((unit.ch == '[') && !(unit.bk || quot)) { + // I hate to do something similar to the LIST OPERATOR matters + // above, but ... + int listIndex = nextIndex; + if (listIndex < pLength && pattern[listIndex] == '^') listIndex++; + if (listIndex < pLength && pattern[listIndex] == ']') listIndex++; + int listEndIndex = -1; + int listNest = 0; + while (listIndex < pLength && listEndIndex < 0) { + switch(pattern[listIndex++]) { + case '\\': + listIndex++; + break; + case '[': + // Sun's API document says that regexp like "[a-d[m-p]]" + // is legal. Even something like "[[[^]]]]" is accepted. + listNest++; + if (listIndex < pLength && pattern[listIndex] == '^') listIndex++; + if (listIndex < pLength && pattern[listIndex] == ']') listIndex++; + break; + case ']': + if (listNest == 0) + listEndIndex = listIndex; + listNest--; + break; + } + } + if (listEndIndex >= 0) { + nextIndex = listEndIndex; + if ((endIndex = nextIndex) >= pLength) + throw new REException(getLocalizedMessage("subexpr.no.end"),REException.REG_ESUBREG,nextIndex); + else + continue; + } + throw new REException(getLocalizedMessage("subexpr.no.end"),REException.REG_ESUBREG,nextIndex); + } else if (unit.ch == '(' && (syntax.get(RESyntax.RE_NO_BK_PARENS) ^ (unit.bk || quot))) nested++; else if (unit.ch == ')' && (syntax.get(RESyntax.RE_NO_BK_PARENS) ^ (unit.bk || quot))) nested--; + } // endIndex is now position at a ')','\)' // nextIndex is end of string or position after ')' or '\)' @@ -616,6 +654,9 @@ public class RE extends REToken { // ZERO-OR-MORE REPEAT OPERATOR // * + // + // This method used to check "repeat.empty.token" to avoid such regexp + // as "(a*)*", but now "repeat.empty.token" is allowed. else if ((unit.ch == '*') && !(unit.bk || quot)) { if (currentToken == null) @@ -624,14 +665,15 @@ public class RE extends REToken { throw new REException(getLocalizedMessage("repeat.chained"),REException.REG_BADRPT,index); if (currentToken instanceof RETokenWordBoundary || currentToken instanceof RETokenWordBoundary) throw new REException(getLocalizedMessage("repeat.assertion"),REException.REG_BADRPT,index); - if (currentToken.getMinimumLength() == 0) - throw new REException(getLocalizedMessage("repeat.empty.token"),REException.REG_BADRPT,index); currentToken = setRepeated(currentToken,0,Integer.MAX_VALUE,index); } // ONE-OR-MORE REPEAT OPERATOR / POSSESSIVE MATCHING OPERATOR // + | \+ depending on RE_BK_PLUS_QM // not available if RE_LIMITED_OPS is set + // + // This method used to check "repeat.empty.token" to avoid such regexp + // as "(a*)+", but now "repeat.empty.token" is allowed. else if ((unit.ch == '+') && !syntax.get(RESyntax.RE_LIMITED_OPS) && (!syntax.get(RESyntax.RE_BK_PLUS_QM) ^ (unit.bk || quot))) { if (currentToken == null) @@ -648,8 +690,6 @@ public class RE extends REToken { } else if (currentToken instanceof RETokenWordBoundary || currentToken instanceof RETokenWordBoundary) throw new REException(getLocalizedMessage("repeat.assertion"),REException.REG_BADRPT,index); - else if (currentToken.getMinimumLength() == 0) - throw new REException(getLocalizedMessage("repeat.empty.token"),REException.REG_BADRPT,index); else currentToken = setRepeated(currentToken,1,Integer.MAX_VALUE,index); } diff --git a/libjava/classpath/gnu/regexp/RETokenRepeated.java b/libjava/classpath/gnu/regexp/RETokenRepeated.java index 3165a6f1654..6291a3c3960 100644 --- a/libjava/classpath/gnu/regexp/RETokenRepeated.java +++ b/libjava/classpath/gnu/regexp/RETokenRepeated.java @@ -108,6 +108,7 @@ final class RETokenRepeated extends REToken { REMatch doables; REMatch doablesLast; REMatch recurrent; + int lastIndex = mymatch.index; do { // Check for stingy match for each possibility. @@ -151,6 +152,15 @@ final class RETokenRepeated extends REToken { ++numRepeats; positions.addElement(newMatch); + + // doables.index == lastIndex means an empty string + // was the longest that matched this token. + // We break here, otherwise we will fall into an endless loop. + if (doables.index == lastIndex) { + if (numRepeats < min) numRepeats = min; + break; + } + lastIndex = doables.index; } while (numRepeats < max); // If there aren't enough repeats, then fail @@ -165,7 +175,16 @@ final class RETokenRepeated extends REToken { REMatch allResultsLast = null; REMatch results = null; - while (--posIndex >= min) { + int indexCount = posIndex - min; + if (indexCount <= 0) { + // This case occurs when we exited the previous do loop before + // numRepeats >= min because an empty string matched the token. + // In this case, an empty string can match as many times as + // desired. + indexCount = 1; + } + while (indexCount-- > 0) { + --posIndex; newMatch = (REMatch) positions.elementAt(posIndex); results = matchRest(input, newMatch); if (results != null) { diff --git a/libjava/classpath/gnu/xml/aelfred2/ContentHandler2.java b/libjava/classpath/gnu/xml/aelfred2/ContentHandler2.java deleted file mode 100644 index 15161383592..00000000000 --- a/libjava/classpath/gnu/xml/aelfred2/ContentHandler2.java +++ /dev/null @@ -1,65 +0,0 @@ -/* ContentHandler2.java -- - Copyright (C) 2004 Free Software Foundation, Inc. - -This file is part of GNU Classpath. - -GNU Classpath is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2, or (at your option) -any later version. - -GNU Classpath is distributed in the hope that it will be useful, but -WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -General Public License for more details. - -You should have received a copy of the GNU General Public License -along with GNU Classpath; see the file COPYING. If not, write to the -Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA -02110-1301 USA. - -Linking this library statically or dynamically with other modules is -making a combined work based on this library. Thus, the terms and -conditions of the GNU General Public License cover the whole -combination. - -As a special exception, the copyright holders of this library give you -permission to link this library with independent modules to produce an -executable, regardless of the license terms of these independent -modules, and to copy and distribute the resulting executable under -terms of your choice, provided that you also meet, for each linked -independent module, the terms and conditions of the license of that -module. An independent module is a module which is not derived from -or based on this library. If you modify this library, you may extend -this exception to your version of the library, but you are not -obligated to do so. If you do not wish to do so, delete this -exception statement from your version. */ - -package gnu.xml.aelfred2; - -import org.xml.sax.ContentHandler; -import org.xml.sax.SAXException; - -/** - * Extension to the SAX ContentHandler interface to report parsing events - * and parameters required by DOM Level 3 but not supported by SAX. - * - * @author Chris Burdess - */ -public interface ContentHandler2 - extends ContentHandler -{ - - /** - * Reports the XML declaration. - * @param version the value of the version attribute in the XML - * declaration - * @param encoding the encoding specified in the XML declaration, if any - * @param standalone the standalone attribute from the XML declaration - * @param inputEncoding the encoding of the XML input - */ - void xmlDecl(String version, String encoding, boolean standalone, - String inputEncoding) - throws SAXException; - -} diff --git a/libjava/classpath/gnu/xml/aelfred2/SAXDriver.java b/libjava/classpath/gnu/xml/aelfred2/SAXDriver.java index 6864ff65971..e7c4d5a3f64 100644 --- a/libjava/classpath/gnu/xml/aelfred2/SAXDriver.java +++ b/libjava/classpath/gnu/xml/aelfred2/SAXDriver.java @@ -655,21 +655,6 @@ final public class SAXDriver attributesList.clear(); } - void xmlDecl(String version, - String encoding, - boolean standalone, - String inputEncoding) - throws SAXException - { - if (contentHandler instanceof ContentHandler2) - { - ((ContentHandler2) contentHandler).xmlDecl(version, - encoding, - standalone, - inputEncoding); - } - } - void skippedEntity(String name) throws SAXException { diff --git a/libjava/classpath/gnu/xml/aelfred2/XmlParser.java b/libjava/classpath/gnu/xml/aelfred2/XmlParser.java index f2abb889666..ab2ed16f946 100644 --- a/libjava/classpath/gnu/xml/aelfred2/XmlParser.java +++ b/libjava/classpath/gnu/xml/aelfred2/XmlParser.java @@ -841,9 +841,6 @@ final class XmlParser { inputEncoding = encodingName; } - handler.xmlDecl(version, encodingName, docIsStandalone, - inputEncoding); - return encodingName; } diff --git a/libjava/classpath/gnu/xml/dom/Consumer.java b/libjava/classpath/gnu/xml/dom/Consumer.java index f99e221e998..026e2754ef9 100644 --- a/libjava/classpath/gnu/xml/dom/Consumer.java +++ b/libjava/classpath/gnu/xml/dom/Consumer.java @@ -301,22 +301,6 @@ public class Consumer extends DomConsumer doc.setBuilding(true); } - /** - * Required by DOM Level 3 to report document parameters - */ - public void xmlDecl(String version, - String encoding, - boolean standalone, - String inputEncoding) - throws SAXException - { - super.xmlDecl(version, encoding, standalone, inputEncoding); - - DomDocument doc = (DomDocument) getDocument(); - doc.setXmlEncoding(encoding); - doc.setInputEncoding(inputEncoding); - } - public void endDocument () throws SAXException { diff --git a/libjava/classpath/gnu/xml/dom/DomDocument.java b/libjava/classpath/gnu/xml/dom/DomDocument.java index 29b8dc72e53..900d03ac3dc 100644 --- a/libjava/classpath/gnu/xml/dom/DomDocument.java +++ b/libjava/classpath/gnu/xml/dom/DomDocument.java @@ -1313,6 +1313,31 @@ public class DomDocument return config; } + public boolean isEqualNode(Node arg) + { + if (!super.isEqualNode(arg)) + return false; + Document d = (Document) arg; + String dversion = d.getXmlVersion(); + if (dversion == null || !dversion.equals(version)) + return false; + boolean dstandalone = d.getXmlStandalone(); + if (dstandalone != standalone) + return false; + String dencoding = d.getXmlEncoding(); + if (dencoding == null || dencoding.equalsIgnoreCase("UTF-8")) + { + if (encoding != null && !encoding.equalsIgnoreCase("UTF-8")) + return false; + } + else + { + if (!dencoding.equals(encoding)) + return false; + } + return true; + } + public void normalizeDocument() { boolean save = building; diff --git a/libjava/classpath/gnu/xml/dom/DomElement.java b/libjava/classpath/gnu/xml/dom/DomElement.java index 34509f647bc..f55b084cc94 100644 --- a/libjava/classpath/gnu/xml/dom/DomElement.java +++ b/libjava/classpath/gnu/xml/dom/DomElement.java @@ -519,5 +519,38 @@ public class DomElement Attr attr = (Attr) attrs.getNamedItemNS(namespaceURI, localName); setIdAttributeNode(attr, isId); } + + public boolean isEqualNode(Node arg) + { + if (!super.isEqualNode(arg)) + return false; + getAttributes(); + NamedNodeMap argAttrs = arg.getAttributes(); + int len = argAttrs.getLength(); + if (argAttrs == null || (len != attributes.length)) + return false; + for (int i = 0; i < len; i++) + { + Node argCtx = argAttrs.item(i); + // Don't compare namespace nodes + if (XMLConstants.XMLNS_ATTRIBUTE_NS_URI + .equals(argCtx.getNamespaceURI())) + continue; + // Find corresponding attribute node + DomNode ctx = attributes.first; + for (; ctx != null; ctx = ctx.next) + { + if (XMLConstants.XMLNS_ATTRIBUTE_NS_URI + .equals(ctx.getNamespaceURI())) + continue; + if (!ctx.isEqualNode(argCtx)) + continue; + break; + } + if (ctx == null) + return false; // not found + } + return true; + } } diff --git a/libjava/classpath/gnu/xml/dom/DomNamedNodeMap.java b/libjava/classpath/gnu/xml/dom/DomNamedNodeMap.java index 6f224029ac3..e3f08e4b4b7 100644 --- a/libjava/classpath/gnu/xml/dom/DomNamedNodeMap.java +++ b/libjava/classpath/gnu/xml/dom/DomNamedNodeMap.java @@ -319,7 +319,7 @@ public class DomNamedNodeMap if (ns) { String tln = ctx.getLocalName(); - if (tln.equals(name)) + if (name != null && name.equals(tln)) { String tu = ctx.getNamespaceURI(); if ((tu == null && uri == null) || diff --git a/libjava/classpath/gnu/xml/dom/DomNode.java b/libjava/classpath/gnu/xml/dom/DomNode.java index 3f29fb1283d..93f7c6f64f3 100644 --- a/libjava/classpath/gnu/xml/dom/DomNode.java +++ b/libjava/classpath/gnu/xml/dom/DomNode.java @@ -38,6 +38,7 @@ exception statement from your version. */ package gnu.xml.dom; import java.util.HashMap; +import java.util.HashSet; import java.util.Iterator; import java.util.Map; @@ -149,7 +150,7 @@ public abstract class DomNode boolean readonly; // event registrations - private ListenerRecord[] listeners; + private HashSet listeners; private int nListeners; // DOM Level 3 userData dictionary. @@ -167,19 +168,6 @@ public abstract class DomNode */ public void compact() { - if (listeners != null && listeners.length != nListeners) - { - if (nListeners == 0) - { - listeners = null; - } - else - { - ListenerRecord[] l = new ListenerRecord[nListeners]; - System.arraycopy(listeners, 0, l, 0, nListeners); - listeners = l; - } - } } /** @@ -201,6 +189,7 @@ public abstract class DomNode } } this.owner = owner; + this.listeners = new HashSet(); } @@ -372,6 +361,16 @@ public abstract class DomNode return; } break; + case DOCUMENT_TYPE_NODE: + if (!owner.building) + break; + switch (childNodeType) + { + case COMMENT_NODE: + case PROCESSING_INSTRUCTION_NODE: + return; + } + break; } if (owner.checkingWellformedness) { @@ -960,12 +959,6 @@ public abstract class DomNode */ public void trimToSize() { - if (listeners != null && listeners.length != nListeners) - { - ListenerRecord[] newKids = new ListenerRecord[length]; - System.arraycopy(listeners, 0, newKids, 0, nListeners); - listeners = newKids; - } } /** @@ -1175,7 +1168,7 @@ public abstract class DomNode node.next = null; node.readonly = false; - node.listeners = null; + node.listeners = new HashSet(); node.nListeners = 0; return node; @@ -1264,16 +1257,17 @@ public abstract class DomNode elementName = name; matchAnyURI = "*".equals(uri); matchAnyName = "*".equals(name); - + DomNode.this.addEventListener("DOMNodeInserted", this, true); DomNode.this.addEventListener("DOMNodeRemoved", this, true); } void detach() { - current.detach(); + if (current != null) + current.detach(); current = null; - + DomNode.this.removeEventListener("DOMNodeInserted", this, true); DomNode.this.removeEventListener("DOMNodeRemoved", this, true); } @@ -1336,6 +1330,8 @@ public abstract class DomNode return; } + if (current != null) + current.detach(); current = null; } @@ -1354,6 +1350,7 @@ public abstract class DomNode lastIndex--; } Node ret = current.previousNode (); + current.detach(); current = null; return ret; } @@ -1361,9 +1358,11 @@ public abstract class DomNode // somewhere after last node while (++lastIndex != index) current.nextNode (); - Node ret = current.nextNode (); - current = null; - return ret; + + Node ret = current.nextNode (); + current.detach(); + current = null; + return ret; } public int getLength() @@ -1375,7 +1374,7 @@ public abstract class DomNode { retval++; } - current = null; + iter.detach(); return retval; } @@ -1403,13 +1402,18 @@ public abstract class DomNode this.useCapture = useCapture; } - boolean equals(ListenerRecord rec) + public boolean equals(Object o) { + ListenerRecord rec = (ListenerRecord)o; return listener == rec.listener && useCapture == rec.useCapture && type == rec.type; } + public int hashCode() + { + return listener.hashCode() ^ type.hashCode(); + } } /** @@ -1464,30 +1468,12 @@ public abstract class DomNode EventListener listener, boolean useCapture) { - if (listeners == null) - { - listeners = new ListenerRecord[1]; - } - else if (nListeners == listeners.length) - { - ListenerRecord[] newListeners = - new ListenerRecord[listeners.length + NKIDS_DELTA]; - System.arraycopy(listeners, 0, newListeners, 0, nListeners); - listeners = newListeners; - } - // prune duplicates ListenerRecord record; record = new ListenerRecord(type, listener, useCapture); - for (int i = 0; i < nListeners; i++) - { - if (record.equals(listeners[i])) - { - return; - } - } - listeners [nListeners++] = record; + listeners.add(record); + nListeners = listeners.size(); } // XXX this exception should be discarded from DOM @@ -1672,11 +1658,14 @@ public abstract class DomNode ListenerRecord[] notificationSet) { int count = 0; + Iterator iter; + + iter = current.listeners.iterator(); // do any of this set of listeners get notified? - for (int i = 0; i < current.nListeners; i++) + while (iter.hasNext()) { - ListenerRecord rec = current.listeners[i]; + ListenerRecord rec = (ListenerRecord)iter.next(); if (rec.useCapture != capture) { @@ -1697,6 +1686,7 @@ public abstract class DomNode } notificationSet[count++] = rec; } + iter = null; // Notify just those listeners e.currentNode = current; @@ -1704,18 +1694,21 @@ public abstract class DomNode { try { + iter = current.listeners.iterator(); // Late in the DOM CR process (3rd or 4th CR?) the // removeEventListener spec became asymmetric with respect // to addEventListener ... effect is now immediate. - for (int j = 0; j < current.nListeners; j++) + while (iter.hasNext()) { - if (current.listeners[j].equals(notificationSet[i])) + ListenerRecord rec = (ListenerRecord)iter.next(); + + if (rec.equals(notificationSet[i])) { notificationSet[i].listener.handleEvent(e); break; } } - + iter = null; } catch (Exception x) { @@ -1733,36 +1726,8 @@ public abstract class DomNode EventListener listener, boolean useCapture) { - for (int i = 0; i < nListeners; i++) - { - if (listeners[i].listener != listener) - { - continue; - } - if (listeners[i].useCapture != useCapture) - { - continue; - } - if (!listeners[i].type.equals(type)) - { - continue; - } - - if (nListeners == 1) - { - listeners = null; - nListeners = 0; - } - else - { - for (int j = i + 1; j < nListeners; j++) - { - listeners[i++] = listeners[j++]; - } - listeners[--nListeners] = null; - } - break; - } + listeners.remove(new ListenerRecord(type, listener, useCapture)); + nListeners = listeners.size(); // no exceptions reported } @@ -1779,10 +1744,15 @@ public abstract class DomNode readonly = false; for (DomNode ctx = first; ctx != null; ctx = ctx.next) { + boolean saved2 = ctx.readonly; + ctx.readonly = false; switch (ctx.nodeType) { case TEXT_NODE: - while (ctx.next != null && ctx.next.nodeType == TEXT_NODE) + case CDATA_SECTION_NODE: + while (ctx.next != null && + (ctx.next.nodeType == TEXT_NODE || + ctx.next.nodeType == CDATA_SECTION_NODE)) { Text text = (Text) ctx; text.appendData(ctx.next.getNodeValue()); @@ -1794,7 +1764,11 @@ public abstract class DomNode int len = attrs.getLength(); for (int i = 0; i < len; i++) { - attrs.item(i).normalize(); + DomNode attr = (DomNode) attrs.item(i); + boolean saved3 = attr.readonly; + attr.readonly = false; + attr.normalize(); + attr.readonly = saved3; } // Fall through case DOCUMENT_NODE: @@ -1804,6 +1778,7 @@ public abstract class DomNode ctx.normalize(); break; } + ctx.readonly = saved2; } readonly = saved; } @@ -2017,39 +1992,56 @@ public abstract class DomNode public boolean isEqualNode(Node arg) { if (this == arg) - { - return true; - } + return true; if (arg == null) + return false; + if (nodeType != arg.getNodeType()) + return false; + switch (nodeType) { - return false; - } - if (nodeType != arg.getNodeType() || - !equal(getNodeName(), arg.getNodeName()) || - !equal(getLocalName(), arg.getLocalName()) || - !equal(getNamespaceURI(), arg.getNamespaceURI()) || - !equal(getPrefix(), arg.getPrefix()) || - !equal(getNodeValue(), arg.getNodeValue())) - { - return false; + case ELEMENT_NODE: + case ATTRIBUTE_NODE: + if (!equal(getLocalName(), arg.getLocalName()) || + !equal(getNamespaceURI(), arg.getNamespaceURI())) + return false; + break; + case PROCESSING_INSTRUCTION_NODE: + if (!equal(getNodeName(), arg.getNodeName()) || + !equal(getNodeValue(), arg.getNodeValue())) + return false; + break; + case COMMENT_NODE: + case TEXT_NODE: + case CDATA_SECTION_NODE: + if (!equal(getNodeValue(), arg.getNodeValue())) + return false; + break; } // Children Node argCtx = arg.getFirstChild(); getFirstChild(); // because of DomAttr lazy children - for (DomNode ctx = first; ctx != null; ctx = ctx.next) + DomNode ctx = first; + for (; ctx != null && argCtx != null; ctx = ctx.next) { - if (!ctx.isEqualNode(argCtx)) + if (nodeType == DOCUMENT_NODE) { - return false; + // Ignore whitespace outside document element + while (ctx != null && ctx.nodeType == TEXT_NODE) + ctx = ctx.next; + while (argCtx != null && ctx.getNodeType() == TEXT_NODE) + argCtx = argCtx.getNextSibling(); + if (ctx == null && argCtx != null) + return false; + else if (argCtx == null && ctx != null) + return false; } + if (!ctx.isEqualNode(argCtx)) + return false; argCtx = argCtx.getNextSibling(); } - if (argCtx != null) - { - return false; - } + if (ctx != null || argCtx != null) + return false; - // TODO Attr NamedNodeMap // TODO DocumentType return true; } @@ -2185,5 +2177,14 @@ public abstract class DomNode } } + public void list(java.io.PrintStream out, int indent) + { + for (int i = 0; i < indent; i++) + out.print(" "); + out.println(toString()); + for (DomNode ctx = first; ctx != null; ctx = ctx.next) + ctx.list(out, indent + 1); + } + } diff --git a/libjava/classpath/gnu/xml/dom/ls/DomLSParser.java b/libjava/classpath/gnu/xml/dom/ls/DomLSParser.java index eb7c1c49f3a..7ac4cc749fc 100644 --- a/libjava/classpath/gnu/xml/dom/ls/DomLSParser.java +++ b/libjava/classpath/gnu/xml/dom/ls/DomLSParser.java @@ -260,6 +260,7 @@ public class DomLSParser eventSink.coalescing = coalescing; // get and configure reader XMLReader reader = getXMLReader(); + eventSink.reader = reader; try { reader.setContentHandler(eventSink); diff --git a/libjava/classpath/gnu/xml/dom/ls/SAXEventSink.java b/libjava/classpath/gnu/xml/dom/ls/SAXEventSink.java index a850460b1e2..aad5ac76e70 100644 --- a/libjava/classpath/gnu/xml/dom/ls/SAXEventSink.java +++ b/libjava/classpath/gnu/xml/dom/ls/SAXEventSink.java @@ -37,6 +37,7 @@ exception statement from your version. */ package gnu.xml.dom.ls; +import java.util.HashSet; import java.util.Iterator; import java.util.LinkedList; import java.util.List; @@ -45,20 +46,26 @@ import org.w3c.dom.Attr; import org.w3c.dom.DocumentType; import org.w3c.dom.Element; import org.w3c.dom.Entity; +import org.w3c.dom.EntityReference; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; import org.w3c.dom.Text; import org.xml.sax.Attributes; +import org.xml.sax.ContentHandler; import org.xml.sax.DTDHandler; import org.xml.sax.Locator; import org.xml.sax.SAXException; +import org.xml.sax.SAXNotRecognizedException; +import org.xml.sax.SAXNotSupportedException; +import org.xml.sax.XMLReader; import org.xml.sax.ext.Attributes2; import org.xml.sax.ext.DeclHandler; import org.xml.sax.ext.LexicalHandler; -import gnu.xml.aelfred2.ContentHandler2; +import org.xml.sax.ext.Locator2; import gnu.xml.dom.DomAttr; import gnu.xml.dom.DomDocument; import gnu.xml.dom.DomDoctype; +import gnu.xml.dom.DomNode; /** * A SAX content and lexical handler used to construct a DOM document. @@ -66,17 +73,28 @@ import gnu.xml.dom.DomDoctype; * @author Chris Burdess */ class SAXEventSink - implements ContentHandler2, LexicalHandler, DTDHandler, DeclHandler + implements ContentHandler, LexicalHandler, DTDHandler, DeclHandler { private static final String XMLNS_URI = XMLConstants.XMLNS_ATTRIBUTE_NS_URI; private static final String XMLNS_PREFIX = XMLConstants.XMLNS_ATTRIBUTE; + private static final HashSet PREDEFINED_ENTITIES = new HashSet(); + static + { + PREDEFINED_ENTITIES.add("amp"); + PREDEFINED_ENTITIES.add("lt"); + PREDEFINED_ENTITIES.add("gt"); + PREDEFINED_ENTITIES.add("quot"); + PREDEFINED_ENTITIES.add("apos"); + } boolean namespaceAware; boolean ignoreWhitespace; boolean expandEntityReferences; boolean ignoreComments; boolean coalescing; + + XMLReader reader; // reference back to the parser to get features DomDocument doc; // document being constructed Node ctx; // current context (parent node) @@ -110,20 +128,42 @@ class SAXEventSink doc.setStrictErrorChecking(false); doc.setBuilding(true); ctx = doc; - } - public void xmlDecl(String version, String encoding, boolean standalone, - String inputEncoding) - throws SAXException - { - if (interrupted) + final String FEATURES = "http://xml.org/sax/features/"; + final String PROPERTIES = "http://xml.org/sax/properties/"; + final String GNU_PROPERTIES = "http://gnu.org/sax/properties/"; + + boolean standalone = reader.getFeature(FEATURES + "is-standalone"); + doc.setXmlStandalone(standalone); + try + { + String version = (String) reader.getProperty(PROPERTIES + + "document-xml-version"); + doc.setXmlVersion(version); + } + catch (SAXNotRecognizedException e) + { + } + catch (SAXNotSupportedException e) + { + } + if (locator != null && locator instanceof Locator2) + { + String encoding = ((Locator2) locator).getEncoding(); + doc.setInputEncoding(encoding); + } + try + { + String encoding = (String) reader.getProperty(GNU_PROPERTIES + + "document-xml-encoding"); + doc.setXmlEncoding(encoding); + } + catch (SAXNotRecognizedException e) + { + } + catch (SAXNotSupportedException e) { - return; } - doc.setXmlVersion(version); - doc.setXmlEncoding(encoding); - doc.setXmlStandalone(standalone); - doc.setInputEncoding(inputEncoding); } public void endDocument() @@ -265,7 +305,7 @@ class SAXEventSink public void characters(char[] c, int off, int len) throws SAXException { - if (interrupted) + if (interrupted || len < 1) { return; } @@ -301,11 +341,8 @@ class SAXEventSink { return; } - if (!inDTD) - { - Node pi = createProcessingInstruction(target, data); - ctx.appendChild(pi); - } + Node pi = createProcessingInstruction(target, data); + ctx.appendChild(pi); } protected Node createProcessingInstruction(String target, String data) @@ -354,6 +391,8 @@ class SAXEventSink public void startEntity(String name) throws SAXException { + if (interrupted) + return; DocumentType doctype = doc.getDoctype(); if (doctype == null) { @@ -361,19 +400,9 @@ class SAXEventSink "reference to entity in undeclared doctype"); } if ("[dtd]".equals(name) || name.charAt(0) == '%') - { - // Ignore DTD and parameter entities - ctx = doctype; - return; - } - if ("lt".equals(name) || - "gt".equals(name) || - "amp".equals(name) || - "apos".equals(name) || - "quot".equals(name)) - { - return; - } + return; + if (PREDEFINED_ENTITIES.contains(name)) + return; // Get entity NamedNodeMap entities = doctype.getEntities(); Entity entity = (Entity) entities.getNamedItem(name); @@ -382,59 +411,47 @@ class SAXEventSink throw new SAXException("SAX parser error: " + "reference to undeclared entity: " + name); } - pushEntity(entity); + EntityReference ref = doc.createEntityReference(name); + // DomDocument populates with the entity replacement text, remove this + Node child = ref.getFirstChild(); + while (child != null) + { + Node nextChild = child.getNextSibling(); + ref.removeChild(child); + child = nextChild; + } + ctx.appendChild(ref); + ctx = ref; } public void endEntity(String name) throws SAXException { + if (interrupted) + return; if ("[dtd]".equals(name) || name.charAt(0) == '%') - { - // Ignore DTD and parameter entities - return; - } - if ("lt".equals(name) || - "gt".equals(name) || - "amp".equals(name) || - "apos".equals(name) || - "quot".equals(name)) - { - return; - } - // Get entity - Entity entity = popEntity(); - // TODO resolve external entities to ensure that entity has content + return; + if (PREDEFINED_ENTITIES.contains(name)) + return; + // Get entity reference + EntityReference ref = (EntityReference) ctx; + if (!ref.getNodeName().equals(name)) + throw new SAXException("expecting end of "+ref.getNodeName()+" entity"); + ctx = ctx.getParentNode(); + if (ref instanceof DomNode) + ((DomNode) ref).makeReadonly(); if (expandEntityReferences) { - // Get entity content - for (Node child = entity.getFirstChild(); child != null; - child = child.getNextSibling()) + // Move entity content from reference node onto context + Node child = ref.getFirstChild(); + while (child != null) { + Node nextChild = child.getNextSibling(); ctx.appendChild(child); + child = nextChild; } + ctx.removeChild(ref); } - else - { - Node entityReference = doc.createEntityReference(name); - ctx.appendChild(entityReference); - } - } - - void pushEntity(Node entity) - { - if (entityCtx == null) - { - entityCtx = new LinkedList(); - } - entityCtx.addLast(ctx); - ctx = entity; - } - - Entity popEntity() - { - Entity ret = (Entity) ctx; - ctx = (Node) entityCtx.removeLast(); - return ret; } public void startCDATA() @@ -456,11 +473,8 @@ class SAXEventSink { return; } - if (!inDTD) - { - Node comment = createComment(c, off, len); - ctx.appendChild(comment); - } + Node comment = createComment(c, off, len); + ctx.appendChild(comment); } protected Node createComment(char[] c, int off, int len) @@ -477,6 +491,8 @@ class SAXEventSink { return; } + if (!inDTD) + throw new SAXException("notation decl outside DTD"); DomDoctype doctype = (DomDoctype) ctx; doctype.declareNotation(name, publicId, systemId); } @@ -489,6 +505,8 @@ class SAXEventSink { return; } + if (!inDTD) + throw new SAXException("unparsed entity decl outside DTD"); DomDoctype doctype = (DomDoctype) ctx; Entity entity = doctype.declareEntity(name, publicId, systemId, notationName); @@ -503,6 +521,8 @@ class SAXEventSink { return; } + if (!inDTD) + throw new SAXException("element decl outside DTD"); // Ignore fake element declarations generated by ValidationConsumer. // If an element is not really declared in the DTD it will not be // declared in the document model. @@ -522,6 +542,8 @@ class SAXEventSink { return; } + if (!inDTD) + throw new SAXException("attribute decl outside DTD"); DomDoctype doctype = (DomDoctype) ctx; doctype.attributeDecl(eName, aName, type, mode, value); } @@ -533,6 +555,8 @@ class SAXEventSink { return; } + if (!inDTD) + throw new SAXException("internal entity decl outside DTD"); DomDoctype doctype = (DomDoctype) ctx; Entity entity = doctype.declareEntity(name, null, null, null); if (entity != null) @@ -549,6 +573,8 @@ class SAXEventSink { return; } + if (!inDTD) + throw new SAXException("external entity decl outside DTD"); DomDoctype doctype = (DomDoctype) ctx; Entity entity = doctype.declareEntity(name, publicId, systemId, null); } diff --git a/libjava/classpath/gnu/xml/pipeline/DomConsumer.java b/libjava/classpath/gnu/xml/pipeline/DomConsumer.java index 389e02bb387..bdbf9281880 100644 --- a/libjava/classpath/gnu/xml/pipeline/DomConsumer.java +++ b/libjava/classpath/gnu/xml/pipeline/DomConsumer.java @@ -37,7 +37,6 @@ exception statement from your version. */ package gnu.xml.pipeline; -import gnu.xml.aelfred2.ContentHandler2; import gnu.xml.util.DomParser; import org.xml.sax.Attributes; @@ -330,7 +329,7 @@ public class DomConsumer implements EventConsumer * accepted illegal input data).

*/ public static class Handler - implements ContentHandler2, LexicalHandler, + implements ContentHandler, LexicalHandler, DTDHandler, DeclHandler { protected DomConsumer consumer; @@ -430,20 +429,6 @@ public class DomConsumer implements EventConsumer top = document; } - // ContentHandler2 - public void xmlDecl(String version, - String encoding, - boolean standalone, - String inputEncoding) - throws SAXException - { - if (document != null) - { - document.setXmlVersion(version); - document.setXmlStandalone(standalone); - } - } - // SAX1 public void endDocument () throws SAXException diff --git a/libjava/classpath/gnu/xml/pipeline/EventFilter.java b/libjava/classpath/gnu/xml/pipeline/EventFilter.java index 6600271718a..a14fb340f12 100644 --- a/libjava/classpath/gnu/xml/pipeline/EventFilter.java +++ b/libjava/classpath/gnu/xml/pipeline/EventFilter.java @@ -44,8 +44,6 @@ import org.xml.sax.*; import org.xml.sax.ext.*; import org.xml.sax.helpers.XMLFilterImpl; -import gnu.xml.aelfred2.ContentHandler2; - /** * A customizable event consumer, used to assemble various kinds of filters * using SAX handlers and an optional second consumer. It can be constructed @@ -138,7 +136,7 @@ import gnu.xml.aelfred2.ContentHandler2; * @author David Brownell */ public class EventFilter - implements EventConsumer, ContentHandler2, DTDHandler, + implements EventConsumer, ContentHandler, DTDHandler, LexicalHandler, DeclHandler { // SAX handlers @@ -600,17 +598,6 @@ public class EventFilter docNext.startDocument (); } - public void xmlDecl(String version, String encoding, boolean standalone, - String inputEncoding) - throws SAXException - { - if (docNext != null && docNext instanceof ContentHandler2) - { - ((ContentHandler2) docNext).xmlDecl(version, encoding, standalone, - inputEncoding); - } - } - /** SAX2: passes this callback to the next consumer, if any */ public void skippedEntity (String name) throws SAXException { diff --git a/libjava/classpath/gnu/xml/stream/BufferedReader.java b/libjava/classpath/gnu/xml/stream/BufferedReader.java new file mode 100644 index 00000000000..f8287e823fd --- /dev/null +++ b/libjava/classpath/gnu/xml/stream/BufferedReader.java @@ -0,0 +1,198 @@ +/* BufferedReader.java -- + Copyright (C) 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.xml.stream; + +import java.io.IOException; +import java.io.Reader; + +/** + * A mark-capable buffered reader. + * + * @author Chris Burdess + */ +class BufferedReader + extends Reader +{ + + static final int DEFAULT_BUFFER_SIZE = 4096; + + final Reader in; + char[] buf; + int pos, count, markpos, marklimit, bufferSize; + + BufferedReader(Reader in) + { + this(in, DEFAULT_BUFFER_SIZE); + } + + BufferedReader(Reader in, int bufferSize) + { + if (bufferSize < 1) + throw new IllegalArgumentException(); + this.in = in; + this.bufferSize = bufferSize; + buf = new char[bufferSize]; + pos = count = bufferSize; + } + + public void close() + throws IOException + { + buf = null; + pos = count = 0; + markpos = -1; + in.close(); + } + + public void mark(int readlimit) + throws IOException + { + marklimit = readlimit; + markpos = pos; + } + + public boolean markSupported() + { + return true; + } + + public int read() + throws IOException + { + if (pos >= count && !refill()) + return -1; + return (int) buf[pos++]; + } + + public int read(char[] b) + throws IOException + { + return read(b, 0, b.length); + } + + public int read(char[] b, int off, int len) + throws IOException + { + if (off < 0 || len < 0 || b.length - off < len) + throw new IndexOutOfBoundsException(); + + if (len == 0) + return 0; + + if (pos >= count && !refill()) + return -1; + + int ret = Math.min(count - pos, len); + System.arraycopy(buf, pos, b, off, ret); + pos += ret; + off += ret; + len -= ret; + + while (len > 0 && refill()) + { + int remain = Math.min(count - pos, len); + System.arraycopy(buf, pos, b, off, remain); + pos += remain; + off += remain; + len -= remain; + ret += remain; + } + + return ret; + } + + public void reset() + throws IOException + { + if (markpos == -1) + throw new IOException(buf == null ? "Stream closed." : "Invalid mark."); + pos = markpos; + } + + public long skip(long n) + throws IOException + { + if (buf == null) + throw new IOException("Stream closed."); + final long origN = n; + while (n > 0L) + { + if (pos >= count && !refill()) + break; + int numread = (int) Math.min((long) (count - pos), n); + pos += numread; + n -= numread; + } + return origN - n; + } + + private boolean refill() + throws IOException + { + if (buf == null) + throw new IOException("Stream closed."); + + int markcount = count - markpos; + if (markpos == -1 || markcount >= marklimit) + { + markpos = -1; + pos = count = 0; + } + else + { + char[] newbuf = buf; + if (markpos < bufferSize) + { + newbuf = new char[count - markpos + bufferSize]; + } + System.arraycopy(buf, markpos, newbuf, 0, markcount); + buf = newbuf; + count = markcount; + pos -= markpos; + markpos = 0; + } + + int numread = in.read(buf, count, bufferSize); + if (numread <= 0) + return false; + + count += numread; + return true; + } + +} diff --git a/libjava/classpath/gnu/xml/stream/CRLFReader.java b/libjava/classpath/gnu/xml/stream/CRLFReader.java new file mode 100644 index 00000000000..1d214ce52c1 --- /dev/null +++ b/libjava/classpath/gnu/xml/stream/CRLFReader.java @@ -0,0 +1,180 @@ +/* CRLFReader.java -- + Copyright (C) 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.xml.stream; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.Reader; + +/** + * Filtered reader that normalizes CRLF pairs into LFs. + * + * @author Chris Burdess + */ +class CRLFReader + extends Reader +{ + + /** + * The CR octet. + */ + public static final int CR = 13; + + /** + * The LF octet. + */ + public static final int LF = 10; + + private boolean doReset; + + protected Reader in; + + /** + * Constructor. + */ + protected CRLFReader(Reader in) + { + if (!in.markSupported()) + in = new BufferedReader(in); + this.in = in; + } + + public int read() + throws IOException + { + int c = in.read(); + if (c == 13) // CR + { + in.mark(1); + int d = in.read(); + if (d == 10) // LF + c = d; + else + in.reset(); + } + return c; + } + + public int read(char[] b) + throws IOException + { + return read(b, 0, b.length); + } + + public int read(char[] b, int off, int len) + throws IOException + { + in.mark(len + 1); + int l = in.read(b, off, len); + if (l > 0) + { + int i = indexOfCRLF(b, off, l); + if (doReset) + { + in.reset(); + if (i != -1) + { + l = in.read(b, off, (i + 1) - off); // read to CR + in.read(); // skip LF + b[i] = '\n'; // fix CR as LF + } + else + l = in.read(b, off, len); // CR(s) but no LF + } + } + return l; + } + + public boolean markSupported() + { + return in.markSupported(); + } + + public void mark(int limit) + throws IOException + { + in.mark(limit); + } + + public void reset() + throws IOException + { + in.reset(); + } + + public long skip(long n) + throws IOException + { + return in.skip(n); + } + + public void close() + throws IOException + { + in.close(); + } + + private int indexOfCRLF(char[] b, int off, int len) + throws IOException + { + doReset = false; + int lm1 = len - 1; + for (int i = off; i < len; i++) + { + if (b[i] == '\r') // CR + { + int d; + if (i == lm1) + { + d = in.read(); + doReset = true; + } + else + d = b[i + 1]; + if (d == '\n') // LF + { + doReset = true; + return i; + } + } + } + return -1; + } + +} + diff --git a/libjava/classpath/gnu/xml/stream/SAXParser.java b/libjava/classpath/gnu/xml/stream/SAXParser.java new file mode 100644 index 00000000000..54c8b36244b --- /dev/null +++ b/libjava/classpath/gnu/xml/stream/SAXParser.java @@ -0,0 +1,966 @@ +/* SAXParser.java -- + Copyright (C) 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.xml.stream; + +import java.io.InputStream; +import java.io.IOException; +import java.io.Reader; +import java.net.URL; +import java.util.Iterator; +import java.util.Map; +import javax.xml.XMLConstants; +import javax.xml.namespace.QName; +import javax.xml.stream.Location; +import javax.xml.stream.XMLEventReader; +import javax.xml.stream.XMLReporter; +import javax.xml.stream.XMLResolver; +import javax.xml.stream.XMLStreamConstants; +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.XMLStreamReader; + +import org.xml.sax.ContentHandler; +import org.xml.sax.DTDHandler; +import org.xml.sax.EntityResolver; +import org.xml.sax.ErrorHandler; +import org.xml.sax.InputSource; +import org.xml.sax.Parser; +import org.xml.sax.SAXException; +import org.xml.sax.SAXNotRecognizedException; +import org.xml.sax.SAXNotSupportedException; +import org.xml.sax.SAXParseException; +import org.xml.sax.XMLReader; +import org.xml.sax.ext.Attributes2; +import org.xml.sax.ext.DeclHandler; +import org.xml.sax.ext.EntityResolver2; +import org.xml.sax.ext.LexicalHandler; +import org.xml.sax.ext.Locator2; + +/** + * JAXP SAX parser using an underlying StAX parser. + * This parser supports the following additional SAX features and + * properties: + * + * + * + * + * + * + * + * + * + * + * + * + *
Features
http://gnu.org/sax/features/xml-baseread/writeIndicates or sets whether XML Base processing is enabled
Properties
http://gnu.org/sax/properties/base-uriread-onlyStringReturns the base URI of the current event
http://gnu.org/sax/properties/document-xml-encodingread-onlyStringReturns the encoding specified in the XML declaration
+ * + * @author Chris Burdess + */ +public class SAXParser + extends javax.xml.parsers.SAXParser + implements XMLReader, Attributes2, Locator2, XMLReporter, + XMLParser.XMLResolver2 +{ + + ContentHandler contentHandler; + DeclHandler declHandler; + DTDHandler dtdHandler; + EntityResolver entityResolver; + ErrorHandler errorHandler; + LexicalHandler lexicalHandler; + + boolean validating = false; + boolean namespaceAware = true; + boolean xIncludeAware = false; + boolean stringInterning = true; + boolean coalescing = true; + boolean replaceERefs = true; + boolean externalEntities = true; + boolean supportDTD = true; + boolean baseAware = true; + + XMLParser parser; + XMLStreamReader reader; + String encoding; + String xmlVersion; + boolean xmlStandalone; + String xmlEncoding; + String baseURI; + + public SAXParser() + { + } + + SAXParser(boolean validating, boolean namespaceAware, boolean xIncludeAware) + { + this.validating = validating; + this.namespaceAware = namespaceAware; + this.xIncludeAware = xIncludeAware; + } + + // -- SAXParser -- + + public Parser getParser() + throws SAXException + { + return null; + } + + public XMLReader getXMLReader() + throws SAXException + { + return this; + } + + public boolean isNamespaceAware() + { + return namespaceAware; + } + + public boolean isValidating() + { + return validating; + } + + public void setProperty(String name, Object value) + throws SAXNotRecognizedException, SAXNotSupportedException + { + if (parser != null) + throw new IllegalStateException("parsing in progress"); + final String FEATURES = "http://xml.org/sax/features/"; + final String PROPERTIES = "http://xml.org/sax/properties/"; + final String GNU_FEATURES = "http://gnu.org/sax/features/"; + if ((FEATURES + "namespaces").equals(name)) + namespaceAware = Boolean.TRUE.equals(value); + else if ((FEATURES + "namespace-prefixes").equals(name)) + { + // NOOP + } + else if ((FEATURES + "string-interning").equals(name)) + stringInterning = Boolean.TRUE.equals(value); + else if ((FEATURES + "use-attributes2").equals(name)) + { + // NOOP + } + else if ((FEATURES + "validation").equals(name)) + validating = Boolean.TRUE.equals(value); + else if ((FEATURES + "external-general-entities").equals(name)) + externalEntities = Boolean.TRUE.equals(value); + else if ((FEATURES + "external-parameter-entities").equals(name)) + externalEntities = Boolean.TRUE.equals(value); + else if ((PROPERTIES + "declaration-handler").equals(name)) + declHandler = (DeclHandler) value; + else if ((PROPERTIES + "lexical-handler").equals(name)) + lexicalHandler = (LexicalHandler) value; + else if ((GNU_FEATURES + "xml-base").equals(name)) + baseAware = Boolean.TRUE.equals(value); + else + throw new SAXNotSupportedException(name); + } + + public Object getProperty(String name) + throws SAXNotRecognizedException, SAXNotSupportedException + { + final String FEATURES = "http://xml.org/sax/features/"; + final String PROPERTIES = "http://xml.org/sax/properties/"; + final String GNU_FEATURES = "http://gnu.org/sax/features/"; + final String GNU_PROPERTIES = "http://gnu.org/sax/properties/"; + if ((GNU_FEATURES + "base-uri").equals(name)) + return baseURI; + if ((FEATURES + "is-standalone").equals(name)) + return xmlStandalone ? Boolean.TRUE : Boolean.FALSE; + if ((FEATURES + "namespaces").equals(name)) + return namespaceAware ? Boolean.TRUE : Boolean.FALSE; + if ((FEATURES + "namespace-prefixes").equals(name)) + return Boolean.TRUE; + if ((FEATURES + "string-interning").equals(name)) + return stringInterning ? Boolean.TRUE : Boolean.FALSE; + if ((FEATURES + "use-attributes2").equals(name)) + return Boolean.TRUE; + if ((FEATURES + "use-locator2").equals(name)) + return Boolean.TRUE; + if ((FEATURES + "use-entity-resolver2").equals(name)) + return Boolean.FALSE; + if ((FEATURES + "validation").equals(name)) + return validating ? Boolean.TRUE : Boolean.FALSE; + if ((FEATURES + "external-general-entities").equals(name)) + return externalEntities ? Boolean.TRUE : Boolean.FALSE; + if ((FEATURES + "external-parameter-entities").equals(name)) + return externalEntities ? Boolean.TRUE : Boolean.FALSE; + if ((FEATURES + "xml-1.1").equals(name)) + return Boolean.TRUE; + if ((PROPERTIES + "declaration-handler").equals(name)) + return declHandler; + if ((PROPERTIES + "document-xml-version").equals(name)) + return xmlVersion; + if ((PROPERTIES + "lexical-handler").equals(name)) + return lexicalHandler; + if ((GNU_FEATURES + "xml-base").equals(name)) + return baseAware ? Boolean.TRUE : Boolean.FALSE; + if ((GNU_PROPERTIES + "document-xml-encoding").equals(name)) + return xmlEncoding; + throw new SAXNotSupportedException(name); + } + + public boolean isXIncludeAware() + { + return xIncludeAware; + } + + public void reset() + { + parser = null; + encoding = null; + xmlVersion = null; + xmlStandalone = false; + } + + // -- XMLReader -- + + public boolean getFeature(String name) + throws SAXNotRecognizedException, SAXNotSupportedException + { + Object ret = getProperty(name); + if (ret instanceof Boolean) + return ((Boolean) ret).booleanValue(); + throw new SAXNotSupportedException(name); + } + + public void setFeature(String name, boolean value) + throws SAXNotRecognizedException, SAXNotSupportedException + { + setProperty(name, value ? Boolean.TRUE : Boolean.FALSE); + } + + public void setEntityResolver(EntityResolver resolver) + { + entityResolver = resolver; + } + + public EntityResolver getEntityResolver() + { + return entityResolver; + } + + public void setDTDHandler(DTDHandler handler) + { + dtdHandler = handler; + } + + public DTDHandler getDTDHandler() + { + return dtdHandler; + } + + public void setContentHandler(ContentHandler handler) + { + contentHandler = handler; + } + + public ContentHandler getContentHandler() + { + return contentHandler; + } + + public void setErrorHandler(ErrorHandler handler) + { + errorHandler = handler; + } + + public ErrorHandler getErrorHandler() + { + return errorHandler; + } + + public synchronized void parse(InputSource input) + throws IOException, SAXException + { + reset(); + String systemId = input.getSystemId(); + InputStream in = input.getByteStream(); + boolean opened = false; + if (in != null) + parser = new XMLParser(in, systemId, + validating, + namespaceAware, + coalescing, + replaceERefs, + externalEntities, + supportDTD, + baseAware, + stringInterning, + this, + this); + else + { + Reader r = input.getCharacterStream(); + if (r != null) + parser = new XMLParser(r, systemId, + validating, + namespaceAware, + coalescing, + replaceERefs, + externalEntities, + supportDTD, + baseAware, + stringInterning, + this, + this); + } + if (parser == null) + { + if (systemId == null) + throw new SAXException("No stream or system ID specified"); + systemId = XMLParser.absolutize(null, systemId); + in = new URL(systemId).openStream(); + opened = true; + parser = new XMLParser(in, systemId, + validating, + namespaceAware, + coalescing, + replaceERefs, + externalEntities, + supportDTD, + baseAware, + stringInterning, + this, + this); + } + reader = parser; + baseURI = systemId; + + if (xIncludeAware) + reader = new XIncludeFilter(parser, systemId, namespaceAware, + validating, true); + + if (contentHandler != null) + contentHandler.setDocumentLocator(this); + boolean startDocumentDone = false; + try + { + while (parser.hasNext()) + { + int event = parser.next(); + if (baseAware) + baseURI = parser.getXMLBase(); + switch (event) + { + case XMLStreamConstants.CHARACTERS: + if (contentHandler != null) + { + char[] b = reader.getTextCharacters(); + contentHandler.characters(b, 0, b.length); + } + break; + case XMLStreamConstants.SPACE: + if (contentHandler != null) + { + char[] b = reader.getTextCharacters(); + if (isIgnorableWhitespace(parser, b, false)) + contentHandler.ignorableWhitespace(b, 0, b.length); + else + contentHandler.characters(b, 0, b.length); + } + break; + case XMLStreamConstants.CDATA: + if (lexicalHandler != null) + lexicalHandler.startCDATA(); + if (contentHandler != null) + { + char[] b = reader.getTextCharacters(); + if (isIgnorableWhitespace(parser, b, true)) + contentHandler.ignorableWhitespace(b, 0, b.length); + else + contentHandler.characters(b, 0, b.length); + } + if (lexicalHandler != null) + lexicalHandler.endCDATA(); + break; + case XMLStreamConstants.START_ELEMENT: + if (contentHandler != null) + { + QName name = reader.getName(); + String uri = name.getNamespaceURI(); + String localName = name.getLocalPart(); + String prefix = name.getPrefix(); + String qName = localName; + if (!"".equals(prefix)) + qName = prefix + ":" + localName; + if (!namespaceAware) + { + uri = ""; + localName = ""; + } + else + { + int nc = reader.getNamespaceCount(); + for (int i = 0; i < nc; i++) + { + String nsuri = reader.getNamespaceURI(i); + String nsprefix = reader.getNamespacePrefix(i); + if ("xml".equals(nsprefix)) + continue; + contentHandler.startPrefixMapping(nsprefix, nsuri); + } + } + contentHandler.startElement(uri, localName, qName, this); + } + break; + case XMLStreamConstants.END_ELEMENT: + if (contentHandler != null) + { + QName name = reader.getName(); + String uri = name.getNamespaceURI(); + String localName = name.getLocalPart(); + String prefix = name.getPrefix(); + String qName = localName; + if (!"".equals(prefix)) + qName = prefix + ":" + localName; + if (!namespaceAware) + { + uri = ""; + localName = ""; + } + contentHandler.endElement(uri, localName, qName); + if (namespaceAware) + { + int nc = reader.getNamespaceCount(); + for (int i = 0; i < nc; i++) + { + String nsprefix = reader.getNamespacePrefix(i); + if ("xml".equals(nsprefix)) + continue; + contentHandler.endPrefixMapping(nsprefix); + } + } + } + break; + case XMLStreamConstants.COMMENT: + if (lexicalHandler != null) + { + char[] b = reader.getTextCharacters(); + lexicalHandler.comment(b, 0, b.length); + } + break; + case XMLStreamConstants.PROCESSING_INSTRUCTION: + if (contentHandler != null) + { + String target = reader.getPITarget(); + String data = reader.getPIData(); + if (data == null) + data = ""; + contentHandler.processingInstruction(target, data); + } + break; + case XMLStreamConstants.START_ENTITY: + if (lexicalHandler != null) + { + String name = reader.getText(); + lexicalHandler.startEntity(name); + } + break; + case XMLStreamConstants.END_ENTITY: + if (lexicalHandler != null) + { + String name = reader.getText(); + lexicalHandler.endEntity(name); + } + break; + case XMLStreamConstants.START_DOCUMENT: + encoding = reader.getEncoding(); + xmlVersion = reader.getVersion(); + xmlStandalone = reader.isStandalone(); + xmlEncoding = reader.getCharacterEncodingScheme(); + if (contentHandler != null) + contentHandler.startDocument(); + startDocumentDone = true; + break; + case XMLStreamConstants.END_DOCUMENT: + if (contentHandler != null) + contentHandler.endDocument(); + break; + case XMLStreamConstants.DTD: + XMLParser.Doctype doctype = parser.doctype; + if (lexicalHandler != null) + { + String rootName = doctype.rootName; + String publicId = doctype.publicId; + String systemId2 = doctype.systemId; + lexicalHandler.startDTD(rootName, publicId, systemId2); + } + for (Iterator i = doctype.entryIterator(); i.hasNext(); ) + { + String entry = (String) i.next(); + char c = entry.charAt(0); + String name = entry.substring(1); + if ('E' == c) + { + // Element decl + if (declHandler != null) + { + XMLParser.ContentModel model = + doctype.getElementModel(name); + declHandler.elementDecl(name, model.text); + } + } + else if ('A' == c) + { + // Attlist decl + if (declHandler != null) + { + for (Iterator j = doctype.attlistIterator(name); + j.hasNext(); ) + { + Map.Entry att = (Map.Entry) j.next(); + String aname = (String) att.getKey(); + XMLParser.AttributeDecl decl = + (XMLParser.AttributeDecl) att.getValue(); + String type = decl.type; + String value = decl.value; + String mode = null; + switch (decl.valueType) + { + case XMLParser.ATTRIBUTE_DEFAULT_FIXED: + mode = "#FIXED"; + break; + case XMLParser.ATTRIBUTE_DEFAULT_REQUIRED: + mode = "#REQUIRED"; + break; + case XMLParser.ATTRIBUTE_DEFAULT_IMPLIED: + mode = "#IMPLIED"; + break; + } + declHandler.attributeDecl(name, aname, + type, mode, value); + } + } + } + else if ('e' == c) + { + // Entity decl + Object entity = doctype.getEntity(name); + if (entity instanceof String) + { + if (declHandler != null) + declHandler.internalEntityDecl(name, + (String) entity); + } + else + { + XMLParser.ExternalIds ids = + (XMLParser.ExternalIds) entity; + if (ids.notationName != null) + { + if (dtdHandler != null) + { + String pub = ids.publicId; + String url = ids.systemId; + String not = ids.notationName; + dtdHandler.unparsedEntityDecl(name, + pub, + url, + not); + } + } + else + { + if (declHandler != null) + { + String pub = ids.publicId; + String url = ids.systemId; + declHandler.externalEntityDecl(name, + pub, + url); + } + } + } + } + else if ('n' == c) + { + // Notation decl + if (dtdHandler != null) + { + XMLParser.ExternalIds ids = + doctype.getNotation(name); + String pub = ids.publicId; + String url = ids.systemId; + dtdHandler.notationDecl(name, pub, url); + } + } + else if ('c' == c) + { + // Comment + if (lexicalHandler != null) + { + String comment = doctype.getComment(name); + char[] b = comment.toCharArray(); + lexicalHandler.comment(b, 0, b.length); + } + } + else if ('p' == c) + { + // Processing instruction + if (contentHandler != null) + { + String[] pi = doctype.getPI(name); + String target = pi[0]; + String data = pi[1]; + if (data == null) + data = ""; + contentHandler.processingInstruction(target, data); + } + } + } + if (lexicalHandler != null) + lexicalHandler.endDTD(); + } + } + } + catch (XMLStreamException e) + { + if (!startDocumentDone && contentHandler != null) + contentHandler.startDocument(); + SAXParseException e2 = new SAXParseException(e.getMessage(), this); + e2.initCause(e); + if (errorHandler != null) + errorHandler.fatalError(e2); + if (contentHandler != null) + contentHandler.endDocument(); + throw e2; + } + finally + { + if (opened) + in.close(); + reset(); + } + } + + /** + * Indicates whether the specified characters are ignorable whitespace. + */ + private boolean isIgnorableWhitespace(XMLParser reader, char[] b, + boolean testCharacters) + throws XMLStreamException + { + XMLParser.Doctype doctype = reader.doctype; + if (doctype == null) + return false; + String currentElement = reader.getCurrentElement(); + // check for xml:space + int ac = reader.getAttributeCount(); + for (int i = 0; i < ac; i++) + { + QName aname = reader.getAttributeQName(i); + if ("space".equals(aname.getLocalPart()) && + XMLConstants.XML_NS_URI.equals(aname.getNamespaceURI())) + { + if ("preserve".equals(reader.getAttributeValue(i))) + return false; + } + } + XMLParser.ContentModel model = doctype.getElementModel(currentElement); + if (model == null || model.type != XMLParser.ContentModel.ELEMENT) + return false; + if (model.external && xmlStandalone) + return false; + boolean white = true; + if (testCharacters) + { + for (int i = 0; i < b.length; i++) + { + if (b[i] != ' ' && b[i] != '\t' && b[i] != '\n' && b[i] != '\r') + { + white = false; + break; + } + } + } + return white; + } + + public void parse(String systemId) + throws IOException, SAXException + { + parse(new InputSource(systemId)); + } + + // -- Attributes2 -- + + public int getIndex(String qName) + { + int len = reader.getAttributeCount(); + for (int i = 0; i < len; i++) + { + QName q = reader.getAttributeQName(i); + String localName = q.getLocalPart(); + String prefix = q.getPrefix(); + String qn = ("".equals(prefix)) ? localName : prefix + ":" + localName; + if (qName.equals(qn)) + return i; + } + return -1; + } + + public int getIndex(String uri, String localName) + { + int len = reader.getAttributeCount(); + for (int i = 0; i < len; i++) + { + QName q = reader.getAttributeQName(i); + String ln = q.getLocalPart(); + String u = q.getNamespaceURI(); + if (u == null && uri != null) + continue; + if (u != null && !u.equals(uri)) + continue; + if (ln.equals(localName)) + return i; + } + return -1; + } + + public int getLength() + { + return reader.getAttributeCount(); + } + + public String getLocalName(int index) + { + return reader.getAttributeName(index); + } + + public String getQName(int index) + { + QName q = reader.getAttributeQName(index); + String localName = q.getLocalPart(); + String prefix = q.getPrefix(); + return ("".equals(prefix)) ? localName : prefix + ":" + localName; + } + + public String getType(int index) + { + String ret = reader.getAttributeType(index); + // SAX doesn't permit ENUMERATION? + return ("ENUMERATION".equals(ret)) ? "NMTOKEN" : ret; + } + + public String getType(String qName) + { + int index = getIndex(qName); + return (index == -1) ? null : getType(index); + } + + public String getType(String uri, String localName) + { + int index = getIndex(uri, localName); + return (index == -1) ? null : getType(index); + } + + public String getURI(int index) + { + String ret = reader.getAttributeNamespace(index); + return (ret == null) ? "" : ret; + } + + public String getValue(int index) + { + return reader.getAttributeValue(index); + } + + public String getValue(String qName) + { + int index = getIndex(qName); + return (index == -1) ? null : getValue(index); + } + + public String getValue(String uri, String localName) + { + int index = getIndex(uri, localName); + return (index == -1) ? null : getValue(index); + } + + public boolean isDeclared(int index) + { + return parser.isAttributeDeclared(index); + } + + public boolean isDeclared(String qName) + { + int index = getIndex(qName); + return (index == -1) ? false : isDeclared(index); + } + + public boolean isDeclared(String uri, String localName) + { + int index = getIndex(uri, localName); + return (index == -1) ? false : isDeclared(index); + } + + public boolean isSpecified(int index) + { + return reader.isAttributeSpecified(index); + } + + public boolean isSpecified(String qName) + { + int index = getIndex(qName); + return (index == -1) ? false : isSpecified(index); + } + + public boolean isSpecified(String uri, String localName) + { + int index = getIndex(uri, localName); + return (index == -1) ? false : isSpecified(index); + } + + // -- Locator2 -- + + public int getColumnNumber() + { + Location l = reader.getLocation(); + return l.getColumnNumber(); + } + + public int getLineNumber() + { + Location l = reader.getLocation(); + return l.getLineNumber(); + } + + public String getPublicId() + { + return null; + } + + public String getSystemId() + { + Location l = reader.getLocation(); + return l.getLocationURI(); + } + + public String getEncoding() + { + return encoding; + } + + public String getXMLVersion() + { + return xmlVersion; + } + + // -- XMLResolver -- + + public InputStream resolve(String uri) + throws XMLStreamException + { + return resolve(null, uri); + } + + public InputStream resolve(String publicId, String systemId) + throws XMLStreamException + { + if (entityResolver != null) + { + try + { + InputSource input = + entityResolver.resolveEntity(publicId, systemId); + if (input != null) + return input.getByteStream(); + } + catch (SAXException e) + { + XMLStreamException e2 = new XMLStreamException(e.getMessage()); + e2.initCause(e); + throw e2; + } + catch (IOException e) + { + XMLStreamException e2 = new XMLStreamException(e.getMessage()); + e2.initCause(e); + throw e2; + } + } + return null; + } + + public XMLEventReader resolveAsXMLEventReader(String uri) + throws XMLStreamException + { + // unused + return null; + } + + public XMLStreamReader resolveAsXMLStreamReader(String uri) + throws XMLStreamException + { + // unused + return null; + } + + // -- XMLReporter -- + + public void report(String message, String errorType, + Object relatedInformation, Location location) + throws XMLStreamException + { + if (errorHandler != null) + { + try + { + errorHandler.warning(new SAXParseException(message, this)); + } + catch (SAXException e) + { + XMLStreamException e2 = new XMLStreamException(e.getMessage()); + e2.initCause(e); + throw e2; + } + } + } + + public static void main(String[] args) + throws Exception + { + SAXParser parser = new SAXParser(); + InputSource input = new InputSource(args[0]); + parser.parse(input, new org.xml.sax.helpers.DefaultHandler()); + + } + +} diff --git a/libjava/classpath/gnu/xml/stream/SAXParserFactory.java b/libjava/classpath/gnu/xml/stream/SAXParserFactory.java new file mode 100644 index 00000000000..a2ccea1e694 --- /dev/null +++ b/libjava/classpath/gnu/xml/stream/SAXParserFactory.java @@ -0,0 +1,104 @@ +/* SAXParserFactory.java -- + Copyright (C) 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.xml.stream; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; +import javax.xml.parsers.ParserConfigurationException; + +import org.xml.sax.SAXException; +import org.xml.sax.SAXNotRecognizedException; +import org.xml.sax.SAXNotSupportedException; + +/** + * SAX parser factory providing a SAX compatibility layer on top of StAX. + * + * @author Chris Burdess + */ +public class SAXParserFactory + extends javax.xml.parsers.SAXParserFactory +{ + + static final Set FEATURE_NAMES = new HashSet(); + static + { + FEATURE_NAMES.add("http://xml.org/sax/features/namespaces"); + FEATURE_NAMES.add("http://xml.org/sax/features/string-interning"); + FEATURE_NAMES.add("http://xml.org/sax/features/validation"); + } + + Map features = new HashMap(); + + public javax.xml.parsers.SAXParser newSAXParser() + throws ParserConfigurationException, SAXException + { + boolean validating = isValidating(); + boolean namespaceAware = isNamespaceAware(); + boolean xIncludeAware = isXIncludeAware(); + SAXParser ret = new SAXParser(validating, namespaceAware, xIncludeAware); + for (Iterator i = features.entrySet().iterator(); i.hasNext(); ) + { + Map.Entry entry = (Map.Entry) i.next(); + String name = (String) entry.getKey(); + Boolean value = (Boolean) entry.getValue(); + ret.setFeature(name, value.booleanValue()); + } + return ret; + } + + public void setFeature(String name, boolean value) + throws ParserConfigurationException, SAXNotRecognizedException, SAXNotSupportedException + { + if (!FEATURE_NAMES.contains(name)) + throw new SAXNotSupportedException(name); + features.put(name, value ? Boolean.TRUE : Boolean.FALSE); + } + + public boolean getFeature(String name) + throws ParserConfigurationException, SAXNotRecognizedException, SAXNotSupportedException + { + if (!FEATURE_NAMES.contains(name)) + throw new SAXNotSupportedException(name); + Boolean value = (Boolean) features.get(name); + return (value == null) ? false : value.booleanValue(); + } + +} diff --git a/libjava/classpath/gnu/xml/stream/UnicodeReader.java b/libjava/classpath/gnu/xml/stream/UnicodeReader.java new file mode 100644 index 00000000000..c38516c30f8 --- /dev/null +++ b/libjava/classpath/gnu/xml/stream/UnicodeReader.java @@ -0,0 +1,201 @@ +/* UnicodeReader.java -- + Copyright (C) 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.xml.stream; + +import java.io.IOException; +import java.io.Reader; + +/** + * A reader that converts UTF-16 characters to Unicode code points. + * + * @author Chris Burdess + */ +class UnicodeReader +{ + + final Reader in; + + UnicodeReader(Reader in) + { + this.in = in; + } + + public void mark(int limit) + throws IOException + { + in.mark(limit * 2); + } + + public void reset() + throws IOException + { + in.reset(); + } + + public int read() + throws IOException + { + int ret = in.read(); + if (ret == -1) + return ret; + if (ret >= 0xd800 && ret < 0xdc00) + { + // Unicode surrogate? + int low = in.read(); + if (low >= 0xdc00 && low < 0xe000) + ret = Character.toCodePoint((char) ret, (char) low); + else + throw new IOException("unpaired surrogate: U+" + + Integer.toHexString(ret)); + } + else if (ret >= 0xdc00 && ret < 0xe000) + throw new IOException("unpaired surrogate: U+" + + Integer.toHexString(ret)); + return ret; + } + + public int read(int[] buf, int off, int len) + throws IOException + { + if (len == 0) + return 0; + char[] b2 = new char[len]; + int ret = in.read(b2, 0, len); + if (ret <= 0) + return ret; + int l = ret - 1; + int i = 0, j = off; + for (; i < l; i++) + { + char c = b2[i]; + if (c >= 0xd800 && c < 0xdc00) + { + // Unicode surrogate? + char d = b2[i + 1]; + if (d >= 0xdc00 && d < 0xe000) + { + buf[j++] = Character.toCodePoint(c, d); + i++; + continue; + } + else + throw new IOException("unpaired surrogate: U+" + + Integer.toHexString(c)); + } + else if (c >= 0xdc00 && c < 0xe000) + throw new IOException("unpaired surrogate: U+" + + Integer.toHexString(c)); + buf[j++] = (int) c; + } + if (i == l) + { + // last char + char c = b2[l]; + if (c >= 0xd800 && c < 0xdc00) + { + int low = in.read(); + if (low >= 0xdc00 && low < 0xe000) + { + buf[j++] = Character.toCodePoint(c, (char) low); + return j; + } + else + throw new IOException("unpaired surrogate: U+" + + Integer.toHexString(c)); + } + else if (c >= 0xdc00 && c < 0xe000) + throw new IOException("unpaired surrogate: U+" + + Integer.toHexString(c)); + buf[j++] = (int) c; + } + return j; + } + + public void close() + throws IOException + { + in.close(); + } + + public static int[] toCodePointArray(String text) + throws IOException + { + char[] b2 = text.toCharArray(); + int[] buf = new int[b2.length]; + if (b2.length > 0) + { + int l = b2.length - 1; + int i = 0, j = 0; + for (; i < l; i++) + { + char c = b2[i]; + if (c >= 0xd800 && c < 0xdc00) + { + // Unicode surrogate? + char d = b2[i + 1]; + if (d >= 0xdc00 && d < 0xe000) + { + buf[j++] = Character.toCodePoint(c, d); + i++; + continue; + } + else + throw new IOException("unpaired surrogate: U+" + + Integer.toHexString(c)); + } + else if (c >= 0xdc00 && c < 0xe000) + throw new IOException("unpaired surrogate: U+" + + Integer.toHexString(c)); + buf[j++] = (int) c; + } + if (i == l) + { + // last char + buf[j++] = (int) b2[l]; + if (j < buf.length) + { + int[] buf2 = new int[j]; + System.arraycopy(buf, 0, buf2, 0, j); + buf = buf2; + } + } + } + return buf; + } + +} diff --git a/libjava/classpath/gnu/xml/stream/XIncludeFilter.java b/libjava/classpath/gnu/xml/stream/XIncludeFilter.java new file mode 100644 index 00000000000..e151ac69d3c --- /dev/null +++ b/libjava/classpath/gnu/xml/stream/XIncludeFilter.java @@ -0,0 +1,932 @@ +/* XIncludeFilter.java -- + Copyright (C) 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.xml.stream; + +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.IOException; +import java.io.Reader; +import java.net.HttpURLConnection; +import java.net.URL; +import java.net.URLConnection; +import java.util.HashSet; +import java.util.NoSuchElementException; +import java.util.StringTokenizer; +import javax.xml.namespace.QName; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.stream.XMLInputFactory; +import javax.xml.stream.XMLStreamConstants; +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.XMLStreamReader; +import javax.xml.stream.util.ReaderDelegate; + +import org.w3c.dom.Attr; +import org.w3c.dom.Document; +import org.w3c.dom.DOMImplementation; +import org.w3c.dom.NamedNodeMap; +import org.w3c.dom.Node; +import org.w3c.dom.ProcessingInstruction; +import org.w3c.dom.TypeInfo; +import org.w3c.dom.traversal.DocumentTraversal; +import org.w3c.dom.traversal.NodeFilter; +import org.w3c.dom.traversal.TreeWalker; +import org.w3c.dom.xpath.XPathEvaluator; +import org.w3c.dom.xpath.XPathNSResolver; +import org.w3c.dom.xpath.XPathResult; +import org.xml.sax.SAXException; + +/** + * StAX filter for performing XInclude processing. + * + * @see http://www.w3.org/TR/xinclude/ + * @see http://www.w3.org/TR/xptr-framework/ + * @see http://www.w3.org/TR/xptr-element/ + * + * @author Chris Burdess + */ +class XIncludeFilter + extends ReaderDelegate +{ + + static final String XINCLUDE_NS_URI = "http://www.w3.org/2001/XInclude"; + static final int SHOW_FLAGS = + NodeFilter.SHOW_CDATA_SECTION | + NodeFilter.SHOW_COMMENT | + NodeFilter.SHOW_ELEMENT | + NodeFilter.SHOW_ENTITY_REFERENCE | + NodeFilter.SHOW_PROCESSING_INSTRUCTION | + NodeFilter.SHOW_TEXT; + + final String systemId; + final boolean namespaceAware; + final boolean validating; + final boolean expandERefs; + String href; + int event; + boolean included; + XPathResult result; + int snapshotIndex; + Node current; + TreeWalker walker; + HashSet seen = new HashSet(); + boolean backtracking; + boolean lookahead; + + Reader includedText; + char[] buf; + int len = -1; + boolean inInclude, inFallback, seenFallback; + + DocumentBuilder builder; + + XIncludeFilter(XMLStreamReader reader, String systemId, + boolean namespaceAware, boolean validating, + boolean expandERefs) + { + super(reader); + this.systemId = XMLParser.absolutize(null, systemId); + this.namespaceAware = namespaceAware; + this.validating = validating; + this.expandERefs = expandERefs; + } + + public int getAttributeCount() + { + if (current != null) + { + NamedNodeMap attrs = current.getAttributes(); + return (attrs == null) ? 0 : attrs.getLength(); + } + return super.getAttributeCount(); + } + + public String getAttributeName(int index) + { + if (current != null) + { + NamedNodeMap attrs = current.getAttributes(); + if (attrs == null) + return null; + Node attr = attrs.item(index); + return attr.getLocalName(); + } + return super.getAttributeName(index); + } + + public String getAttributeNamespace(int index) + { + if (current != null) + { + NamedNodeMap attrs = current.getAttributes(); + if (attrs == null) + return null; + Node attr = attrs.item(index); + return attr.getNamespaceURI(); + } + return super.getAttributeNamespace(index); + } + + public String getAttributePrefix(int index) + { + if (current != null) + { + NamedNodeMap attrs = current.getAttributes(); + if (attrs == null) + return null; + Node attr = attrs.item(index); + return attr.getPrefix(); + } + return super.getAttributePrefix(index); + } + + public QName getAttributeQName(int index) + { + if (current != null) + { + NamedNodeMap attrs = current.getAttributes(); + if (attrs == null) + return null; + Node attr = attrs.item(index); + String localName = attr.getLocalName(); + String uri = attr.getNamespaceURI(); + String prefix = attr.getPrefix(); + return new QName(uri, localName, prefix); + } + return super.getAttributeQName(index); + } + + public String getAttributeType(int index) + { + if (current != null) + { + NamedNodeMap attrs = current.getAttributes(); + if (attrs == null) + return null; + Attr attr = (Attr) attrs.item(index); + TypeInfo ti = attr.getSchemaTypeInfo(); + return (ti == null) ? "CDATA" : ti.getTypeName(); + } + return super.getAttributeType(index); + } + + public boolean isAttributeSpecified(int index) + { + if (current != null) + { + NamedNodeMap attrs = current.getAttributes(); + if (attrs == null) + return false; + Attr attr = (Attr) attrs.item(index); + return attr.getSpecified(); + } + return super.isAttributeSpecified(index); + } + + public String getAttributeValue(int index) + { + if (current != null) + { + NamedNodeMap attrs = current.getAttributes(); + if (attrs == null) + return null; + Node attr = attrs.item(index); + return attr.getNodeValue(); + } + return super.getAttributeValue(index); + } + + public String getAttributeValue(String uri, String localName) + { + if (current != null) + { + NamedNodeMap attrs = current.getAttributes(); + if (attrs == null) + return null; + Node attr = attrs.getNamedItemNS(uri, localName); + return (attr == null) ? null : attr.getNodeValue(); + } + return super.getAttributeValue(uri, localName); + } + + public String getElementText() + throws XMLStreamException + { + if (current != null) + return current.getTextContent(); + return super.getElementText(); + } + + public int getEventType() + { + return event; + } + + public String getLocalName() + { + if (current != null) + return current.getLocalName(); + return super.getLocalName(); + } + + public QName getName() + { + if (current != null) + { + String localName = current.getLocalName(); + String uri = current.getNamespaceURI(); + String prefix = current.getPrefix(); + return new QName(uri, localName, prefix); + } + return super.getName(); + } + + public String getNamespaceURI() + { + if (current != null) + return current.getNamespaceURI(); + return super.getNamespaceURI(); + } + + // TODO namespaces + + public String getPIData() + { + if (current != null) + return ((ProcessingInstruction) current).getData(); + return super.getPIData(); + } + + public String getPITarget() + { + if (current != null) + return ((ProcessingInstruction) current).getTarget(); + return super.getPITarget(); + } + + public String getPrefix() + { + if (current != null) + return current.getPrefix(); + return super.getPrefix(); + } + + public String getText() + { + if (current != null) + return current.getNodeValue(); + if (walker != null) + { + Node n = walker.getCurrentNode(); + if (n != null) + return n.getTextContent(); + } + if (buf != null) + return new String(buf, 0, len); + return super.getText(); + } + + public char[] getTextCharacters() + { + if (current != null) + { + buf = current.getNodeValue().toCharArray(); + len = buf.length; + } + if (buf != null) + return buf; + return super.getTextCharacters(); + } + + public int getTextCharacters(int sourceStart, char[] target, + int targetStart, int length) + throws XMLStreamException + { + if (current != null) + { + buf = current.getNodeValue().toCharArray(); + len = buf.length; + } + if (buf != null) + { + int max = Math.min(len - sourceStart, length); + if (max > 0) + System.arraycopy(buf, sourceStart, target, targetStart, max); + return max; + } + return super.getTextCharacters(sourceStart, target, targetStart, length); + } + + public int getTextLength() + { + if (current != null) + { + buf = current.getNodeValue().toCharArray(); + len = buf.length; + } + if (buf != null) + return len; + return super.getTextLength(); + } + + public int getTextStart() + { + if (current != null) + { + buf = current.getNodeValue().toCharArray(); + len = buf.length; + } + if (buf != null) + return 0; + return super.getTextStart(); + } + + public boolean hasNext() + throws XMLStreamException + { + if (!lookahead) + { + try + { + next(); + } + catch (NoSuchElementException e) + { + event = -1; + } + lookahead = true; + } + return (event != -1); + } + + public int next() + throws XMLStreamException + { + if (lookahead) + { + lookahead = false; + return event; + } + buf = null; + len = 0; + if (walker != null) + { + Node c = walker.getCurrentNode(); + Node n = null; + if (c.getNodeType() == Node.ELEMENT_NODE) + { + boolean isStartElement = !seen.contains(c); + if (isStartElement) + { + seen.add(c); + current = c; + event = XMLStreamConstants.START_ELEMENT; + return event; + } + else if (backtracking) + { + n = walker.nextSibling(); + if (n != null) + backtracking = false; + } + else + { + n = walker.firstChild(); + if (n == null) + n = walker.nextSibling(); + } + } + else + { + n = walker.firstChild(); + if (n == null) + n = walker.nextSibling(); + } + if (n == null) + { + current = walker.parentNode(); + if (current != null && current.getNodeType() == Node.ELEMENT_NODE) + { + // end-element + backtracking = true; + event = XMLStreamConstants.END_ELEMENT; + return event; + } + else + { + walker = null; + current = null; + } + } + else + { + current = n; + switch (n.getNodeType()) + { + case Node.ELEMENT_NODE: + return next(); + case Node.TEXT_NODE: + String text = n.getNodeValue(); + buf = text.toCharArray(); + len = buf.length; + event = isSpace(buf, len) ? + XMLStreamConstants.SPACE : + XMLStreamConstants.CHARACTERS; + return event; + case Node.CDATA_SECTION_NODE: + event = XMLStreamConstants.CDATA; + return event; + case Node.COMMENT_NODE: + event = XMLStreamConstants.COMMENT; + return event; + case Node.PROCESSING_INSTRUCTION_NODE: + event = XMLStreamConstants.PROCESSING_INSTRUCTION; + return event; + case Node.ENTITY_REFERENCE_NODE: + event = XMLStreamConstants.ENTITY_REFERENCE; + return event; + default: + throw new IllegalStateException(); + } + } + } + if (result != null) + { + switch (result.getResultType()) + { + case XPathResult.BOOLEAN_TYPE: + boolean bval = result.getBooleanValue(); + String btext = bval ? "true" : "false"; + buf = btext.toCharArray(); + len = buf.length; + result = null; + event = XMLStreamConstants.CHARACTERS; + return event; + case XPathResult.NUMBER_TYPE: + double nval = result.getNumberValue(); + String ntext = new Double(nval).toString(); + buf = ntext.toCharArray(); + len = buf.length; + result = null; + event = XMLStreamConstants.CHARACTERS; + return event; + case XPathResult.STRING_TYPE: + String stext = result.getStringValue(); + buf = stext.toCharArray(); + len = buf.length; + result = null; + event = isSpace(buf, len) ? + XMLStreamConstants.SPACE : + XMLStreamConstants.CHARACTERS; + return event; + case XPathResult.ANY_UNORDERED_NODE_TYPE: + case XPathResult.FIRST_ORDERED_NODE_TYPE: + Node n1 = result.getSingleNodeValue(); + Document d1 = getDocument(n1); + walker = getDocumentTraversal(d1) + .createTreeWalker(n1, SHOW_FLAGS, null, expandERefs); + result = null; + return next(); + case XPathResult.ORDERED_NODE_ITERATOR_TYPE: + case XPathResult.UNORDERED_NODE_ITERATOR_TYPE: + Node n2 = result.iterateNext(); + if (n2 == null) + { + result = null; + return next(); + } + Document d2 = getDocument(n2); + walker = getDocumentTraversal(d2) + .createTreeWalker(n2, SHOW_FLAGS, null, expandERefs); + return next(); + case XPathResult.ORDERED_NODE_SNAPSHOT_TYPE: + case XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE: + Node n3 = result.snapshotItem(snapshotIndex++); + if (n3 == null) + { + result = null; + return next(); + } + Document d3 = getDocument(n3); + walker = getDocumentTraversal(d3) + .createTreeWalker(n3, SHOW_FLAGS, null, expandERefs); + return next(); + default: + throw new IllegalStateException(); + } + } + if (includedText != null) + { + // fill buffer + if (buf == null) + buf = new char[2048]; + try + { + len = includedText.read(buf, 0, buf.length); + if (len == -1) + { + includedText = null; + buf = null; + return next(); + } + // chars or space? + return (event = isSpace(buf, len) ? + XMLStreamConstants.SPACE : + XMLStreamConstants.CHARACTERS); + } + catch (IOException e) + { + XMLStreamException e2 = new XMLStreamException(e.getMessage()); + e2.initCause(e); + throw e2; + } + } + event = super.next(); + switch (event) + { + case XMLStreamConstants.START_ELEMENT: + String uri = getNamespaceURI(); + if (XINCLUDE_NS_URI.equals(uri)) + { + String localName = getLocalName(); + if ("include".equals(localName)) + { + href = getAttributeValue(null, "href"); + String parse = getAttributeValue(null, "parse"); + String xpointer = getAttributeValue(null, "xpointer"); + String encoding = getAttributeValue(null, "encoding"); + String accept = getAttributeValue(null, "accept"); + String acceptLanguage = getAttributeValue(null, + "accept-language"); + if (includeResource(href, parse, xpointer, encoding, + accept, acceptLanguage)) + { + // Skip to xi:include end-element event + int depth = 0; + while (depth >= 0) + { + event = super.next(); + switch (event) + { + case XMLStreamConstants.START_ELEMENT: + depth++; + break; + case XMLStreamConstants.END_ELEMENT: + depth--; + } + } + } + else + inInclude = true; + } + else if (inInclude && "fallback".equals(localName)) + { + if (!seenFallback) + inFallback = seenFallback = true; + else + throw new XMLStreamException("duplicate xi:fallback element"); + } + else if (inInclude) + { + throw new XMLStreamException("illegal xi element '" + + localName + "'"); + } + return next(); + } + break; + case XMLStreamConstants.END_ELEMENT: + String uri2 = getNamespaceURI(); + if (XINCLUDE_NS_URI.equals(uri2)) + { + String localName = getLocalName(); + if ("include".equals(localName)) + { + if (!seenFallback && included) + { + String msg = "Unable to read " + href + + " and no xi:fallback element present"; + throw new XMLStreamException(msg); + } + included = false; + href = null; + inInclude = inFallback = seenFallback = false; + } + else if ("fallback".equals(localName)) + inFallback = false; + return next(); + } + break; + } + if (inInclude && !inFallback) + return next(); + return event; + } + + boolean isSpace(char[] text, int len) + { + boolean space = true; + for (int i = 0; i < len; i++) + { + char c = text[i]; + if (c != ' ' && c != '\t' && c != '\n' && c != '\r') + { + space = false; + break; + } + } + return space; + } + + String getBaseURI() + { + String base = (String) getParent().getProperty("gnu.xml.stream.baseURI"); + return (base == null) ? systemId : base; + } + + boolean includeResource(String href, String parse, String xpointer, + String encoding, String accept, + String acceptLanguage) + { + included = false; + try + { + if (xpointer != null) + throw new XMLStreamException("xpointer attribute not yet supported"); + String base = getBaseURI(); + if (href == null || "".equals(href)) + href = base; + else + href = XMLParser.absolutize(base, href); + if (parse == null || "xml".equals(parse)) + { + seen.clear(); + result = null; + snapshotIndex = 0; + walker = null; + current = null; + backtracking = false; + + URLConnection connection = getURLConnection(href, accept, + acceptLanguage); + InputStream in = connection.getInputStream(); + Document doc = getDocumentBuilder().parse(in, href); + DocumentTraversal dt = getDocumentTraversal(doc); + if (xpointer == null) + { + result = null; + Node item = doc.getDocumentElement(); + walker = dt.createTreeWalker(item, SHOW_FLAGS, null, + expandERefs); + } + else + { + result = null; + snapshotIndex = 0; + walker = null; + // shorthand or scheme-based? + int lpi = xpointer.indexOf('('); + int rpi = xpointer.indexOf(')', lpi); + if (lpi != -1 && rpi != -1) + { + String scheme = xpointer.substring(0, lpi); + if ("element".equals(scheme)) + { + // element() scheme + String elementSchemeData = + xpointer.substring(lpi + 1, rpi); + Node item = doc; + int si = elementSchemeData.indexOf('/'); + if (si == -1) + { + if (elementSchemeData.length() > 0) + item = doc.getElementById(elementSchemeData); + } + else + { + if (si > 0) + { + String context = + elementSchemeData.substring(0, si); + item = doc.getElementById(context); + elementSchemeData = + elementSchemeData.substring(si + 1); + } + StringTokenizer st = + new StringTokenizer(elementSchemeData, "/"); + while (st.hasMoreTokens() && item != null) + { + int n = Integer.parseInt(st.nextToken()); + Node ctx = item.getFirstChild(); + int count = 1; + while (ctx != null && count++ < n) + ctx = ctx.getNextSibling(); + item = ctx; + } + } + walker = dt.createTreeWalker(item, SHOW_FLAGS, null, + expandERefs); + included = true; + } + else if ("xpointer".equals(scheme)) + { + xpointer = xpointer.substring(lpi + 1, rpi); + XPathEvaluator eval = getXPathEvaluator(doc); + XPathNSResolver resolver = eval.createNSResolver(doc); + result = + (XPathResult) eval.evaluate(xpointer, doc, + resolver, + XPathResult.ANY_TYPE, + null); + // TODO xpointer() scheme functions + included = true; + } + else + { + String msg = "Unknown XPointer scheme: " + scheme; + throw new XMLStreamException(msg); + } + } + else + { + Node item = doc.getElementById(xpointer); + walker = dt.createTreeWalker(item, SHOW_FLAGS, null, + expandERefs); + included = true; + } + } + } + else if ("text".equals(parse)) + { + URLConnection connection = getURLConnection(href, accept, + acceptLanguage); + InputStream in = connection.getInputStream(); + if (encoding == null) + { + encoding = connection.getContentEncoding(); + if (encoding == null) + { + String contentType = connection.getContentType(); + if (contentType != null) + encoding = getParameter(contentType, "charset"); + } + } + if (encoding == null) + includedText = new InputStreamReader(in, "UTF-8"); + else + includedText = new InputStreamReader(in, encoding); + included = true; + } + else + throw new XMLStreamException("value of 'parse' attribute must be "+ + "'xml' or 'text'"); + return true; + } + catch (IOException e) + { + return false; + } + catch (XMLStreamException e) + { + return false; + } + catch (SAXException e) + { + return false; + } + } + + URLConnection getURLConnection(String href, String accept, + String acceptLanguage) + throws IOException + { + URL url = new URL(href); + URLConnection connection = url.openConnection(); + if (connection instanceof HttpURLConnection) + { + HttpURLConnection http = (HttpURLConnection) connection; + http.setInstanceFollowRedirects(true); + if (accept != null) + http.setRequestProperty("Accept", accept); + if (acceptLanguage != null) + http.setRequestProperty("Accept-Language", acceptLanguage); + } + return connection; + } + + Document getDocument(Node node) + { + if (node.getNodeType() == Node.DOCUMENT_NODE) + return (Document) node; + return node.getOwnerDocument(); + } + + DocumentBuilder getDocumentBuilder() + throws XMLStreamException + { + if (builder == null) + { + try + { + DocumentBuilderFactory f = DocumentBuilderFactory.newInstance(); + f.setXIncludeAware(true); + f.setNamespaceAware(namespaceAware); + f.setValidating(validating); + builder = f.newDocumentBuilder(); + } + catch (ParserConfigurationException e) + { + XMLStreamException e2 = new XMLStreamException(e.getMessage()); + e2.initCause(e); + throw e2; + } + } + builder.reset(); + return builder; + } + + DocumentTraversal getDocumentTraversal(Document doc) + throws XMLStreamException + { + DOMImplementation dom = doc.getImplementation(); + if (!dom.hasFeature("Traversal", "2.0")) + throw new XMLStreamException("Traversal not supported"); + return (DocumentTraversal) doc; + } + + XPathEvaluator getXPathEvaluator(Document doc) + throws XMLStreamException + { + DOMImplementation dom = doc.getImplementation(); + if (!dom.hasFeature("XPath", "3.0")) + throw new XMLStreamException("XPath not supported"); + return (XPathEvaluator) doc; + } + + static String getParameter(String contentType, String name) + { + StringTokenizer st = new StringTokenizer(contentType, " ;"); + if (st.hasMoreTokens()) + st.nextToken(); + while (st.hasMoreTokens()) + { + String token = st.nextToken(); + int ei = token.indexOf('='); + if (ei != -1) + { + String key = token.substring(0, ei); + if (key.equals(name)) + { + String value = token.substring(ei + 1); + int len = value.length(); + if (len > 1 && + value.charAt(0) == '"' && + value.charAt(len - 1) == '"') + value = value.substring(1, len - 1); + else if (len > 1 && + value.charAt(0) == '\'' && + value.charAt(len - 1) == '\'') + value = value.substring(1, len - 1); + return value; + } + } + } + return null; + } + +} diff --git a/libjava/classpath/gnu/xml/stream/XMLInputFactoryImpl.java b/libjava/classpath/gnu/xml/stream/XMLInputFactoryImpl.java index c99f564b623..164774d8236 100644 --- a/libjava/classpath/gnu/xml/stream/XMLInputFactoryImpl.java +++ b/libjava/classpath/gnu/xml/stream/XMLInputFactoryImpl.java @@ -77,6 +77,9 @@ public class XMLInputFactoryImpl protected boolean replacingEntityReferences = true; protected boolean externalEntities = true; protected boolean supportDTD = true; + protected boolean xIncludeAware = false; + protected boolean baseAware = true; + protected boolean stringInterning = true; public XMLInputFactoryImpl() { @@ -86,11 +89,28 @@ public class XMLInputFactoryImpl public XMLStreamReader createXMLStreamReader(Reader reader) throws XMLStreamException { + /* return new XMLStreamReaderImpl(reader, null, null, resolver, reporter, validating, namespaceAware, coalescing, replacingEntityReferences, externalEntities, supportDTD); + */ + XMLParser ret = new XMLParser(reader, null, + validating, + namespaceAware, + coalescing, + replacingEntityReferences, + externalEntities, + supportDTD, + baseAware, + stringInterning, + reporter, + resolver); + if (xIncludeAware) + return new XIncludeFilter(ret, null, namespaceAware, validating, + replacingEntityReferences); + return ret; } public XMLStreamReader createXMLStreamReader(Source source) @@ -98,21 +118,51 @@ public class XMLInputFactoryImpl { String systemId = source.getSystemId(); InputStream in = getInputStream(source); - return new XMLStreamReaderImpl(in, null, systemId, + /*return new XMLStreamReaderImpl(in, null, systemId, resolver, reporter, validating, namespaceAware, coalescing, replacingEntityReferences, - externalEntities, supportDTD); + externalEntities, supportDTD);*/ + XMLParser ret = new XMLParser(in, systemId, + validating, + namespaceAware, + coalescing, + replacingEntityReferences, + externalEntities, + supportDTD, + baseAware, + stringInterning, + reporter, + resolver); + if (xIncludeAware) + return new XIncludeFilter(ret, systemId, namespaceAware, validating, + replacingEntityReferences); + return ret; } public XMLStreamReader createXMLStreamReader(InputStream in) throws XMLStreamException { - return new XMLStreamReaderImpl(in, null, null, + /*return new XMLStreamReaderImpl(in, null, null, resolver, reporter, validating, namespaceAware, coalescing, replacingEntityReferences, - externalEntities, supportDTD); + externalEntities, supportDTD);*/ + XMLParser ret = new XMLParser(in, null, + validating, + namespaceAware, + coalescing, + replacingEntityReferences, + externalEntities, + supportDTD, + baseAware, + stringInterning, + reporter, + resolver); + if (xIncludeAware) + return new XIncludeFilter(ret, null, namespaceAware, validating, + replacingEntityReferences); + return ret; } public XMLStreamReader createXMLStreamReader(InputStream in, String encoding) @@ -210,6 +260,12 @@ public class XMLInputFactoryImpl resolver = (XMLResolver) value; else if (name.equals(ALLOCATOR)) allocator = (XMLEventAllocator) value; + else if (name.equals("gnu.xml.stream.stringInterning")) + stringInterning = ((Boolean) value).booleanValue(); + else if (name.equals("gnu.xml.stream.baseAware")) + baseAware = ((Boolean) value).booleanValue(); + else if (name.equals("gnu.xml.stream.xIncludeAware")) + xIncludeAware = ((Boolean) value).booleanValue(); else throw new IllegalArgumentException(name); } @@ -235,6 +291,12 @@ public class XMLInputFactoryImpl return resolver; if (name.equals(ALLOCATOR)) return allocator; + if (name.equals("gnu.xml.stream.stringInterning")) + return stringInterning ? Boolean.TRUE : Boolean.FALSE; + if (name.equals("gnu.xml.stream.baseAware")) + return baseAware ? Boolean.TRUE : Boolean.FALSE; + if (name.equals("gnu.xml.stream.xIncludeAware")) + return xIncludeAware ? Boolean.TRUE : Boolean.FALSE; throw new IllegalArgumentException(name); } @@ -248,7 +310,10 @@ public class XMLInputFactoryImpl name.equals(SUPPORT_DTD) || name.equals(REPORTER) || name.equals(RESOLVER) || - name.equals(ALLOCATOR); + name.equals(ALLOCATOR) || + name.equals("gnu.xml.stream.stringInterning") || + name.equals("gnu.xml.stream.baseAware") || + name.equals("gnu.xml.stream.xIncludeAware"); } public void setEventAllocator(XMLEventAllocator allocator) diff --git a/libjava/classpath/gnu/xml/stream/XMLParser.java b/libjava/classpath/gnu/xml/stream/XMLParser.java new file mode 100644 index 00000000000..6f10b9303dc --- /dev/null +++ b/libjava/classpath/gnu/xml/stream/XMLParser.java @@ -0,0 +1,5266 @@ +/* XMLParser.java -- + Copyright (C) 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. + +Partly derived from code which carried the following notice: + + Copyright (c) 1997, 1998 by Microstar Software Ltd. + + AElfred is free for both commercial and non-commercial use and + redistribution, provided that Microstar's copyright and disclaimer are + retained intact. You are free to modify AElfred for your own use and + to redistribute AElfred with your modifications, provided that the + modifications are clearly documented. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + merchantability or fitness for a particular purpose. Please use it AT + YOUR OWN RISK. +*/ + +package gnu.xml.stream; + +import java.io.BufferedInputStream; +import java.io.EOFException; +import java.io.File; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.IOException; +import java.io.Reader; +import java.io.StringReader; +import java.io.UnsupportedEncodingException; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.LinkedList; +import java.util.Map; +import java.util.NoSuchElementException; +import java.util.StringTokenizer; + +import javax.xml.XMLConstants; +import javax.xml.namespace.NamespaceContext; +import javax.xml.namespace.QName; +import javax.xml.stream.Location; +import javax.xml.stream.XMLInputFactory; +import javax.xml.stream.XMLReporter; +import javax.xml.stream.XMLResolver; +import javax.xml.stream.XMLStreamConstants; +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.XMLStreamReader; + +import gnu.java.net.CRLFInputStream; + +/** + * An XML parser. + * This parser supports the following additional StAX properties: + * + * + * + * + * + * + * + * + * + * + *
gnu.xml.stream.stringInterningBooleanIndicates whether markup strings will be interned
gnu.xml.stream.xmlBaseBooleanIndicates whether XML Base processing will be performed
gnu.xml.stream.baseURIStringReturns the base URI of the current event
+ * + * @see http://www.w3.org/TR/REC-xml/ + * @see http://www.w3.org/TR/xml11/ + * @see http://www.w3.org/TR/REC-xml-names + * @see http://www.w3.org/TR/xml-names11 + * @see http://www.w3.org/TR/xmlbase/ + * + * @author Chris Burdess + */ +public class XMLParser + implements XMLStreamReader, NamespaceContext +{ + + // -- parser state machine states -- + private static final int INIT = 0; // start state + private static final int PROLOG = 1; // in prolog + private static final int CONTENT = 2; // in content + private static final int EMPTY_ELEMENT = 3; // empty element state + private static final int MISC = 4; // in Misc (after root element) + + // -- parameters for parsing literals -- + private final static int LIT_ENTITY_REF = 2; + private final static int LIT_NORMALIZE = 4; + private final static int LIT_ATTRIBUTE = 8; + private final static int LIT_DISABLE_PE = 16; + private final static int LIT_DISABLE_CREF = 32; + private final static int LIT_DISABLE_EREF = 64; + private final static int LIT_PUBID = 256; + + // -- types of attribute values -- + final static int ATTRIBUTE_DEFAULT_UNDECLARED = 30; + final static int ATTRIBUTE_DEFAULT_SPECIFIED = 31; + final static int ATTRIBUTE_DEFAULT_IMPLIED = 32; + final static int ATTRIBUTE_DEFAULT_REQUIRED = 33; + final static int ATTRIBUTE_DEFAULT_FIXED = 34; + + /** + * The current input. + */ + private Input input; + + /** + * Stack of inputs representing XML general entities. + * The input representing the XML input stream or reader is always the + * first element in this stack. + */ + private LinkedList inputStack = new LinkedList(); + + /** + * Stack of start-entity events to be reported. + */ + private LinkedList startEntityStack = new LinkedList(); + + /** + * Stack of end-entity events to be reported. + */ + private LinkedList endEntityStack = new LinkedList(); + + /** + * Current parser state within the main state machine. + */ + private int state = INIT; + + /** + * The (type of the) current event. + */ + private int event; + + /** + * Whether we are looking ahead. Used by hasNext. + */ + private boolean lookahead; + + /** + * The element name stack. The first element in this stack will be the + * root element. + */ + private LinkedList stack = new LinkedList(); + + /** + * Stack of namespace contexts. These are maps specifying prefix-to-URI + * mappings. The first element in this stack is the most recent namespace + * context (i.e. the other way around from the element name stack). + */ + private LinkedList namespaces = new LinkedList(); + + /** + * The base-URI stack. This holds the base URI context for each element. + * The first element in this stack is the most recent context (i.e. the + * other way around from the element name stack). + */ + private LinkedList bases = new LinkedList(); + + /** + * The list of attributes for the current element, in the order defined in + * the XML stream. + */ + private ArrayList attrs = new ArrayList(); + + /** + * Buffer for text and character data. + */ + private StringBuffer buf = new StringBuffer(); + + /** + * Buffer for NMTOKEN strings (markup). + */ + private StringBuffer nmtokenBuf = new StringBuffer(); + + /** + * Buffer for string literals. (e.g. attribute values) + */ + private StringBuffer literalBuf = new StringBuffer(); + + /** + * Temporary Unicode character buffer used during character data reads. + */ + private int[] tmpBuf = new int[1024]; + + /** + * The element content model for the current element. + */ + private ContentModel currentContentModel; + + /** + * The validation stack. This holds lists of the elements seen for each + * element, in order to determine whether the names and order of these + * elements match the content model for the element. The last entry in + * this stack represents the current element. + */ + private LinkedList validationStack; + + /** + * These sets contain the IDs and the IDREFs seen in the document, to + * ensure that IDs are unique and that each IDREF refers to an ID in the + * document. + */ + private HashSet ids, idrefs; + + /** + * The target and data associated with the current processing instruction + * event. + */ + private String piTarget, piData; + + /** + * The XML version declared in the XML declaration. + */ + private String xmlVersion; + + /** + * The encoding declared in the XML declaration. + */ + private String xmlEncoding; + + /** + * The standalone value declared in the XML declaration. + */ + private Boolean xmlStandalone; + + /** + * The document type definition. + */ + Doctype doctype; + + /** + * State variables for determining parameter-entity expansion. + */ + private boolean expandPE, peIsError; + + /** + * Whether this is a validating parser. + */ + private final boolean validating; + + /** + * Whether strings representing markup will be interned. + */ + private final boolean stringInterning; + + /** + * If true, CDATA sections will be merged with adjacent text nodes into a + * single event. + */ + private final boolean coalescing; + + /** + * Whether to replace general entity references with their replacement + * text automatically during parsing. + * Otherwise entity-reference events will be issued. + */ + private final boolean replaceERefs; + + /** + * Whether to support external entities. + */ + private final boolean externalEntities; + + /** + * Whether to support DTDs. + */ + private final boolean supportDTD; + + /** + * Whether to support XML namespaces. If true, namespace information will + * be available. Otherwise namespaces will simply be reported as ordinary + * attributes. + */ + private final boolean namespaceAware; + + /** + * Whether to support XML Base. If true, URIs specified in xml:base + * attributes will be honoured when resolving external entities. + */ + private final boolean baseAware; + + /** + * The reporter to receive parsing warnings. + */ + final XMLReporter reporter; + + /** + * Callback interface for resolving external entities. + */ + final XMLResolver resolver; + + // -- Constants for testing the next kind of markup event -- + private static final String TEST_START_ELEMENT = "<"; + private static final String TEST_END_ELEMENT = ""; + private static final String TEST_END_COMMENT = "--"; + private static final String TEST_END_PI = "?>"; + private static final String TEST_END_CDATA = "]]>"; + + /** + * The general entities predefined by the XML specification. + */ + private static final LinkedHashMap PREDEFINED_ENTITIES = new LinkedHashMap(); + static + { + PREDEFINED_ENTITIES.put("amp", "&"); + PREDEFINED_ENTITIES.put("lt", "<"); + PREDEFINED_ENTITIES.put("gt", ">"); + PREDEFINED_ENTITIES.put("apos", "'"); + PREDEFINED_ENTITIES.put("quot", "\""); + } + + /** + * Creates a new XML parser for the given input stream. + * This constructor should be used where possible, as it allows the + * encoding of the XML data to be correctly determined from the stream. + * @param in the input stream + * @param systemId the URL from which the input stream was retrieved + * (necessary if there are external entities to be resolved) + * @param validating if the parser is to be a validating parser + * @param namespaceAware if the parser should support XML Namespaces + * @param coalescing if CDATA sections should be merged into adjacent text + * nodes + * @param replaceERefs if entity references should be automatically + * replaced by their replacement text (otherwise they will be reported as + * entity-reference events) + * @param externalEntities if external entities should be loaded + * @param supportDTD if support for the XML DTD should be enabled + * @param baseAware if the parser should support XML Base to resolve + * external entities + * @param stringInterning whether strings will be interned during parsing + * @param reporter the reporter to receive warnings during processing + * @param resolver the callback interface used to resolve external + * entities + */ + public XMLParser(InputStream in, String systemId, + boolean validating, + boolean namespaceAware, + boolean coalescing, + boolean replaceERefs, + boolean externalEntities, + boolean supportDTD, + boolean baseAware, + boolean stringInterning, + XMLReporter reporter, + XMLResolver resolver) + { + this.validating = validating; + this.namespaceAware = namespaceAware; + this.coalescing = coalescing; + this.replaceERefs = replaceERefs; + this.externalEntities = externalEntities; + this.supportDTD = supportDTD; + this.baseAware = baseAware; + this.stringInterning = stringInterning; + this.reporter = reporter; + this.resolver = resolver; + if (validating) + { + validationStack = new LinkedList(); + ids = new HashSet(); + idrefs = new HashSet(); + } + pushInput(new Input(in, null, null, systemId, null, null, false, true)); + } + + /** + * Creates a new XML parser for the given character stream. + * This constructor is only available for compatibility with the JAXP + * APIs, which permit XML to be parsed from a character stream. Because + * the encoding specified by the character stream may conflict with that + * specified in the XML declaration, this method should be avoided where + * possible. + * @param in the input stream + * @param systemId the URL from which the input stream was retrieved + * (necessary if there are external entities to be resolved) + * @param validating if the parser is to be a validating parser + * @param namespaceAware if the parser should support XML Namespaces + * @param coalescing if CDATA sections should be merged into adjacent text + * nodes + * @param replaceERefs if entity references should be automatically + * replaced by their replacement text (otherwise they will be reported as + * entity-reference events) + * @param externalEntities if external entities should be loaded + * @param supportDTD if support for the XML DTD should be enabled + * @param baseAware if the parser should support XML Base to resolve + * external entities + * @param stringInterning whether strings will be interned during parsing + * @param reporter the reporter to receive warnings during processing + * @param resolver the callback interface used to resolve external + * entities + */ + public XMLParser(Reader reader, String systemId, + boolean validating, + boolean namespaceAware, + boolean coalescing, + boolean replaceERefs, + boolean externalEntities, + boolean supportDTD, + boolean baseAware, + boolean stringInterning, + XMLReporter reporter, + XMLResolver resolver) + { + this.validating = validating; + this.namespaceAware = namespaceAware; + this.coalescing = coalescing; + this.replaceERefs = replaceERefs; + this.externalEntities = externalEntities; + this.supportDTD = supportDTD; + this.baseAware = baseAware; + this.stringInterning = stringInterning; + this.reporter = reporter; + this.resolver = resolver; + if (validating) + { + validationStack = new LinkedList(); + ids = new HashSet(); + idrefs = new HashSet(); + } + pushInput(new Input(null, reader, null, systemId, null, null, false, true)); + } + + // -- NamespaceContext -- + + public String getNamespaceURI(String prefix) + { + if (XMLConstants.XML_NS_PREFIX.equals(prefix)) + return XMLConstants.XML_NS_URI; + if (XMLConstants.XMLNS_ATTRIBUTE.equals(prefix)) + return XMLConstants.XMLNS_ATTRIBUTE_NS_URI; + for (Iterator i = namespaces.iterator(); i.hasNext(); ) + { + LinkedHashMap ctx = (LinkedHashMap) i.next(); + String namespaceURI = (String) ctx.get(prefix); + if (namespaceURI != null) + return namespaceURI; + } + return null; + } + + public String getPrefix(String namespaceURI) + { + if (XMLConstants.XML_NS_URI.equals(namespaceURI)) + return XMLConstants.XML_NS_PREFIX; + if (XMLConstants.XMLNS_ATTRIBUTE_NS_URI.equals(namespaceURI)) + return XMLConstants.XMLNS_ATTRIBUTE; + for (Iterator i = namespaces.iterator(); i.hasNext(); ) + { + LinkedHashMap ctx = (LinkedHashMap) i.next(); + if (ctx.containsValue(namespaceURI)) + { + for (Iterator j = ctx.entrySet().iterator(); j.hasNext(); ) + { + Map.Entry entry = (Map.Entry) i.next(); + String uri = (String) entry.getValue(); + if (uri.equals(namespaceURI)) + return (String) entry.getKey(); + } + } + } + return null; + } + + public Iterator getPrefixes(String namespaceURI) + { + if (XMLConstants.XML_NS_URI.equals(namespaceURI)) + return Collections.singleton(XMLConstants.XML_NS_PREFIX).iterator(); + if (XMLConstants.XMLNS_ATTRIBUTE_NS_URI.equals(namespaceURI)) + return Collections.singleton(XMLConstants.XMLNS_ATTRIBUTE).iterator(); + LinkedList acc = new LinkedList(); + for (Iterator i = namespaces.iterator(); i.hasNext(); ) + { + LinkedHashMap ctx = (LinkedHashMap) i.next(); + if (ctx.containsValue(namespaceURI)) + { + for (Iterator j = ctx.entrySet().iterator(); j.hasNext(); ) + { + Map.Entry entry = (Map.Entry) i.next(); + String uri = (String) entry.getValue(); + if (uri.equals(namespaceURI)) + acc.add(entry.getKey()); + } + } + } + return acc.iterator(); + } + + // -- XMLStreamReader -- + + public void close() + throws XMLStreamException + { + stack = null; + namespaces = null; + bases = null; + buf = null; + attrs = null; + doctype = null; + + inputStack = null; + validationStack = null; + ids = null; + idrefs = null; + } + + public NamespaceContext getNamespaceContext() + { + return this; + } + + public int getAttributeCount() + { + return attrs.size(); + } + + public String getAttributeName(int index) + { + Attribute a = (Attribute) attrs.get(index); + return a.localName; + } + + public String getAttributeNamespace(int index) + { + String prefix = getAttributePrefix(index); + return getNamespaceURI(prefix); + } + + public String getAttributePrefix(int index) + { + Attribute a = (Attribute) attrs.get(index); + return a.prefix; + } + + public QName getAttributeQName(int index) + { + Attribute a = (Attribute) attrs.get(index); + String namespaceURI = getNamespaceURI(a.prefix); + return new QName(namespaceURI, a.localName, a.prefix); + } + + public String getAttributeType(int index) + { + Attribute a = (Attribute) attrs.get(index); + return a.type; + } + + private String getAttributeType(String elementName, String attName) + { + if (doctype != null) + { + AttributeDecl att = doctype.getAttributeDecl(elementName, attName); + if (att != null) + return att.type; + } + return "CDATA"; + } + + public String getAttributeValue(int index) + { + Attribute a = (Attribute) attrs.get(index); + return a.value; + } + + public String getAttributeValue(String namespaceURI, String localName) + { + for (Iterator i = attrs.iterator(); i.hasNext(); ) + { + Attribute a = (Attribute) i.next(); + if (a.localName.equals(localName)) + { + String uri = getNamespaceURI(a.prefix); + if ((uri == null && namespaceURI == null) || + (uri != null && uri.equals(namespaceURI))) + return a.value; + } + } + return null; + } + + boolean isAttributeDeclared(int index) + { + if (doctype == null) + return false; + Attribute a = (Attribute) attrs.get(index); + String qn = ("".equals(a.prefix)) ? a.localName : + a.prefix + ":" + a.localName; + String elementName = buf.toString(); + return doctype.isAttributeDeclared(elementName, qn); + } + + public String getCharacterEncodingScheme() + { + return xmlEncoding; + } + + public String getElementText() + throws XMLStreamException + { + if (event != XMLStreamConstants.START_ELEMENT) + throw new XMLStreamException("current event must be START_ELEMENT"); + StringBuffer elementText = new StringBuffer(); + int depth = stack.size(); + while (event != XMLStreamConstants.END_ELEMENT || stack.size() > depth) + { + switch (next()) + { + case XMLStreamConstants.CHARACTERS: + case XMLStreamConstants.SPACE: + elementText.append(buf.toString()); + } + } + return elementText.toString(); + } + + public String getEncoding() + { + return (input.inputEncoding == null) ? "UTF-8" : input.inputEncoding; + } + + public int getEventType() + { + return event; + } + + public String getLocalName() + { + switch (event) + { + case XMLStreamConstants.START_ELEMENT: + case XMLStreamConstants.END_ELEMENT: + String qName = buf.toString(); + int ci = qName.indexOf(':'); + return (ci == -1) ? qName : qName.substring(ci + 1); + default: + return null; + } + } + + public Location getLocation() + { + return input; + } + + public QName getName() + { + switch (event) + { + case XMLStreamConstants.START_ELEMENT: + case XMLStreamConstants.END_ELEMENT: + String qName = buf.toString(); + int ci = qName.indexOf(':'); + String localName = (ci == -1) ? qName : qName.substring(ci + 1); + String prefix = (ci == -1) ? + (namespaceAware ? XMLConstants.DEFAULT_NS_PREFIX : null) : + qName.substring(0, ci); + String namespaceURI = getNamespaceURI(prefix); + return new QName(namespaceURI, localName, prefix); + default: + return null; + } + } + + public int getNamespaceCount() + { + if (!namespaceAware) + return 0; + switch (event) + { + case XMLStreamConstants.START_ELEMENT: + case XMLStreamConstants.END_ELEMENT: + LinkedHashMap ctx = (LinkedHashMap) namespaces.getFirst(); + return ctx.size(); + default: + return 0; + } + } + + public String getNamespacePrefix(int index) + { + LinkedHashMap ctx = (LinkedHashMap) namespaces.getFirst(); + int count = 0; + for (Iterator i = ctx.keySet().iterator(); i.hasNext(); ) + { + String prefix = (String) i.next(); + if (count++ == index) + return prefix; + } + return null; + } + + public String getNamespaceURI() + { + switch (event) + { + case XMLStreamConstants.START_ELEMENT: + case XMLStreamConstants.END_ELEMENT: + String qName = buf.toString(); + int ci = qName.indexOf(':'); + if (ci == -1) + return null; + String prefix = qName.substring(0, ci); + return getNamespaceURI(prefix); + default: + return null; + } + } + + public String getNamespaceURI(int index) + { + LinkedHashMap ctx = (LinkedHashMap) namespaces.getFirst(); + int count = 0; + for (Iterator i = ctx.values().iterator(); i.hasNext(); ) + { + String uri = (String) i.next(); + if (count++ == index) + return uri; + } + return null; + } + + public String getPIData() + { + return piData; + } + + public String getPITarget() + { + return piTarget; + } + + public String getPrefix() + { + switch (event) + { + case XMLStreamConstants.START_ELEMENT: + case XMLStreamConstants.END_ELEMENT: + String qName = buf.toString(); + int ci = qName.indexOf(':'); + return (ci == -1) ? + (namespaceAware ? XMLConstants.DEFAULT_NS_PREFIX : null) : + qName.substring(0, ci); + default: + return null; + } + } + + public Object getProperty(String name) + throws IllegalArgumentException + { + if (name == null) + throw new IllegalArgumentException("name is null"); + if (XMLInputFactory.ALLOCATOR.equals(name)) + return null; + if (XMLInputFactory.IS_COALESCING.equals(name)) + return coalescing ? Boolean.TRUE : Boolean.FALSE; + if (XMLInputFactory.IS_NAMESPACE_AWARE.equals(name)) + return namespaceAware ? Boolean.TRUE : Boolean.FALSE; + if (XMLInputFactory.IS_REPLACING_ENTITY_REFERENCES.equals(name)) + return replaceERefs ? Boolean.TRUE : Boolean.FALSE; + if (XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES.equals(name)) + return externalEntities ? Boolean.TRUE : Boolean.FALSE; + if (XMLInputFactory.IS_VALIDATING.equals(name)) + return Boolean.FALSE; + if (XMLInputFactory.REPORTER.equals(name)) + return reporter; + if (XMLInputFactory.RESOLVER.equals(name)) + return resolver; + if (XMLInputFactory.SUPPORT_DTD.equals(name)) + return supportDTD ? Boolean.TRUE : Boolean.FALSE; + if ("gnu.xml.stream.stringInterning".equals(name)) + return stringInterning ? Boolean.TRUE : Boolean.FALSE; + if ("gnu.xml.stream.xmlBase".equals(name)) + return baseAware ? Boolean.TRUE : Boolean.FALSE; + if ("gnu.xml.stream.baseURI".equals(name)) + return getXMLBase(); + return null; + } + + public String getText() + { + return buf.toString(); + } + + public char[] getTextCharacters() + { + return buf.toString().toCharArray(); + } + + public int getTextCharacters(int sourceStart, char[] target, + int targetStart, int length) + throws XMLStreamException + { + length = Math.min(sourceStart + buf.length(), length); + int sourceEnd = sourceStart + length; + buf.getChars(sourceStart, sourceEnd, target, targetStart); + return length; + } + + public int getTextLength() + { + return buf.length(); + } + + public int getTextStart() + { + return 0; + } + + public String getVersion() + { + return (xmlVersion == null) ? "1.0" : xmlVersion; + } + + public boolean hasName() + { + switch (event) + { + case XMLStreamConstants.START_ELEMENT: + case XMLStreamConstants.END_ELEMENT: + return true; + default: + return false; + } + } + + public boolean hasText() + { + switch (event) + { + case XMLStreamConstants.CHARACTERS: + case XMLStreamConstants.SPACE: + return true; + default: + return false; + } + } + + public boolean isAttributeSpecified(int index) + { + Attribute a = (Attribute) attrs.get(index); + return a.specified; + } + + public boolean isCharacters() + { + return (event == XMLStreamConstants.CHARACTERS); + } + + public boolean isEndElement() + { + return (event == XMLStreamConstants.END_ELEMENT); + } + + public boolean isStandalone() + { + return Boolean.TRUE.equals(xmlStandalone); + } + + public boolean isStartElement() + { + return (event == XMLStreamConstants.START_ELEMENT); + } + + public boolean isWhiteSpace() + { + return (event == XMLStreamConstants.SPACE); + } + + public int nextTag() + throws XMLStreamException + { + do + { + switch (next()) + { + case XMLStreamConstants.START_ELEMENT: + case XMLStreamConstants.END_ELEMENT: + case XMLStreamConstants.CHARACTERS: + case XMLStreamConstants.SPACE: + case XMLStreamConstants.COMMENT: + case XMLStreamConstants.PROCESSING_INSTRUCTION: + break; + default: + throw new XMLStreamException("Unexpected event type: " + event); + } + } + while (event != XMLStreamConstants.START_ELEMENT && + event != XMLStreamConstants.END_ELEMENT); + return event; + } + + public void require(int type, String namespaceURI, String localName) + throws XMLStreamException + { + if (event != type) + throw new XMLStreamException("Current event type is " + event); + if (event == XMLStreamConstants.START_ELEMENT || + event == XMLStreamConstants.END_ELEMENT) + { + String ln = getLocalName(); + if (!ln.equals(localName)) + throw new XMLStreamException("Current local-name is " + ln); + String uri = getNamespaceURI(); + if ((uri == null && namespaceURI != null) || + (uri != null && !uri.equals(namespaceURI))) + throw new XMLStreamException("Current namespace URI is " + uri); + } + } + + public boolean standaloneSet() + { + return (xmlStandalone != null); + } + + public boolean hasNext() + throws XMLStreamException + { + if (event == XMLStreamConstants.END_DOCUMENT) + return false; + if (!lookahead) + { + next(); + lookahead = true; + } + return event != -1; + } + + public int next() + throws XMLStreamException + { + if (lookahead) + { + lookahead = false; + return event; + } + if (event == XMLStreamConstants.END_ELEMENT) + { + // Pop namespace context + if (namespaceAware) + namespaces.removeFirst(); + // Pop base context + if (baseAware) + bases.removeFirst(); + } + if (!startEntityStack.isEmpty()) + { + String entityName = (String) startEntityStack.removeFirst(); + buf.setLength(0); + buf.append(entityName); + event = XMLStreamConstants.START_ENTITY; + return event; + } + else if (!endEntityStack.isEmpty()) + { + String entityName = (String) endEntityStack.removeFirst(); + buf.setLength(0); + buf.append(entityName); + event = XMLStreamConstants.END_ENTITY; + return event; + } + try + { + if (!input.initialized) + input.init(); + switch (state) + { + case CONTENT: + if (tryRead(TEST_END_ELEMENT)) + { + readEndElement(); + if (stack.isEmpty()) + state = MISC; + event = XMLStreamConstants.END_ELEMENT; + } + else if (tryRead(TEST_COMMENT)) + { + readComment(false); + event = XMLStreamConstants.COMMENT; + } + else if (tryRead(TEST_PI)) + { + readPI(false); + event = XMLStreamConstants.PROCESSING_INSTRUCTION; + } + else if (tryRead(TEST_CDATA)) + { + readCDSect(); + event = XMLStreamConstants.CDATA; + } + else if (tryRead(TEST_START_ELEMENT)) + { + state = readStartElement(); + event = XMLStreamConstants.START_ELEMENT; + } + else + { + // Check for character reference or predefined entity + mark(8); + int c = readCh(); + if (c == 0x26) // '&' + { + c = readCh(); + if (c == 0x23) // '#' + { + reset(); + event = readCharData(null); + } + else + { + // entity reference + reset(); + readCh(); // & + readReference(); + String ref = buf.toString(); + String text = (String) PREDEFINED_ENTITIES.get(ref); + if (text != null) + { + event = readCharData(text); + } + else if (replaceERefs && !isUnparsedEntity(ref)) + { + // this will report a start-entity event + boolean external = false; + if (doctype != null) + { + Object entity = doctype.getEntity(ref); + if (entity instanceof ExternalIds) + external = true; + } + expandEntity(ref, false, external); + event = next(); + } + else + { + event = XMLStreamConstants.ENTITY_REFERENCE; + } + } + } + else + { + reset(); + event = readCharData(null); + if (validating && doctype != null) + validatePCData(buf.toString()); + } + } + break; + case EMPTY_ELEMENT: + String elementName = (String) stack.removeLast(); + buf.setLength(0); + buf.append(elementName); + state = stack.isEmpty() ? MISC : CONTENT; + event = XMLStreamConstants.END_ELEMENT; + if (validating && doctype != null) + endElementValidationHook(); + break; + case INIT: // XMLDecl? + if (tryRead(TEST_XML_DECL)) + readXMLDecl(); + input.finalizeEncoding(); + event = XMLStreamConstants.START_DOCUMENT; + state = PROLOG; + break; + case PROLOG: // Misc* (doctypedecl Misc*)? + skipWhitespace(); + if (doctype == null && tryRead(TEST_DOCTYPE_DECL)) + { + readDoctypeDecl(); + event = XMLStreamConstants.DTD; + } + else if (tryRead(TEST_COMMENT)) + { + readComment(false); + event = XMLStreamConstants.COMMENT; + } + else if (tryRead(TEST_PI)) + { + readPI(false); + event = XMLStreamConstants.PROCESSING_INSTRUCTION; + } + else if (tryRead(TEST_START_ELEMENT)) + { + state = readStartElement(); + event = XMLStreamConstants.START_ELEMENT; + } + else + { + int c = readCh(); + error("no root element: U+" + Integer.toHexString(c)); + } + break; + case MISC: // Comment | PI | S + skipWhitespace(); + if (tryRead(TEST_COMMENT)) + { + readComment(false); + event = XMLStreamConstants.COMMENT; + } + else if (tryRead(TEST_PI)) + { + readPI(false); + event = XMLStreamConstants.PROCESSING_INSTRUCTION; + } + else + { + if (event == XMLStreamConstants.END_DOCUMENT) + throw new NoSuchElementException(); + int c = readCh(); + if (c != -1) + error("Only comments and PIs may appear after " + + "the root element"); + event = XMLStreamConstants.END_DOCUMENT; + } + break; + default: + event = -1; + } + return event; + } + catch (IOException e) + { + XMLStreamException e2 = new XMLStreamException(); + e2.initCause(e); + throw e2; + } + } + + // package private + + /** + * Returns the current element name. + */ + String getCurrentElement() + { + return (String) stack.getLast(); + } + + // private + + private void mark(int limit) + throws IOException + { + input.mark(limit); + } + + private void reset() + throws IOException + { + input.reset(); + } + + private int read() + throws IOException + { + return input.read(); + } + + private int read(int[] b, int off, int len) + throws IOException + { + return input.read(b, off, len); + } + + /** + * Parsed character read. + */ + private int readCh() + throws IOException, XMLStreamException + { + int c = read(); + if (expandPE && c == 0x25) // '%' + { + if (peIsError) + error("PE reference within decl in internal subset."); + expandPEReference(); + return readCh(); + } + return c; + } + + /** + * Reads the next character, ensuring it is the character specified. + * @param delim the character to match + * @exception XMLStreamException if the next character is not the + * specified one + */ + private void require(char delim) + throws IOException, XMLStreamException + { + mark(1); + int c = readCh(); + if (delim != c) + { + reset(); + error("required character (got U+" + Integer.toHexString(c) + ")", + new Character(delim)); + } + } + + /** + * Reads the next few characters, ensuring they match the string specified. + * @param delim the string to match + * @exception XMLStreamException if the next characters do not match the + * specified string + */ + private void require(String delim) + throws IOException, XMLStreamException + { + char[] chars = delim.toCharArray(); + int len = chars.length; + mark(len); + int off = 0; + do + { + int l2 = read(tmpBuf, off, len - off); + if (l2 == -1) + { + reset(); + error("EOF before required string", delim); + } + off += l2; + } + while (off < len); + for (int i = 0; i < chars.length; i++) + { + if (chars[i] != tmpBuf[i]) + { + reset(); + error("required string", delim); + } + } + } + + /** + * Try to read a single character. On failure, reset the stream. + * @param delim the character to test + * @return true if the character matched delim, false otherwise. + */ + private boolean tryRead(char delim) + throws IOException, XMLStreamException + { + mark(1); + int c = readCh(); + if (delim != c) + { + reset(); + return false; + } + return true; + } + + /** + * Tries to read the specified characters. + * If successful, the stream is positioned after the last character, + * otherwise it is reset. + * @param test the string to test + * @return true if the characters matched the test string, false otherwise. + */ + private boolean tryRead(String test) + throws IOException + { + char[] chars = test.toCharArray(); + int len = chars.length; + mark(len); + int count = 0; + int l2 = read(tmpBuf, 0, len); + if (l2 == -1) + { + reset(); + return false; + } + count += l2; + while (count < len) + { + // force read + int c = read(); + if (c == -1) + { + reset(); + return false; + } + tmpBuf[count++] = (char) c; + } + for (int i = 0; i < len; i++) + { + if (chars[i] != tmpBuf[i]) + { + reset(); + return false; + } + } + return true; + } + + /** + * Reads characters until the specified test string is encountered. + * @param delim the string delimiting the end of the characters + */ + private void readUntil(String delim) + throws IOException, XMLStreamException + { + int startLine = input.line; + try + { + while (!tryRead(delim)) + { + int c = readCh(); + if (c == -1) + throw new EOFException(); + else if (input.xml11) + { + if (!isXML11Char(c) || isXML11RestrictedChar(c)) + error("illegal XML 1.1 character", + "U+" + Integer.toHexString(c)); + } + else if (!isChar(c)) + error("illegal XML character", + "U+" + Integer.toHexString(c)); + buf.append(Character.toChars(c)); + } + } + catch (EOFException e) + { + error("end of input while looking for delimiter "+ + "(started on line " + startLine + ')', delim); + } + } + + /** + * Reads any whitespace characters. + * @return true if whitespace characters were read, false otherwise + */ + private boolean tryWhitespace() + throws IOException, XMLStreamException + { + boolean white; + boolean ret = false; + do + { + mark(1); + int c = readCh(); + while (c == -1 && inputStack.size() > 1) + { + popInput(); + c = readCh(); + } + white = (c == 0x20 || c == 0x09 || c == 0x0a || c == 0x0d); + if (white) + ret = true; + } + while (white); + reset(); + return ret; + } + + /** + * Skip over any whitespace characters. + */ + private void skipWhitespace() + throws IOException, XMLStreamException + { + boolean white; + do + { + mark(1); + int c = readCh(); + while (c == -1 && inputStack.size() > 1) + { + popInput(); + c = readCh(); + } + white = (c == 0x20 || c == 0x09 || c == 0x0a || c == 0x0d); + } + while (white); + reset(); + } + + /** + * Try to read as many whitespace characters as are available. + * @exception XMLStreamException if no whitespace characters were seen + */ + private void requireWhitespace() + throws IOException, XMLStreamException + { + if (!tryWhitespace()) + error("whitespace required"); + } + + /** + * Returns the current base URI for resolving external entities. + */ + String getXMLBase() + { + if (baseAware) + { + for (Iterator i = bases.iterator(); i.hasNext(); ) + { + String base = (String) i.next(); + if (base != null) + return base; + } + } + return input.systemId; + } + + /** + * Push the specified text input source. + */ + private void pushInput(String name, String text, boolean report, + boolean normalize) + throws IOException, XMLStreamException + { + // Check for recursion + if (name != null && !"".equals(name)) + { + for (Iterator i = inputStack.iterator(); i.hasNext(); ) + { + Input ctx = (Input) i.next(); + if (name.equals(ctx.name)) + error("entities may not be self-recursive", name); + } + } + else + report = false; + pushInput(new Input(null, new StringReader(text), input.publicId, + input.systemId, name, input.inputEncoding, report, + normalize)); + } + + /** + * Push the specified external input source. + */ + private void pushInput(String name, ExternalIds ids, boolean report, + boolean normalize) + throws IOException, XMLStreamException + { + if (!externalEntities) + return; + InputStream in = null; + String url = absolutize(input.systemId, ids.systemId); + // Check for recursion + for (Iterator i = inputStack.iterator(); i.hasNext(); ) + { + Input ctx = (Input) i.next(); + if (url.equals(ctx.systemId)) + error("entities may not be self-recursive", url); + if (name != null && !"".equals(name) && name.equals(ctx.name)) + error("entities may not be self-recursive", name); + } + if (name == null || "".equals(name)) + report = false; + if (in == null && url != null && resolver != null) + { + if (resolver instanceof XMLResolver2) + in = ((XMLResolver2) resolver).resolve(ids.publicId, url); + else + in = resolver.resolve(url); + } + if (in == null) + in = resolve(url); + if (in == null) + error("unable to resolve external entity", + (ids.systemId != null) ? ids.systemId : ids.publicId); + pushInput(new Input(in, null, ids.publicId, url, name, null, report, + normalize)); + input.init(); + if (tryRead(TEST_XML_DECL)) + readTextDecl(); + input.finalizeEncoding(); + } + + /** + * Push the specified input source (general entity) onto the input stack. + */ + private void pushInput(Input input) + { + if (input.report) + startEntityStack.addFirst(input.name); + inputStack.addLast(input); + if (this.input != null) + input.xml11 = this.input.xml11; + this.input = input; + } + + /** + * "Absolutize" a URL. This resolves a relative URL into an absolute one. + * @param base the current base URL + * @param href the (absolute or relative) URL to resolve + */ + static String absolutize(String base, String href) + { + if (href == null) + return null; + int ci = href.indexOf(':'); + if (ci > 1 && isLowercaseAscii(href.substring(0, ci))) + { + // href is absolute already + return href; + } + if (base == null) + base = ""; + else + { + int i = base.lastIndexOf('/'); + if (i != -1) + base = base.substring(0, i + 1); + else + base = ""; + } + if ("".equals(base)) + { + // assume file URL relative to current directory + base = System.getProperty("user.dir"); + if (base.charAt(0) == '/') + base = base.substring(1); + base = "file:///" + base.replace(File.separatorChar, '/'); + if (!base.endsWith("/")) + base += "/"; + } + if (href.startsWith("/")) + { + if (base.startsWith("file:")) + return "file://" + href; + int i = base.indexOf("://"); + if (i != -1) + { + i = base.indexOf('/', i + 3); + if (i != -1) + base = base.substring(0, i); + } + } + else + { + while (href.startsWith("..")) + { + int i = base.lastIndexOf('/', base.length() - 2); + if (i != -1) + base = base.substring(0, i + 1); + href = href.substring(2); + if (href.startsWith("/")) + href = href.substring(1); + } + } + return base + href; + } + + private static boolean isLowercaseAscii(String text) + { + int len = text.length(); + for (int i = 0; i < len; i++) + { + char c = text.charAt(i); + if (c < 97 || c > 122) + return false; + } + return true; + } + + /** + * Returns an input stream for the given URL. + */ + private InputStream resolve(String url) + throws IOException + { + try + { + return new URL(url).openStream(); + } + catch (MalformedURLException e) + { + return null; + } + } + + /** + * Pops the current input source (general entity) off the stack. + */ + private void popInput() + { + Input old = (Input) inputStack.removeLast(); + if (old.report) + endEntityStack.addFirst(old.name); + input = (Input) inputStack.getLast(); + } + + /** + * Parse an entity text declaration. + */ + private void readTextDecl() + throws IOException, XMLStreamException + { + final int flags = LIT_DISABLE_CREF | LIT_DISABLE_PE | LIT_DISABLE_EREF; + requireWhitespace(); + if (tryRead("version")) + { + readEq(); + String v = readLiteral(flags, false); + if ("1.0".equals(v)) + input.xml11 = false; + else if ("1.1".equals(v)) + { + Input i1 = (Input) inputStack.getFirst(); + if (!i1.xml11) + error("external entity specifies later version number"); + input.xml11 = true; + } + else + throw new XMLStreamException("illegal XML version: " + v); + requireWhitespace(); + } + require("encoding"); + readEq(); + String enc = readLiteral(flags, false); + skipWhitespace(); + require("?>"); + input.setInputEncoding(enc); + } + + /** + * Parse the XML declaration. + */ + private void readXMLDecl() + throws IOException, XMLStreamException + { + final int flags = LIT_DISABLE_CREF | LIT_DISABLE_PE | LIT_DISABLE_EREF; + + requireWhitespace(); + require("version"); + readEq(); + xmlVersion = readLiteral(flags, false); + if ("1.0".equals(xmlVersion)) + input.xml11 = false; + else if ("1.1".equals(xmlVersion)) + input.xml11 = true; + else + throw new XMLStreamException("illegal XML version: " + xmlVersion); + + boolean white = tryWhitespace(); + + if (tryRead("encoding")) + { + if (!white) + error("whitespace required before 'encoding='"); + readEq(); + xmlEncoding = readLiteral(flags, false); + white = tryWhitespace(); + } + + if (tryRead("standalone")) + { + if (!white) + error("whitespace required before 'standalone='"); + readEq(); + String standalone = readLiteral(flags, false); + if ("yes".equals(standalone)) + xmlStandalone = Boolean.TRUE; + else if ("no".equals(standalone)) + xmlStandalone = Boolean.FALSE; + else + error("standalone flag must be 'yes' or 'no'", standalone); + } + + skipWhitespace(); + require("?>"); + if (xmlEncoding != null) + input.setInputEncoding(xmlEncoding); + } + + /** + * Parse the DOCTYPE declaration. + */ + private void readDoctypeDecl() + throws IOException, XMLStreamException + { + if (!supportDTD) + error("parser was configured not to support DTDs"); + requireWhitespace(); + String rootName = readNmtoken(true); + skipWhitespace(); + ExternalIds ids = readExternalIds(false, true); + doctype = + this.new Doctype(rootName, ids.publicId, ids.systemId); + + // Parse internal subset first + skipWhitespace(); + if (tryRead('[')) + { + while (true) + { + expandPE = true; + skipWhitespace(); + expandPE = false; + if (tryRead(']')) + break; + else + readMarkupdecl(false); + } + } + skipWhitespace(); + require('>'); + + // Parse external subset + if (ids.systemId != null && externalEntities) + { + pushInput("", ">", false, false); + pushInput("[dtd]", ids, true, true); + // loop until we get back to ">" + while (true) + { + expandPE = true; + skipWhitespace(); + expandPE = false; + mark(1); + int c = readCh(); + if (c == 0x3e) // '>' + break; + else if (c == -1) + popInput(); + else + { + reset(); + expandPE = true; + readMarkupdecl(true); + expandPE = true; + } + } + if (inputStack.size() != 2) + error("external subset has unmatched '>'"); + popInput(); + } + checkDoctype(); + if (validating) + validateDoctype(); + + // Make rootName available for reading + buf.setLength(0); + buf.append(rootName); + } + + /** + * Checks the well-formedness of the DTD. + */ + private void checkDoctype() + throws XMLStreamException + { + // TODO check entity recursion + } + + /** + * Parse the markupdecl production. + */ + private void readMarkupdecl(boolean inExternalSubset) + throws IOException, XMLStreamException + { + boolean saved = expandPE; + mark(1); + require('<'); + reset(); + expandPE = false; + if (tryRead(TEST_ELEMENT_DECL)) + { + expandPE = saved; + readElementDecl(); + } + else if (tryRead(TEST_ATTLIST_DECL)) + { + expandPE = saved; + readAttlistDecl(); + } + else if (tryRead(TEST_ENTITY_DECL)) + { + expandPE = saved; + readEntityDecl(inExternalSubset); + } + else if (tryRead(TEST_NOTATION_DECL)) + { + expandPE = saved; + readNotationDecl(inExternalSubset); + } + else if (tryRead(TEST_PI)) + { + readPI(true); + expandPE = saved; + } + else if (tryRead(TEST_COMMENT)) + { + readComment(true); + expandPE = saved; + } + else if (tryRead("")) + { + readMarkupdecl(inExternalSubset); + skipWhitespace(); + } + } + else if (tryRead("IGNORE")) + { + skipWhitespace(); + require('['); + expandPE = false; + for (int nesting = 1; nesting > 0; ) + { + int c = readCh(); + switch (c) + { + case 0x3c: // '<' + if (tryRead("![")) + nesting++; + break; + case 0x5d: // ']' + if (tryRead("]>")) + nesting--; + break; + case -1: + throw new EOFException(); + } + } + expandPE = saved; + } + else + error("conditional section must begin with INCLUDE or IGNORE"); + } + else + error("expected markup declaration"); + } + + /** + * Parse the elementdecl production. + */ + private void readElementDecl() + throws IOException, XMLStreamException + { + requireWhitespace(); + String name = readNmtoken(true); + requireWhitespace(); + readContentspec(name); + skipWhitespace(); + require('>'); + } + + /** + * Parse the contentspec production. + */ + private void readContentspec(String elementName) + throws IOException, XMLStreamException + { + if (tryRead("EMPTY")) + doctype.addElementDecl(elementName, "EMPTY", new EmptyContentModel()); + else if (tryRead("ANY")) + doctype.addElementDecl(elementName, "ANY", new AnyContentModel()); + else + { + ContentModel model; + StringBuffer acc = new StringBuffer(); + require('('); + acc.append('('); + skipWhitespace(); + if (tryRead("#PCDATA")) + { + // mixed content + acc.append("#PCDATA"); + MixedContentModel mm = new MixedContentModel(); + model = mm; + skipWhitespace(); + if (tryRead(')')) + { + acc.append(")"); + if (tryRead('*')) + { + mm.min = 0; + mm.max = -1; + } + } + else + { + while (!tryRead(")")) + { + require('|'); + acc.append('|'); + skipWhitespace(); + String name = readNmtoken(true); + acc.append(name); + mm.addName(name); + skipWhitespace(); + } + require('*'); + acc.append(")*"); + mm.min = 0; + mm.max = -1; + } + } + else + model = readElements(acc); + doctype.addElementDecl(elementName, acc.toString(), model); + } + } + + /** + * Parses an element content model. + */ + private ElementContentModel readElements(StringBuffer acc) + throws IOException, XMLStreamException + { + int separator; + ElementContentModel model = new ElementContentModel(); + + // Parse first content particle + skipWhitespace(); + model.addContentParticle(readContentParticle(acc)); + // End or separator + skipWhitespace(); + int c = readCh(); + switch (c) + { + case 0x29: // ')' + acc.append(')'); + mark(1); + c = readCh(); + switch (c) + { + case 0x3f: // '?' + acc.append('?'); + model.min = 0; + model.max = 1; + break; + case 0x2a: // '*' + acc.append('*'); + model.min = 0; + model.max = -1; + break; + case 0x2b: // '+' + acc.append('+'); + model.min = 1; + model.max = -1; + break; + default: + reset(); + } + return model; // done + case 0x7c: // '|' + model.or = true; + // fall through + case 0x2c: // ',' + separator = c; + acc.append(Character.toChars(c)); + break; + default: + error("bad separator in content model", + "U+" + Integer.toHexString(c)); + return model; + } + // Parse subsequent content particles + while (true) + { + skipWhitespace(); + model.addContentParticle(readContentParticle(acc)); + skipWhitespace(); + c = readCh(); + if (c == 0x29) // ')' + { + acc.append(')'); + break; + } + else if (c != separator) + { + error("bad separator in content model", + "U+" + Integer.toHexString(c)); + return model; + } + else + acc.append(c); + } + // Check for occurrence indicator + mark(1); + c = readCh(); + switch (c) + { + case 0x3f: // '?' + acc.append('?'); + model.min = 0; + model.max = 1; + break; + case 0x2a: // '*' + acc.append('*'); + model.min = 0; + model.max = -1; + break; + case 0x2b: // '+' + acc.append('+'); + model.min = 1; + model.max = -1; + break; + default: + reset(); + } + return model; + } + + /** + * Parse a cp production. + */ + private ContentParticle readContentParticle(StringBuffer acc) + throws IOException, XMLStreamException + { + ContentParticle cp = new ContentParticle(); + if (tryRead('(')) + { + acc.append('('); + cp.content = readElements(acc); + } + else + { + String name = readNmtoken(true); + acc.append(name); + cp.content = name; + mark(1); + int c = readCh(); + switch (c) + { + case 0x3f: // '?' + acc.append('?'); + cp.min = 0; + cp.max = 1; + break; + case 0x2a: // '*' + acc.append('*'); + cp.min = 0; + cp.max = -1; + break; + case 0x2b: // '+' + acc.append('+'); + cp.min = 1; + cp.max = -1; + break; + default: + reset(); + } + } + return cp; + } + + /** + * Parse an attribute-list definition. + */ + private void readAttlistDecl() + throws IOException, XMLStreamException + { + requireWhitespace(); + String elementName = readNmtoken(true); + boolean white = tryWhitespace(); + while (!tryRead('>')) + { + if (!white) + error("whitespace required before attribute definition"); + readAttDef(elementName); + white = tryWhitespace(); + } + } + + /** + * Parse a single attribute definition. + */ + private void readAttDef(String elementName) + throws IOException, XMLStreamException + { + String name = readNmtoken(true); + requireWhitespace(); + StringBuffer acc = new StringBuffer(); + HashSet values = new HashSet(); + String type = readAttType(acc, values); + if (validating) + { + if ("ID".equals(type)) + { + // VC: One ID per Element Type + for (Iterator i = doctype.attlistIterator(elementName); + i.hasNext(); ) + { + Map.Entry entry = (Map.Entry) i.next(); + AttributeDecl decl = (AttributeDecl) entry.getValue(); + if ("ID".equals(decl.type)) + error("element types must not have more than one ID " + + "attribute"); + } + } + else if ("NOTATION".equals(type)) + { + // VC: One Notation Per Element Type + for (Iterator i = doctype.attlistIterator(elementName); + i.hasNext(); ) + { + Map.Entry entry = (Map.Entry) i.next(); + AttributeDecl decl = (AttributeDecl) entry.getValue(); + if ("NOTATION".equals(decl.type)) + error("element types must not have more than one NOTATION " + + "attribute"); + } + // VC: No Notation on Empty Element + ContentModel model = doctype.getElementModel(elementName); + if (model != null && model.type == ContentModel.EMPTY) + error("attributes of type NOTATION must not be declared on an " + + "element declared EMPTY"); + } + } + String enumer = null; + if ("ENUMERATION".equals(type) || "NOTATION".equals(type)) + enumer = acc.toString(); + else + values = null; + requireWhitespace(); + readDefault(elementName, name, type, enumer, values); + } + + /** + * Parse an attribute type. + */ + private String readAttType(StringBuffer acc, HashSet values) + throws IOException, XMLStreamException + { + if (tryRead('(')) + { + readEnumeration(false, acc, values); + return "ENUMERATION"; + } + else + { + String typeString = readNmtoken(true); + if ("NOTATION".equals(typeString)) + { + readNotationType(acc, values); + return typeString; + } + else if ("CDATA".equals(typeString) || + "ID".equals(typeString) || + "IDREF".equals(typeString) || + "IDREFS".equals(typeString) || + "ENTITY".equals(typeString) || + "ENTITIES".equals(typeString) || + "NMTOKEN".equals(typeString) || + "NMTOKENS".equals(typeString)) + return typeString; + else + { + error("illegal attribute type", typeString); + return null; + } + } + } + + /** + * Parse an enumeration. + */ + private void readEnumeration(boolean isNames, StringBuffer acc, + HashSet values) + throws IOException, XMLStreamException + { + acc.append('('); + // first token + skipWhitespace(); + String token = readNmtoken(isNames); + acc.append(token); + values.add(token); + // subsequent tokens + skipWhitespace(); + while (!tryRead(')')) + { + require('|'); + acc.append('|'); + skipWhitespace(); + token = readNmtoken(isNames); + // VC: No Duplicate Tokens + if (validating && values.contains(token)) + error("duplicate token", token); + acc.append(token); + values.add(token); + skipWhitespace(); + } + acc.append(')'); + } + + /** + * Parse a notation type for an attribute. + */ + private void readNotationType(StringBuffer acc, HashSet values) + throws IOException, XMLStreamException + { + requireWhitespace(); + require('('); + readEnumeration(true, acc, values); + } + + /** + * Parse the default value for an attribute. + */ + private void readDefault(String elementName, String name, + String type, String enumeration, HashSet values) + throws IOException, XMLStreamException + { + int valueType = ATTRIBUTE_DEFAULT_SPECIFIED; + int flags = LIT_ATTRIBUTE; + String value = null, defaultType = null; + boolean saved = expandPE; + + if (!"CDATA".equals(type)) + flags |= LIT_NORMALIZE; + + expandPE = false; + if (tryRead('#')) + { + if (tryRead("FIXED")) + { + defaultType = "#FIXED"; + valueType = ATTRIBUTE_DEFAULT_FIXED; + requireWhitespace(); + value = readLiteral(flags, false); + } + else if (tryRead("REQUIRED")) + { + defaultType = "#REQUIRED"; + valueType = ATTRIBUTE_DEFAULT_REQUIRED; + } + else if (tryRead("IMPLIED")) + { + defaultType = "#IMPLIED"; + valueType = ATTRIBUTE_DEFAULT_IMPLIED; + } + else + error("illegal keyword for attribute default value"); + } + else + value = readLiteral(flags, false); + expandPE = saved; + if (validating) + { + if ("ID".equals(type)) + { + // VC: Attribute Default Value Syntactically Correct + if (value != null && !isNmtoken(value, true)) + error("default value must match Name production", value); + // VC: ID Attribute Default + if (valueType != ATTRIBUTE_DEFAULT_REQUIRED && + valueType != ATTRIBUTE_DEFAULT_IMPLIED) + error("ID attributes must have a declared default of " + + "#IMPLIED or #REQUIRED"); + } + else if (value != null) + { + // VC: Attribute Default Value Syntactically Correct + if ("IDREF".equals(type) || "ENTITY".equals(type)) + { + if (!isNmtoken(value, true)) + error("default value must match Name production", value); + } + else if ("IDREFS".equals(type) || "ENTITIES".equals(type)) + { + StringTokenizer st = new StringTokenizer(value); + while (st.hasMoreTokens()) + { + String token = st.nextToken(); + if (!isNmtoken(token, true)) + error("default value must match Name production", token); + } + } + else if ("NMTOKEN".equals(type) || "ENUMERATION".equals(type)) + { + if (!isNmtoken(value, false)) + error("default value must match Nmtoken production", value); + } + else if ("NMTOKENS".equals(type)) + { + StringTokenizer st = new StringTokenizer(value); + while (st.hasMoreTokens()) + { + String token = st.nextToken(); + if (!isNmtoken(token, false)) + error("default value must match Nmtoken production", + token); + } + } + } + } + // Register attribute def + AttributeDecl attribute = + new AttributeDecl(type, value, valueType, enumeration, values, + inputStack.size() != 1); + doctype.addAttributeDecl(elementName, name, attribute); + } + + /** + * Parse the EntityDecl production. + */ + private void readEntityDecl(boolean inExternalSubset) + throws IOException, XMLStreamException + { + int flags = 0; + // Check if parameter entity + boolean peFlag = false; + expandPE = false; + requireWhitespace(); + if (tryRead('%')) + { + peFlag = true; + requireWhitespace(); + } + expandPE = true; + // Read entity name + String name = readNmtoken(true); + if (name.indexOf(':') != -1) + error("illegal character ':' in entity name", name); + if (peFlag) + name = "%" + name; + requireWhitespace(); + mark(1); + int c = readCh(); + reset(); + if (c == 0x22 || c == 0x27) // " | ' + { + // Internal entity replacement text + String value = readLiteral(flags | LIT_DISABLE_EREF, true); + int ai = value.indexOf('&'); + while (ai != -1) + { + int sci = value.indexOf(';', ai); + if (sci == -1) + error("malformed reference in entity value", value); + String ref = value.substring(ai + 1, sci); + int[] cp = UnicodeReader.toCodePointArray(ref); + if (cp.length == 0) + error("malformed reference in entity value", value); + if (cp[0] == 0x23) // # + { + if (cp.length == 1) + error("malformed reference in entity value", value); + if (cp[1] == 0x78) // 'x' + { + if (cp.length == 2) + error("malformed reference in entity value", value); + for (int i = 2; i < cp.length; i++) + { + int x = cp[i]; + if (x < 0x30 || + (x > 0x39 && x < 0x41) || + (x > 0x46 && x < 0x61) || + x > 0x66) + error("malformed character reference in entity value", + value); + } + } + else + { + for (int i = 1; i < cp.length; i++) + { + int x = cp[i]; + if (x < 0x30 || x > 0x39) + error("malformed character reference in entity value", + value); + } + } + } + else + { + if (!isNameStartCharacter(cp[0])) + error("malformed reference in entity value", value); + for (int i = 1; i < cp.length; i++) + { + if (!isNameCharacter(cp[i])) + error("malformed reference in entity value", value); + } + } + ai = value.indexOf('&', sci); + } + doctype.addEntityDecl(name, value, inExternalSubset); + } + else + { + ExternalIds ids = readExternalIds(false, false); + // Check for NDATA + boolean white = tryWhitespace(); + if (!peFlag && tryRead("NDATA")) + { + if (!white) + error("whitespace required before NDATA"); + requireWhitespace(); + ids.notationName = readNmtoken(true); + } + doctype.addEntityDecl(name, ids, inExternalSubset); + } + // finish + skipWhitespace(); + require('>'); + } + + /** + * Parse the NotationDecl production. + */ + private void readNotationDecl(boolean inExternalSubset) + throws IOException, XMLStreamException + { + requireWhitespace(); + String notationName = readNmtoken(true); + if (notationName.indexOf(':') != -1) + error("illegal character ':' in notation name", notationName); + if (validating) + { + // VC: Unique Notation Name + ExternalIds notation = doctype.getNotation(notationName); + if (notation != null) + error("duplicate notation name", notationName); + } + requireWhitespace(); + ExternalIds ids = readExternalIds(true, false); + ids.notationName = notationName; + doctype.addNotationDecl(notationName, ids, inExternalSubset); + skipWhitespace(); + require('>'); + } + + /** + * Returns a tuple {publicId, systemId}. + */ + private ExternalIds readExternalIds(boolean inNotation, boolean isSubset) + throws IOException, XMLStreamException + { + int c; + int flags = LIT_DISABLE_CREF | LIT_DISABLE_PE | LIT_DISABLE_EREF; + ExternalIds ids = new ExternalIds(); + + if (tryRead("PUBLIC")) + { + requireWhitespace(); + ids.publicId = readLiteral(LIT_NORMALIZE | LIT_PUBID | flags, false); + if (inNotation) + { + skipWhitespace(); + mark(1); + c = readCh(); + reset(); + if (c == 0x22 || c == 0x27) // " | ' + { + String href = readLiteral(flags, false); + ids.systemId = absolutize(input.systemId, href); + } + } + else + { + requireWhitespace(); + String href = readLiteral(flags, false); + ids.systemId = absolutize(input.systemId, href); + } + // Check valid URI characters + for (int i = 0; i < ids.publicId.length(); i++) + { + char d = ids.publicId.charAt(i); + if (d >= 'a' && d <= 'z') + continue; + if (d >= 'A' && d <= 'Z') + continue; + if (" \r\n0123456789-' ()+,./:=?;!*#@$_%".indexOf(d) != -1) + continue; + error("illegal PUBLIC id character", + "U+" + Integer.toHexString(d)); + } + } + else if (tryRead("SYSTEM")) + { + requireWhitespace(); + String href = readLiteral(flags, false); + ids.systemId = absolutize(input.systemId, href); + } + else if (!isSubset) + { + error("missing SYSTEM or PUBLIC keyword"); + } + if (ids.systemId != null && !inNotation) + { + if (ids.systemId.indexOf('#') != -1) + error("SYSTEM id has a URI fragment", ids.systemId); + } + return ids; + } + + /** + * Parse the start of an element. + * @return the state of the parser afterwards (EMPTY_ELEMENT or CONTENT) + */ + private int readStartElement() + throws IOException, XMLStreamException + { + // Read element name + String elementName = readNmtoken(true); + attrs.clear(); + // Push namespace context + if (namespaceAware) + { + if (elementName.charAt(0) == ':' || + elementName.charAt(elementName.length() - 1) == ':') + error("not a QName", elementName); + namespaces.addFirst(new LinkedHashMap()); + } + // Read element content + boolean white = tryWhitespace(); + mark(1); + int c = readCh(); + while (c != 0x2f && c != 0x3e) // '/' | '>' + { + // Read attribute + reset(); + if (!white) + error("need whitespace between attributes"); + readAttribute(elementName); + white = tryWhitespace(); + mark(1); + c = readCh(); + } + // supply defaulted attributes + if (doctype != null) + { + for (Iterator i = doctype.attlistIterator(elementName); i.hasNext(); ) + { + Map.Entry entry = (Map.Entry) i.next(); + String attName = (String) entry.getKey(); + AttributeDecl decl = (AttributeDecl) entry.getValue(); + if (validating) + { + switch (decl.valueType) + { + case ATTRIBUTE_DEFAULT_REQUIRED: + // VC: Required Attribute + if (decl.value == null && !attributeSpecified(attName)) + error("value for " + attName + " attribute is required"); + break; + case ATTRIBUTE_DEFAULT_FIXED: + // VC: Fixed Attribute Default + for (Iterator j = attrs.iterator(); j.hasNext(); ) + { + Attribute a = (Attribute) j.next(); + if (attName.equals(a.name) && + !decl.value.equals(a.value)) + error("value for " + attName + " attribute must be " + + decl.value); + } + break; + } + } + if (namespaceAware && attName.equals("xmlns")) + { + LinkedHashMap ctx = + (LinkedHashMap) namespaces.getFirst(); + if (ctx.containsKey(XMLConstants.DEFAULT_NS_PREFIX)) + continue; // namespace was specified + } + else if (namespaceAware && attName.startsWith("xmlns:")) + { + LinkedHashMap ctx = + (LinkedHashMap) namespaces.getFirst(); + if (ctx.containsKey(attName.substring(6))) + continue; // namespace was specified + } + else if (attributeSpecified(attName)) + continue; + if (decl.value == null) + continue; + // VC: Standalone Document Declaration + if (validating && decl.external && xmlStandalone == Boolean.TRUE) + error("standalone must be 'no' if attributes inherit values " + + "from externally declared markup declarations"); + Attribute attr = + new Attribute(attName, decl.type, false, decl.value); + if (namespaceAware) + { + if (!addNamespace(attr)) + attrs.add(attr); + } + else + attrs.add(attr); + } + } + if (baseAware) + { + String uri = getAttributeValue(XMLConstants.XML_NS_URI, "base"); + String base = getXMLBase(); + bases.addFirst(absolutize(base, uri)); + } + if (namespaceAware) + { + // check prefix bindings + int ci = elementName.indexOf(':'); + if (ci != -1) + { + String prefix = elementName.substring(0, ci); + String uri = getNamespaceURI(prefix); + if (uri == null) + error("unbound element prefix", prefix); + else if (input.xml11 && "".equals(uri)) + error("XML 1.1 unbound element prefix", prefix); + } + for (Iterator i = attrs.iterator(); i.hasNext(); ) + { + Attribute attr = (Attribute) i.next(); + if (attr.prefix != null && + !XMLConstants.XMLNS_ATTRIBUTE.equals(attr.prefix)) + { + String uri = getNamespaceURI(attr.prefix); + if (uri == null) + error("unbound attribute prefix", attr.prefix); + else if (input.xml11 && "".equals(uri)) + error("XML 1.1 unbound attribute prefix", attr.prefix); + } + } + } + if (validating && doctype != null) + { + validateStartElement(elementName); + currentContentModel = doctype.getElementModel(elementName); + if (currentContentModel == null) + error("no element declaration", elementName); + validationStack.add(new LinkedList()); + } + // make element name available for read + buf.setLength(0); + buf.append(elementName); + // push element onto stack + stack.addLast(elementName); + switch (c) + { + case 0x3e: // '>' + return CONTENT; + case 0x2f: // '/' + require('>'); + return EMPTY_ELEMENT; + } + return -1; // to satisfy compiler + } + + /** + * Indicates whether the specified attribute name was specified for the + * current element. + */ + private boolean attributeSpecified(String attName) + { + for (Iterator j = attrs.iterator(); j.hasNext(); ) + { + Attribute a = (Attribute) j.next(); + if (attName.equals(a.name)) + return true; + } + return false; + } + + /** + * Parse an attribute. + */ + private void readAttribute(String elementName) + throws IOException, XMLStreamException + { + // Read attribute name + String attributeName = readNmtoken(true); + String type = getAttributeType(elementName, attributeName); + readEq(); + // Read literal + final int flags = LIT_ATTRIBUTE | LIT_ENTITY_REF; + String value = (type == null || "CDATA".equals(type)) ? + readLiteral(flags, false) : readLiteral(flags | LIT_NORMALIZE, false); + // add attribute event + Attribute attr = this.new Attribute(attributeName, type, true, value); + if (namespaceAware) + { + if (attributeName.charAt(0) == ':' || + attributeName.charAt(attributeName.length() - 1) == ':') + error("not a QName", attributeName); + else if (attributeName.equals("xmlns")) + { + LinkedHashMap ctx = (LinkedHashMap) namespaces.getFirst(); + if (ctx.containsKey(XMLConstants.DEFAULT_NS_PREFIX)) + error("duplicate default namespace"); + } + else if (attributeName.startsWith("xmlns:")) + { + LinkedHashMap ctx = (LinkedHashMap) namespaces.getFirst(); + if (ctx.containsKey(attributeName.substring(6))) + error("duplicate namespace", attributeName.substring(6)); + } + else if (attrs.contains(attr)) + error("duplicate attribute", attributeName); + } + else if (attrs.contains(attr)) + error("duplicate attribute", attributeName); + if (validating && doctype != null) + { + // VC: Attribute Value Type + AttributeDecl decl = + doctype.getAttributeDecl(elementName, attributeName); + if (decl == null) + error("attribute must be declared", attributeName); + if ("ENUMERATION".equals(decl.type)) + { + // VC: Enumeration + if (!decl.values.contains(value)) + error("value does not match enumeration " + decl.enumeration, + value); + } + else if ("ID".equals(decl.type)) + { + // VC: ID + if (!isNmtoken(value, true)) + error("ID values must match the Name production"); + if (ids.contains(value)) + error("Duplicate ID", value); + ids.add(value); + } + else if ("IDREF".equals(decl.type) || "IDREFS".equals(decl.type)) + { + StringTokenizer st = new StringTokenizer(value); + while (st.hasMoreTokens()) + { + String token = st.nextToken(); + // VC: IDREF + if (!isNmtoken(token, true)) + error("IDREF values must match the Name production"); + idrefs.add(token); + } + } + else if ("NMTOKEN".equals(decl.type) || "NMTOKENS".equals(decl.type)) + { + StringTokenizer st = new StringTokenizer(value); + while (st.hasMoreTokens()) + { + String token = st.nextToken(); + // VC: Name Token + if (!isNmtoken(token, false)) + error("NMTOKEN values must match the Nmtoken production"); + } + } + else if ("ENTITY".equals(decl.type)) + { + // VC: Entity Name + if (!isNmtoken(value, true)) + error("ENTITY values must match the Name production"); + Object entity = doctype.getEntity(value); + if (entity == null || !(entity instanceof ExternalIds) || + ((ExternalIds) entity).notationName == null) + error("ENTITY values must match the name of an unparsed " + + "entity declared in the DTD"); + } + else if ("NOTATION".equals(decl.type)) + { + if (!decl.values.contains(value)) + error("NOTATION values must match a declared notation name", + value); + // VC: Notation Attributes + ExternalIds notation = doctype.getNotation(value); + if (notation == null) + error("NOTATION values must match the name of a notation " + + "declared in the DTD", value); + } + } + if (namespaceAware) + { + if (!addNamespace(attr)) + attrs.add(attr); + } + else + attrs.add(attr); + } + + /** + * Determines whether the specified attribute is a namespace declaration, + * and adds it to the current namespace context if so. Returns false if + * the attribute is an ordinary attribute. + */ + private boolean addNamespace(Attribute attr) + throws XMLStreamException + { + if ("xmlns".equals(attr.name)) + { + LinkedHashMap ctx = (LinkedHashMap) namespaces.getFirst(); + if (ctx.get(XMLConstants.DEFAULT_NS_PREFIX) != null) + error("Duplicate default namespace declaration"); + if (XMLConstants.XML_NS_URI.equals(attr.value)) + error("can't bind XML namespace"); + if ("".equals(attr.value) && !input.xml11) + error("illegal use of 1.1-style prefix unbinding in 1.0 document"); + ctx.put(XMLConstants.DEFAULT_NS_PREFIX, attr.value); + return true; + } + else if ("xmlns".equals(attr.prefix)) + { + LinkedHashMap ctx = (LinkedHashMap) namespaces.getFirst(); + if (ctx.get(attr.localName) != null) + error("Duplicate namespace declaration for prefix", + attr.localName); + if (XMLConstants.XML_NS_PREFIX.equals(attr.localName)) + { + if (!XMLConstants.XML_NS_URI.equals(attr.value)) + error("can't redeclare xml prefix"); + else + return false; // treat as attribute + } + if (XMLConstants.XML_NS_URI.equals(attr.value)) + error("can't bind non-xml prefix to XML namespace"); + if (XMLConstants.XMLNS_ATTRIBUTE.equals(attr.localName)) + error("can't redeclare xmlns prefix"); + if (XMLConstants.XMLNS_ATTRIBUTE_NS_URI.equals(attr.value)) + error("can't bind non-xmlns prefix to XML Namespace namespace"); + if ("".equals(attr.value) && !input.xml11) + error("illegal use of 1.1-style prefix unbinding in 1.0 document"); + ctx.put(attr.localName, attr.value); + return true; + } + return false; + } + + /** + * Parse a closing tag. + */ + private void readEndElement() + throws IOException, XMLStreamException + { + // pop element off stack + String expected = (String) stack.removeLast(); + require(expected); + skipWhitespace(); + require('>'); + // Make element name available + buf.setLength(0); + buf.append(expected); + if (validating && doctype != null) + endElementValidationHook(); + } + + /** + * Validate the end of an element. + * Called on an end-element or empty element if validating. + */ + private void endElementValidationHook() + throws XMLStreamException + { + validateEndElement(); + validationStack.removeLast(); + if (stack.isEmpty()) + currentContentModel = null; + else + { + String parent = (String) stack.getLast(); + currentContentModel = doctype.getElementModel(parent); + } + } + + /** + * Parse a comment. + */ + private void readComment(boolean inDTD) + throws IOException, XMLStreamException + { + boolean saved = expandPE; + expandPE = false; + buf.setLength(0); + readUntil(TEST_END_COMMENT); + require('>'); + expandPE = saved; + if (inDTD) + doctype.addComment(buf.toString()); + } + + /** + * Parse a processing instruction. + */ + private void readPI(boolean inDTD) + throws IOException, XMLStreamException + { + boolean saved = expandPE; + expandPE = false; + piTarget = readNmtoken(true); + if (piTarget.indexOf(':') != -1) + error("illegal character in PI target", new Character(':')); + if ("xml".equalsIgnoreCase(piTarget)) + error("illegal PI target", piTarget); + if (tryRead(TEST_END_PI)) + piData = null; + else + { + if (!tryWhitespace()) + error("whitespace required between PI target and data"); + buf.setLength(0); + readUntil(TEST_END_PI); + piData = buf.toString(); + } + expandPE = saved; + if (inDTD) + doctype.addPI(piTarget, piData); + } + + /** + * Parse an entity reference. + */ + private void readReference() + throws IOException, XMLStreamException + { + buf.setLength(0); + String entityName = readNmtoken(true); + require(';'); + buf.setLength(0); + buf.append(entityName); + } + + /** + * Read an CDATA section. + */ + private void readCDSect() + throws IOException, XMLStreamException + { + buf.setLength(0); + readUntil(TEST_END_CDATA); + } + + /** + * Read character data. + * @return the type of text read (CHARACTERS or SPACE) + */ + private int readCharData(String prefix) + throws IOException, XMLStreamException + { + boolean white = true; + buf.setLength(0); + if (prefix != null) + buf.append(prefix); + boolean done = false; + boolean entities = false; + while (!done) + { + // Block read + mark(tmpBuf.length); + int len = read(tmpBuf, 0, tmpBuf.length); + if (len == -1) + { + if (inputStack.size() > 1) + { + popInput(); + // report end-entity + done = true; + } + else + throw new EOFException(); + } + for (int i = 0; i < len && !done; i++) + { + int c = tmpBuf[i]; + switch (c) + { + case 0x20: + case 0x09: + case 0x0a: + case 0x0d: + buf.append(Character.toChars(c)); + break; // whitespace + case 0x26: // '&' + reset(); + read(tmpBuf, 0, i); + // character reference? + mark(3); + c = readCh(); // & + c = readCh(); + if (c == 0x23) // '#' + { + mark(1); + c = readCh(); + boolean hex = (c == 0x78); // 'x' + if (!hex) + reset(); + char[] ch = readCharacterRef(hex ? 16 : 10); + buf.append(ch, 0, ch.length); + for (int j = 0; j < ch.length; j++) + { + switch (ch[j]) + { + case 0x20: + case 0x09: + case 0x0a: + case 0x0d: + break; // whitespace + default: + white = false; + } + } + } + else + { + // entity reference + reset(); + c = readCh(); // & + String entityName = readNmtoken(true); + require(';'); + String text = + (String) PREDEFINED_ENTITIES.get(entityName); + if (text != null) + buf.append(text); + else + { + pushInput("", "&" + entityName + ";", false, false); + done = true; + break; + } + } + // continue processing + i = -1; + mark(tmpBuf.length); + len = read(tmpBuf, 0, tmpBuf.length); + if (len == -1) + { + if (inputStack.size() > 1) + { + popInput(); + done = true; + } + else + throw new EOFException(); + } + entities = true; + break; // end of text sequence + case 0x3e: // '>' + int l = buf.length(); + if (l > 1 && + buf.charAt(l - 1) == ']' && + buf.charAt(l - 2) == ']') + error("Character data may not contain unescaped ']]>'"); + buf.append(Character.toChars(c)); + break; + case 0x3c: // '<' + reset(); + read(tmpBuf, 0, i); + i = len; + if (coalescing && tryRead(TEST_CDATA)) + readUntil(TEST_END_CDATA); // read CDATA section into buf + else + done = true; // end of text sequence + break; + default: + if (input.xml11) + { + if (!isXML11Char(c) || isXML11RestrictedChar(c)) + error("illegal XML 1.1 character", + "U+" + Integer.toHexString(c)); + } + else if (!isChar(c)) + error("illegal XML character", + "U+" + Integer.toHexString(c)); + white = false; + buf.append(Character.toChars(c)); + } + } + // if text buffer >= 2MB, return it as a chunk + // to avoid excessive memory use + if (buf.length() >= 2097152) + done = true; + } + if (entities) + normalizeCRLF(buf); + return white ? XMLStreamConstants.SPACE : XMLStreamConstants.CHARACTERS; + } + + /** + * Expands the specified entity. + */ + private void expandEntity(String name, boolean inAttr, boolean normalize) + throws IOException, XMLStreamException + { + if (doctype != null) + { + Object value = doctype.getEntity(name); + if (value != null) + { + if (xmlStandalone == Boolean.TRUE) + { + // VC: Standalone Document Declaration + if (doctype.isEntityExternal(name)) + error("reference to external entity in standalone document"); + else if (value instanceof ExternalIds) + { + ExternalIds ids = (ExternalIds) value; + if (ids.notationName != null && + doctype.isNotationExternal(ids.notationName)) + error("reference to external notation in " + + "standalone document"); + } + } + if (value instanceof String) + { + String text = (String) value; + if (inAttr && text.indexOf('<') != -1) + error("< in attribute value"); + pushInput(name, text, !inAttr, normalize); + } + else if (inAttr) + error("reference to external entity in attribute value", name); + else + pushInput(name, (ExternalIds) value, !inAttr, normalize); + return; + } + } + error("reference to undeclared entity", name); + } + + /** + * Indicates whether the specified entity is unparsed. + */ + private boolean isUnparsedEntity(String name) + { + if (doctype != null) + { + Object value = doctype.getEntity(name); + if (value != null && value instanceof ExternalIds) + return ((ExternalIds) value).notationName != null; + } + return false; + } + + /** + * Read an equals sign. + */ + private void readEq() + throws IOException, XMLStreamException + { + skipWhitespace(); + require('='); + skipWhitespace(); + } + + /** + * Character read for reading literals. + * @param recognizePEs whether to recognize parameter-entity references + */ + private int literalReadCh(boolean recognizePEs) + throws IOException, XMLStreamException + { + int c = recognizePEs ? readCh() : read(); + while (c == -1) + { + if (inputStack.size() > 1) + { + inputStack.removeLast(); + input = (Input) inputStack.getLast(); + // Don't issue end-entity + c = recognizePEs ? readCh() : read(); + } + else + throw new EOFException(); + } + return c; + } + + /** + * Read a string literal. + */ + private String readLiteral(int flags, boolean recognizePEs) + throws IOException, XMLStreamException + { + boolean saved = expandPE; + int delim = readCh(); + if (delim != 0x27 && delim != 0x22) + error("expected '\"' or \"'\"", "U+" + Integer.toHexString(delim)); + literalBuf.setLength(0); + if ((flags & LIT_DISABLE_PE) != 0) + expandPE = false; + boolean entities = false; + int inputStackSize = inputStack.size(); + do + { + int c = literalReadCh(recognizePEs); + if (c == delim && inputStackSize == inputStack.size()) + break; + switch (c) + { + case 0x0a: + case 0x0d: + if ((flags & (LIT_ATTRIBUTE | LIT_PUBID)) != 0) + c = 0x20; // normalize to space + break; + case 0x09: + if ((flags & LIT_ATTRIBUTE) != 0) + c = 0x20; // normalize to space + break; + case 0x26: // '&' + mark(2); + c = readCh(); + if (c == 0x23) // '#' + { + if ((flags & LIT_DISABLE_CREF) != 0) + { + reset(); + c = 0x26; // '&' + } + else + { + mark(1); + c = readCh(); + boolean hex = (c == 0x78); // 'x' + if (!hex) + reset(); + char[] ref = readCharacterRef(hex ? 16 : 10); + for (int i = 0; i < ref.length; i++) + { + char x = ref[i]; + if ((flags & (LIT_ATTRIBUTE | LIT_PUBID)) != 0 && + (x == 0x0a || x == 0x0d)) + x = 0x20; // normalize + else if ((flags & LIT_ATTRIBUTE) != 0 && x == 0x09) + x = 0x20; // normalize + literalBuf.append(x); + } + entities = true; + continue; + } + } + else + { + if ((flags & LIT_DISABLE_EREF) != 0) + { + reset(); + c = 0x26; // '&' + } + else + { + reset(); + String entityName = readNmtoken(true); + require(';'); + String text = + (String) PREDEFINED_ENTITIES.get(entityName); + if (text != null) + literalBuf.append(text); + else + expandEntity(entityName, + (flags & LIT_ATTRIBUTE) != 0, + true); + entities = true; + continue; + } + } + break; + case 0x3c: // '<' + if ((flags & LIT_ATTRIBUTE) != 0) + error("attribute values may not contain '<'"); + break; + case -1: + if (inputStack.size() > 1) + { + popInput(); + continue; + } + throw new EOFException(); + default: + if ((c < 0x0020 || c > 0xfffd) || + (c >= 0xd800 && c < 0xdc00) || + (input.xml11 && (c >= 0x007f) && + (c <= 0x009f) && (c != 0x0085))) + error("illegal character", "U+" + Integer.toHexString(c)); + } + literalBuf.append(Character.toChars(c)); + } + while (true); + expandPE = saved; + if (entities) + normalizeCRLF(literalBuf); + if ((flags & LIT_NORMALIZE) > 0) + literalBuf = normalize(literalBuf); + return literalBuf.toString(); + } + + /** + * Performs attribute-value normalization of the text buffer. + * This discards leading and trailing whitespace, and replaces sequences + * of whitespace with a single space. + */ + private StringBuffer normalize(StringBuffer buf) + { + StringBuffer acc = new StringBuffer(); + int len = buf.length(); + int avState = 0; + for (int i = 0; i < len; i++) + { + char c = buf.charAt(i); + if (c == ' ') + avState = (avState == 0) ? 0 : 1; + else + { + if (avState == 1) + acc.append(' '); + acc.append(c); + avState = 2; + } + } + return acc; + } + + /** + * Replace any CR/LF pairs in the buffer with LF. + * This may be necessary if combinations of CR or LF were declared as + * (character) entity references in the input. + */ + private void normalizeCRLF(StringBuffer buf) + { + int len = buf.length() - 1; + for (int i = 0; i < len; i++) + { + char c = buf.charAt(i); + if (c == '\r' && buf.charAt(i + 1) == '\n') + { + buf.deleteCharAt(i--); + len--; + } + } + } + + /** + * Parse and expand a parameter entity reference. + */ + private void expandPEReference() + throws IOException, XMLStreamException + { + String name = readNmtoken(true, new StringBuffer()); + require(';'); + mark(1); // ensure we don't reset to before the semicolon + if (doctype != null) + { + String entityName = "%" + name; + Object entity = doctype.getEntity(entityName); + if (entity != null) + { + if (xmlStandalone == Boolean.TRUE) + { + if (doctype.isEntityExternal(entityName)) + error("reference to external parameter entity in " + + "standalone document"); + } + if (entity instanceof String) + { + pushInput(name, (String) entity, false, input.normalize); + //pushInput(name, " " + (String) entity + " "); + } + else + { + //pushInput("", " "); + pushInput(name, (ExternalIds) entity, false, input.normalize); + //pushInput("", " "); + } + } + else + error("reference to undeclared parameter entity", name); + } + else + error("reference to parameter entity without doctype", name); + } + + /** + * Parse the digits in a character reference. + * @param base the base of the digits (10 or 16) + */ + private char[] readCharacterRef(int base) + throws IOException, XMLStreamException + { + StringBuffer b = new StringBuffer(); + for (int c = readCh(); c != 0x3b && c != -1; c = readCh()) + b.append(Character.toChars(c)); + try + { + int ord = Integer.parseInt(b.toString(), base); + if (input.xml11) + { + if (!isXML11Char(ord)) + error("illegal XML 1.1 character reference " + + "U+" + Integer.toHexString(ord)); + } + else + { + if ((ord < 0x20 && !(ord == 0x0a || ord == 0x09 || ord == 0x0d)) + || (ord >= 0xd800 && ord <= 0xdfff) + || ord == 0xfffe || ord == 0xffff + || ord > 0x0010ffff) + error("illegal XML character reference " + + "U+" + Integer.toHexString(ord)); + } + return Character.toChars(ord); + } + catch (NumberFormatException e) + { + error("illegal characters in character reference", b.toString()); + return null; + } + } + + /** + * Parses an NMTOKEN or Name production. + * @param isName if a Name, otherwise an NMTOKEN + */ + private String readNmtoken(boolean isName) + throws IOException, XMLStreamException + { + return readNmtoken(isName, nmtokenBuf); + } + + /** + * Parses an NMTOKEN or Name production using the specified buffer. + * @param isName if a Name, otherwise an NMTOKEN + * @param buf the character buffer to use + */ + private String readNmtoken(boolean isName, StringBuffer buf) + throws IOException, XMLStreamException + { + buf.setLength(0); + int c = readCh(); + if (isName) + { + if (!isNameStartCharacter(c)) + error("not a name start character", + "U+" + Integer.toHexString(c)); + } + else + { + if (!isNameCharacter(c)) + error("not a name character", + "U+" + Integer.toHexString(c)); + } + buf.append(Character.toChars(c)); + do + { + mark(1); + c = readCh(); + switch (c) + { + case 0x25: // '%' + case 0x3c: // '<' + case 0x3e: // '>' + case 0x26: // '&' + case 0x2c: // ',' + case 0x7c: // '|' + case 0x2a: // '*' + case 0x2b: // '+' + case 0x3f: // '?' + case 0x29: // ')' + case 0x3d: // '=' + case 0x27: // '\'' + case 0x22: // '"' + case 0x5b: // '[' + case 0x20: // ' ' + case 0x09: // '\t' + case 0x0a: // '\n' + case 0x0d: // '\r' + case 0x3b: // ';' + case 0x2f: // '/' + case -1: + reset(); + return intern(buf.toString()); + default: + if (!isNameCharacter(c)) + error("not a name character", + "U+" + Integer.toHexString(c)); + else + buf.append(Character.toChars(c)); + } + } + while (true); + } + + /** + * Indicates whether the specified Unicode character is an XML 1.1 Char. + */ + private boolean isXML11Char(int c) + { + return ((c >= 0x0001 && c <= 0xD7FF) || + (c >= 0xE000 && c < 0xFFFD) || // NB exclude 0xfffd + (c >= 0x10000 && c <= 0x10FFFF)); + } + + /** + * Indicates whether the specified Unicode character is an XML 1.1 + * RestrictedChar. + */ + private boolean isXML11RestrictedChar(int c) + { + return ((c >= 0x0001 && c <= 0x0008) || + (c >= 0x000B && c <= 0x000C) || + (c >= 0x000E && c <= 0x001F) || + (c >= 0x007F && c <= 0x0084) || + (c >= 0x0086 && c <= 0x009F)); + } + + /** + * Indicates whether the specified text matches the Name or Nmtoken + * production. + */ + private boolean isNmtoken(String text, boolean isName) + { + try + { + int[] cp = UnicodeReader.toCodePointArray(text); + if (cp.length == 0) + return false; + if (isName) + { + if (!isNameStartCharacter(cp[0])) + return false; + } + else + { + if (!isNameCharacter(cp[0])) + return false; + } + for (int i = 1; i < cp.length; i++) + { + if (!isNameCharacter(cp[i])) + return false; + } + return true; + } + catch (IOException e) + { + return false; + } + } + + /** + * Indicates whether the specified Unicode character is a Name start + * character. + */ + private boolean isNameStartCharacter(int c) + { + if (input.xml11) + return ((c >= 0x0041 && c <= 0x005a) || + (c >= 0x0061 && c <= 0x007a) || + c == 0x3a | + c == 0x5f | + (c >= 0xC0 && c <= 0xD6) || + (c >= 0xD8 && c <= 0xF6) || + (c >= 0xF8 && c <= 0x2FF) || + (c >= 0x370 && c <= 0x37D) || + (c >= 0x37F && c <= 0x1FFF) || + (c >= 0x200C && c <= 0x200D) || + (c >= 0x2070 && c <= 0x218F) || + (c >= 0x2C00 && c <= 0x2FEF) || + (c >= 0x3001 && c <= 0xD7FF) || + (c >= 0xF900 && c <= 0xFDCF) || + (c >= 0xFDF0 && c <= 0xFFFD) || + (c >= 0x10000 && c <= 0xEFFFF)); + else + return (c == 0x5f || c == 0x3a || isLetter(c)); + } + + /** + * Indicates whether the specified Unicode character is a Name non-initial + * character. + */ + private boolean isNameCharacter(int c) + { + if (input.xml11) + return ((c >= 0x0041 && c <= 0x005a) || + (c >= 0x0061 && c <= 0x007a) || + (c >= 0x0030 && c <= 0x0039) || + c == 0x3a | + c == 0x5f | + c == 0x2d | + c == 0x2e | + c == 0xB7 | + (c >= 0xC0 && c <= 0xD6) || + (c >= 0xD8 && c <= 0xF6) || + (c >= 0xF8 && c <= 0x2FF) || + (c >= 0x300 && c <= 0x37D) || + (c >= 0x37F && c <= 0x1FFF) || + (c >= 0x200C && c <= 0x200D) || + (c >= 0x203F && c <= 0x2040) || + (c >= 0x2070 && c <= 0x218F) || + (c >= 0x2C00 && c <= 0x2FEF) || + (c >= 0x3001 && c <= 0xD7FF) || + (c >= 0xF900 && c <= 0xFDCF) || + (c >= 0xFDF0 && c <= 0xFFFD) || + (c >= 0x10000 && c <= 0xEFFFF)); + else + return (c == 0x2e || c == 0x2d || c == 0x5f || c == 0x3a || + isLetter(c) || isDigit(c) || + isCombiningChar(c) || isExtender(c)); + } + + /** + * Indicates whether the specified Unicode character matches the Letter + * production. + */ + public static boolean isLetter(int c) + { + if ((c >= 0x0041 && c <= 0x005A) || + (c >= 0x0061 && c <= 0x007A) || + (c >= 0x00C0 && c <= 0x00D6) || + (c >= 0x00D8 && c <= 0x00F6) || + (c >= 0x00F8 && c <= 0x00FF) || + (c >= 0x0100 && c <= 0x0131) || + (c >= 0x0134 && c <= 0x013E) || + (c >= 0x0141 && c <= 0x0148) || + (c >= 0x014A && c <= 0x017E) || + (c >= 0x0180 && c <= 0x01C3) || + (c >= 0x01CD && c <= 0x01F0) || + (c >= 0x01F4 && c <= 0x01F5) || + (c >= 0x01FA && c <= 0x0217) || + (c >= 0x0250 && c <= 0x02A8) || + (c >= 0x02BB && c <= 0x02C1) || + c == 0x0386 || + (c >= 0x0388 && c <= 0x038A) || + c == 0x038C || + (c >= 0x038E && c <= 0x03A1) || + (c >= 0x03A3 && c <= 0x03CE) || + (c >= 0x03D0 && c <= 0x03D6) || + c == 0x03DA || + c == 0x03DC || + c == 0x03DE || + c == 0x03E0 || + (c >= 0x03E2 && c <= 0x03F3) || + (c >= 0x0401 && c <= 0x040C) || + (c >= 0x040E && c <= 0x044F) || + (c >= 0x0451 && c <= 0x045C) || + (c >= 0x045E && c <= 0x0481) || + (c >= 0x0490 && c <= 0x04C4) || + (c >= 0x04C7 && c <= 0x04C8) || + (c >= 0x04CB && c <= 0x04CC) || + (c >= 0x04D0 && c <= 0x04EB) || + (c >= 0x04EE && c <= 0x04F5) || + (c >= 0x04F8 && c <= 0x04F9) || + (c >= 0x0531 && c <= 0x0556) || + c == 0x0559 || + (c >= 0x0561 && c <= 0x0586) || + (c >= 0x05D0 && c <= 0x05EA) || + (c >= 0x05F0 && c <= 0x05F2) || + (c >= 0x0621 && c <= 0x063A) || + (c >= 0x0641 && c <= 0x064A) || + (c >= 0x0671 && c <= 0x06B7) || + (c >= 0x06BA && c <= 0x06BE) || + (c >= 0x06C0 && c <= 0x06CE) || + (c >= 0x06D0 && c <= 0x06D3) || + c == 0x06D5 || + (c >= 0x06E5 && c <= 0x06E6) || + (c >= 0x0905 && c <= 0x0939) || + c == 0x093D || + (c >= 0x0958 && c <= 0x0961) || + (c >= 0x0985 && c <= 0x098C) || + (c >= 0x098F && c <= 0x0990) || + (c >= 0x0993 && c <= 0x09A8) || + (c >= 0x09AA && c <= 0x09B0) || + c == 0x09B2 || + (c >= 0x09B6 && c <= 0x09B9) || + (c >= 0x09DC && c <= 0x09DD) || + (c >= 0x09DF && c <= 0x09E1) || + (c >= 0x09F0 && c <= 0x09F1) || + (c >= 0x0A05 && c <= 0x0A0A) || + (c >= 0x0A0F && c <= 0x0A10) || + (c >= 0x0A13 && c <= 0x0A28) || + (c >= 0x0A2A && c <= 0x0A30) || + (c >= 0x0A32 && c <= 0x0A33) || + (c >= 0x0A35 && c <= 0x0A36) || + (c >= 0x0A38 && c <= 0x0A39) || + (c >= 0x0A59 && c <= 0x0A5C) || + c == 0x0A5E || + (c >= 0x0A72 && c <= 0x0A74) || + (c >= 0x0A85 && c <= 0x0A8B) || + c == 0x0A8D || + (c >= 0x0A8F && c <= 0x0A91) || + (c >= 0x0A93 && c <= 0x0AA8) || + (c >= 0x0AAA && c <= 0x0AB0) || + (c >= 0x0AB2 && c <= 0x0AB3) || + (c >= 0x0AB5 && c <= 0x0AB9) || + c == 0x0ABD || + c == 0x0AE0 || + (c >= 0x0B05 && c <= 0x0B0C) || + (c >= 0x0B0F && c <= 0x0B10) || + (c >= 0x0B13 && c <= 0x0B28) || + (c >= 0x0B2A && c <= 0x0B30) || + (c >= 0x0B32 && c <= 0x0B33) || + (c >= 0x0B36 && c <= 0x0B39) || + c == 0x0B3D || + (c >= 0x0B5C && c <= 0x0B5D) || + (c >= 0x0B5F && c <= 0x0B61) || + (c >= 0x0B85 && c <= 0x0B8A) || + (c >= 0x0B8E && c <= 0x0B90) || + (c >= 0x0B92 && c <= 0x0B95) || + (c >= 0x0B99 && c <= 0x0B9A) || + c == 0x0B9C || + (c >= 0x0B9E && c <= 0x0B9F) || + (c >= 0x0BA3 && c <= 0x0BA4) || + (c >= 0x0BA8 && c <= 0x0BAA) || + (c >= 0x0BAE && c <= 0x0BB5) || + (c >= 0x0BB7 && c <= 0x0BB9) || + (c >= 0x0C05 && c <= 0x0C0C) || + (c >= 0x0C0E && c <= 0x0C10) || + (c >= 0x0C12 && c <= 0x0C28) || + (c >= 0x0C2A && c <= 0x0C33) || + (c >= 0x0C35 && c <= 0x0C39) || + (c >= 0x0C60 && c <= 0x0C61) || + (c >= 0x0C85 && c <= 0x0C8C) || + (c >= 0x0C8E && c <= 0x0C90) || + (c >= 0x0C92 && c <= 0x0CA8) || + (c >= 0x0CAA && c <= 0x0CB3) || + (c >= 0x0CB5 && c <= 0x0CB9) || + c == 0x0CDE || + (c >= 0x0CE0 && c <= 0x0CE1) || + (c >= 0x0D05 && c <= 0x0D0C) || + (c >= 0x0D0E && c <= 0x0D10) || + (c >= 0x0D12 && c <= 0x0D28) || + (c >= 0x0D2A && c <= 0x0D39) || + (c >= 0x0D60 && c <= 0x0D61) || + (c >= 0x0E01 && c <= 0x0E2E) || + c == 0x0E30 || + (c >= 0x0E32 && c <= 0x0E33) || + (c >= 0x0E40 && c <= 0x0E45) || + (c >= 0x0E81 && c <= 0x0E82) || + c == 0x0E84 || + (c >= 0x0E87 && c <= 0x0E88) || + c == 0x0E8A || + c == 0x0E8D || + (c >= 0x0E94 && c <= 0x0E97) || + (c >= 0x0E99 && c <= 0x0E9F) || + (c >= 0x0EA1 && c <= 0x0EA3) || + c == 0x0EA5 || + c == 0x0EA7 || + (c >= 0x0EAA && c <= 0x0EAB) || + (c >= 0x0EAD && c <= 0x0EAE) || + c == 0x0EB0 || + (c >= 0x0EB2 && c <= 0x0EB3) || + c == 0x0EBD || + (c >= 0x0EC0 && c <= 0x0EC4) || + (c >= 0x0F40 && c <= 0x0F47) || + (c >= 0x0F49 && c <= 0x0F69) || + (c >= 0x10A0 && c <= 0x10C5) || + (c >= 0x10D0 && c <= 0x10F6) || + c == 0x1100 || + (c >= 0x1102 && c <= 0x1103) || + (c >= 0x1105 && c <= 0x1107) || + c == 0x1109 || + (c >= 0x110B && c <= 0x110C) || + (c >= 0x110E && c <= 0x1112) || + c == 0x113C || + c == 0x113E || + c == 0x1140 || + c == 0x114C || + c == 0x114E || + c == 0x1150 || + (c >= 0x1154 && c <= 0x1155) || + c == 0x1159 || + (c >= 0x115F && c <= 0x1161) || + c == 0x1163 || + c == 0x1165 || + c == 0x1167 || + c == 0x1169 || + (c >= 0x116D && c <= 0x116E) || + (c >= 0x1172 && c <= 0x1173) || + c == 0x1175 || + c == 0x119E || + c == 0x11A8 || + c == 0x11AB || + (c >= 0x11AE && c <= 0x11AF) || + (c >= 0x11B7 && c <= 0x11B8) || + c == 0x11BA || + (c >= 0x11BC && c <= 0x11C2) || + c == 0x11EB || + c == 0x11F0 || + c == 0x11F9 || + (c >= 0x1E00 && c <= 0x1E9B) || + (c >= 0x1EA0 && c <= 0x1EF9) || + (c >= 0x1F00 && c <= 0x1F15) || + (c >= 0x1F18 && c <= 0x1F1D) || + (c >= 0x1F20 && c <= 0x1F45) || + (c >= 0x1F48 && c <= 0x1F4D) || + (c >= 0x1F50 && c <= 0x1F57) || + c == 0x1F59 || + c == 0x1F5B || + c == 0x1F5D || + (c >= 0x1F5F && c <= 0x1F7D) || + (c >= 0x1F80 && c <= 0x1FB4) || + (c >= 0x1FB6 && c <= 0x1FBC) || + c == 0x1FBE || + (c >= 0x1FC2 && c <= 0x1FC4) || + (c >= 0x1FC6 && c <= 0x1FCC) || + (c >= 0x1FD0 && c <= 0x1FD3) || + (c >= 0x1FD6 && c <= 0x1FDB) || + (c >= 0x1FE0 && c <= 0x1FEC) || + (c >= 0x1FF2 && c <= 0x1FF4) || + (c >= 0x1FF6 && c <= 0x1FFC) || + c == 0x2126 || + (c >= 0x212A && c <= 0x212B) || + c == 0x212E || + (c >= 0x2180 && c <= 0x2182) || + (c >= 0x3041 && c <= 0x3094) || + (c >= 0x30A1 && c <= 0x30FA) || + (c >= 0x3105 && c <= 0x312C) || + (c >= 0xAC00 && c <= 0xD7A3)) + return true; // BaseChar + if ((c >= 0x4e00 && c <= 0x9fa5) || + c == 0x3007 || + (c >= 0x3021 && c <= 0x3029)) + return true; // Ideographic + return false; + } + + /** + * Indicates whether the specified Unicode character matches the Digit + * production. + */ + public static boolean isDigit(int c) + { + return ((c >= 0x0030 && c <= 0x0039) || + (c >= 0x0660 && c <= 0x0669) || + (c >= 0x06F0 && c <= 0x06F9) || + (c >= 0x0966 && c <= 0x096F) || + (c >= 0x09E6 && c <= 0x09EF) || + (c >= 0x0A66 && c <= 0x0A6F) || + (c >= 0x0AE6 && c <= 0x0AEF) || + (c >= 0x0B66 && c <= 0x0B6F) || + (c >= 0x0BE7 && c <= 0x0BEF) || + (c >= 0x0C66 && c <= 0x0C6F) || + (c >= 0x0CE6 && c <= 0x0CEF) || + (c >= 0x0D66 && c <= 0x0D6F) || + (c >= 0x0E50 && c <= 0x0E59) || + (c >= 0x0ED0 && c <= 0x0ED9) || + (c >= 0x0F20 && c <= 0x0F29)); + } + + /** + * Indicates whether the specified Unicode character matches the + * CombiningChar production. + */ + public static boolean isCombiningChar(int c) + { + return ((c >= 0x0300 && c <= 0x0345) || + (c >= 0x0360 && c <= 0x0361) || + (c >= 0x0483 && c <= 0x0486) || + (c >= 0x0591 && c <= 0x05A1) || + (c >= 0x05A3 && c <= 0x05B9) || + (c >= 0x05BB && c <= 0x05BD) || + c == 0x05BF || + (c >= 0x05C1 && c <= 0x05C2) || + c == 0x05C4 || + (c >= 0x064B && c <= 0x0652) || + c == 0x0670 || + (c >= 0x06D6 && c <= 0x06DC) || + (c >= 0x06DD && c <= 0x06DF) || + (c >= 0x06E0 && c <= 0x06E4) || + (c >= 0x06E7 && c <= 0x06E8) || + (c >= 0x06EA && c <= 0x06ED) || + (c >= 0x0901 && c <= 0x0903) || + c == 0x093C || + (c >= 0x093E && c <= 0x094C) || + c == 0x094D || + (c >= 0x0951 && c <= 0x0954) || + (c >= 0x0962 && c <= 0x0963) || + (c >= 0x0981 && c <= 0x0983) || + c == 0x09BC || + c == 0x09BE || + c == 0x09BF || + (c >= 0x09C0 && c <= 0x09C4) || + (c >= 0x09C7 && c <= 0x09C8) || + (c >= 0x09CB && c <= 0x09CD) || + c == 0x09D7 || + (c >= 0x09E2 && c <= 0x09E3) || + c == 0x0A02 || + c == 0x0A3C || + c == 0x0A3E || + c == 0x0A3F || + (c >= 0x0A40 && c <= 0x0A42) || + (c >= 0x0A47 && c <= 0x0A48) || + (c >= 0x0A4B && c <= 0x0A4D) || + (c >= 0x0A70 && c <= 0x0A71) || + (c >= 0x0A81 && c <= 0x0A83) || + c == 0x0ABC || + (c >= 0x0ABE && c <= 0x0AC5) || + (c >= 0x0AC7 && c <= 0x0AC9) || + (c >= 0x0ACB && c <= 0x0ACD) || + (c >= 0x0B01 && c <= 0x0B03) || + c == 0x0B3C || + (c >= 0x0B3E && c <= 0x0B43) || + (c >= 0x0B47 && c <= 0x0B48) || + (c >= 0x0B4B && c <= 0x0B4D) || + (c >= 0x0B56 && c <= 0x0B57) || + (c >= 0x0B82 && c <= 0x0B83) || + (c >= 0x0BBE && c <= 0x0BC2) || + (c >= 0x0BC6 && c <= 0x0BC8) || + (c >= 0x0BCA && c <= 0x0BCD) || + c == 0x0BD7 || + (c >= 0x0C01 && c <= 0x0C03) || + (c >= 0x0C3E && c <= 0x0C44) || + (c >= 0x0C46 && c <= 0x0C48) || + (c >= 0x0C4A && c <= 0x0C4D) || + (c >= 0x0C55 && c <= 0x0C56) || + (c >= 0x0C82 && c <= 0x0C83) || + (c >= 0x0CBE && c <= 0x0CC4) || + (c >= 0x0CC6 && c <= 0x0CC8) || + (c >= 0x0CCA && c <= 0x0CCD) || + (c >= 0x0CD5 && c <= 0x0CD6) || + (c >= 0x0D02 && c <= 0x0D03) || + (c >= 0x0D3E && c <= 0x0D43) || + (c >= 0x0D46 && c <= 0x0D48) || + (c >= 0x0D4A && c <= 0x0D4D) || + c == 0x0D57 || + c == 0x0E31 || + (c >= 0x0E34 && c <= 0x0E3A) || + (c >= 0x0E47 && c <= 0x0E4E) || + c == 0x0EB1 || + (c >= 0x0EB4 && c <= 0x0EB9) || + (c >= 0x0EBB && c <= 0x0EBC) || + (c >= 0x0EC8 && c <= 0x0ECD) || + (c >= 0x0F18 && c <= 0x0F19) || + c == 0x0F35 || + c == 0x0F37 || + c == 0x0F39 || + c == 0x0F3E || + c == 0x0F3F || + (c >= 0x0F71 && c <= 0x0F84) || + (c >= 0x0F86 && c <= 0x0F8B) || + (c >= 0x0F90 && c <= 0x0F95) || + c == 0x0F97 || + (c >= 0x0F99 && c <= 0x0FAD) || + (c >= 0x0FB1 && c <= 0x0FB7) || + c == 0x0FB9 || + (c >= 0x20D0 && c <= 0x20DC) || + c == 0x20E1 || + (c >= 0x302A && c <= 0x302F) || + c == 0x3099 || + c == 0x309A); + } + + /** + * Indicates whether the specified Unicode character matches the Extender + * production. + */ + public static boolean isExtender(int c) + { + return (c == 0x00B7 || + c == 0x02D0 || + c == 0x02D1 || + c == 0x0387 || + c == 0x0640 || + c == 0x0E46 || + c == 0x0EC6 || + c == 0x3005 || + (c >= 0x3031 && c <= 0x3035) || + (c >= 0x309D && c <= 0x309E) || + (c >= 0x30FC && c <= 0x30FE)); + } + + /** + * Indicates whether the specified Unicode character matches the Char + * production. + */ + public static boolean isChar(int c) + { + return (c >= 0x20 && c < 0xd800) || + (c >= 0xe00 && c < 0xfffd) || // NB exclude 0xfffd + (c >= 0x10000 && c < 0x110000) || + c == 0xa || c == 0x9 || c == 0xd; + } + + /** + * Interns the specified text or not, depending on the value of + * stringInterning. + */ + private String intern(String text) + { + return stringInterning ? text.intern() : text; + } + + /** + * Report a parsing error. + */ + private void error(String message) + throws XMLStreamException + { + error(message, null); + } + + /** + * Report a parsing error. + */ + private void error(String message, Object info) + throws XMLStreamException + { + if (info != null) + { + if (info instanceof String) + message += ": \"" + ((String) info) + "\""; + else if (info instanceof Character) + message += ": '" + ((Character) info) + "'"; + } + throw new XMLStreamException(message); + } + + /** + * Perform validation of a start-element event. + */ + private void validateStartElement(String elementName) + throws XMLStreamException + { + if (currentContentModel == null) + { + // root element + // VC: Root Element Type + if (!elementName.equals(doctype.rootName)) + error("root element name must match name in DTD"); + return; + } + // VC: Element Valid + switch (currentContentModel.type) + { + case ContentModel.EMPTY: + error("child element found in empty element", elementName); + break; + case ContentModel.ELEMENT: + LinkedList ctx = (LinkedList) validationStack.getLast(); + ctx.add(elementName); + break; + case ContentModel.MIXED: + MixedContentModel mm = (MixedContentModel) currentContentModel; + if (!mm.containsName(elementName)) + error("illegal element for content model", elementName); + break; + } + } + + /** + * Perform validation of an end-element event. + */ + private void validateEndElement() + throws XMLStreamException + { + if (currentContentModel == null) + { + // root element + // VC: IDREF + if (!idrefs.containsAll(ids)) + error("IDREF values must match the value of some ID attribute"); + return; + } + // VC: Element Valid + switch (currentContentModel.type) + { + case ContentModel.ELEMENT: + LinkedList ctx = (LinkedList) validationStack.getLast(); + ElementContentModel ecm = (ElementContentModel) currentContentModel; + validateElementContent(ecm, ctx); + break; + } + } + + /** + * Perform validation of character data. + */ + private void validatePCData(String text) + throws XMLStreamException + { + // VC: Element Valid + switch (currentContentModel.type) + { + case ContentModel.EMPTY: + error("character data found in empty element", text); + break; + case ContentModel.ELEMENT: + boolean white = true; + int len = text.length(); + for (int i = 0; i < len; i++) + { + char c = text.charAt(i); + if (c != ' ' && c != '\t' && c != '\n' && c != '\r') + { + white = false; + break; + } + } + if (!white) + error("character data found in element with element content", text); + else if (xmlStandalone == Boolean.TRUE && currentContentModel.external) + // VC: Standalone Document Declaration + error("whitespace in element content of externally declared " + + "element in standalone document"); + break; + } + } + + /** + * Validates the specified validation context (list of child elements) + * against the element content model for the current element. + */ + private void validateElementContent(ElementContentModel model, + LinkedList children) + throws XMLStreamException + { + // Use regular expression + StringBuffer buf = new StringBuffer(); + for (Iterator i = children.iterator(); i.hasNext(); ) + { + buf.append((String) i.next()); + buf.append(' '); + } + String c = buf.toString(); + String regex = createRegularExpression(model); + if (!c.matches(regex)) + error("element content "+model.text+" does not match expression "+regex, c); + } + + /** + * Creates the regular expression used to validate an element content + * model. + */ + private String createRegularExpression(ElementContentModel model) + { + if (model.regex == null) + { + StringBuffer buf = new StringBuffer(); + buf.append('('); + for (Iterator i = model.contentParticles.iterator(); i.hasNext(); ) + { + ContentParticle cp = (ContentParticle) i.next(); + if (cp.content instanceof String) + { + buf.append('('); + buf.append((String) cp.content); + buf.append(' '); + buf.append(')'); + if (cp.max == -1) + { + if (cp.min == 0) + buf.append('*'); + else + buf.append('+'); + } + else if (cp.min == 0) + buf.append('?'); + } + else + { + ElementContentModel ecm = (ElementContentModel) cp.content; + buf.append(createRegularExpression(ecm)); + } + if (model.or && i.hasNext()) + buf.append('|'); + } + buf.append(')'); + if (model.max == -1) + { + if (model.min == 0) + buf.append('*'); + else + buf.append('+'); + } + else if (model.min == 0) + buf.append('?'); + model.regex = buf.toString(); + } + return model.regex; + } + + /** + * Performs validation of a document type declaration event. + */ + void validateDoctype() + throws XMLStreamException + { + for (Iterator i = doctype.entityIterator(); i.hasNext(); ) + { + Map.Entry entry = (Map.Entry) i.next(); + Object entity = entry.getValue(); + if (entity instanceof ExternalIds) + { + ExternalIds ids = (ExternalIds) entity; + if (ids.notationName != null) + { + // VC: Notation Declared + ExternalIds notation = doctype.getNotation(ids.notationName); + if (notation == null) + error("Notation name must match the declared name of a " + + "notation", ids.notationName); + } + } + } + } + + /** + * Simple test harness for reading an XML file. + * args[0] is the filename of the XML file + * If args[1] is "-x", enable XInclude processing + */ + public static void main(String[] args) + throws Exception + { + boolean xIncludeAware = false; + if (args.length > 1 && "-x".equals(args[1])) + xIncludeAware = true; + XMLParser p = new XMLParser(new java.io.FileInputStream(args[0]), + absolutize(null, args[0]), + true, // validating + true, // namespaceAware + true, // coalescing, + true, // replaceERefs + true, // externalEntities + true, // supportDTD + true, // baseAware + true, // stringInterning + null, + null); + XMLStreamReader reader = p; + if (xIncludeAware) + reader = new XIncludeFilter(p, args[0], true, true, true); + try + { + int event; + //do + while (reader.hasNext()) + { + event = reader.next(); + Location loc = reader.getLocation(); + System.out.print(loc.getLineNumber()+":"+loc.getColumnNumber()+" "); + switch (event) + { + case XMLStreamConstants.START_DOCUMENT: + System.out.println("START_DOCUMENT version="+reader.getVersion()+ + " encoding="+reader.getEncoding()); + break; + case XMLStreamConstants.END_DOCUMENT: + System.out.println("END_DOCUMENT"); + break; + case XMLStreamConstants.START_ELEMENT: + System.out.println("START_ELEMENT "+reader.getName()); + int l = reader.getNamespaceCount(); + for (int i = 0; i < l; i++) + System.out.println("\tnamespace "+reader.getNamespacePrefix(i)+ + "='"+reader.getNamespaceURI(i)+"'"); + l = reader.getAttributeCount(); + for (int i = 0; i < l; i++) + System.out.println("\tattribute "+reader.getAttributeQName(i)+ + "='"+reader.getAttributeValue(i)+"'"); + break; + case XMLStreamConstants.END_ELEMENT: + System.out.println("END_ELEMENT "+reader.getName()); + break; + case XMLStreamConstants.CHARACTERS: + System.out.println("CHARACTERS '"+encodeText(reader.getText())+"'"); + break; + case XMLStreamConstants.CDATA: + System.out.println("CDATA '"+encodeText(reader.getText())+"'"); + break; + case XMLStreamConstants.SPACE: + System.out.println("SPACE '"+encodeText(reader.getText())+"'"); + break; + case XMLStreamConstants.DTD: + System.out.println("DTD "+reader.getText()); + break; + case XMLStreamConstants.ENTITY_REFERENCE: + System.out.println("ENTITY_REFERENCE "+reader.getText()); + break; + case XMLStreamConstants.COMMENT: + System.out.println("COMMENT '"+encodeText(reader.getText())+"'"); + break; + case XMLStreamConstants.PROCESSING_INSTRUCTION: + System.out.println("PROCESSING_INSTRUCTION "+reader.getPITarget()+ + " "+reader.getPIData()); + break; + case XMLStreamConstants.START_ENTITY: + System.out.println("START_ENTITY "+reader.getText()); + break; + case XMLStreamConstants.END_ENTITY: + System.out.println("END_ENTITY "+reader.getText()); + break; + default: + System.out.println("Unknown event: "+event); + } + } + } + catch (XMLStreamException e) + { + Location l = reader.getLocation(); + System.out.println("At line "+l.getLineNumber()+ + ", column "+l.getColumnNumber()+ + " of "+l.getLocationURI()); + throw e; + } + } + + /** + * Escapes control characters in the specified text. For debugging. + */ + private static String encodeText(String text) + { + StringBuffer b = new StringBuffer(); + int len = text.length(); + for (int i = 0; i < len; i++) + { + char c = text.charAt(i); + switch (c) + { + case '\t': + b.append("\\t"); + break; + case '\n': + b.append("\\n"); + break; + case '\r': + b.append("\\r"); + break; + default: + b.append(c); + } + } + return b.toString(); + } + + /** + * An attribute instance. + */ + class Attribute + { + + /** + * Attribute name. + */ + final String name; + + /** + * Attribute type as declared in the DTD, or CDATA otherwise. + */ + final String type; + + /** + * Whether the attribute was specified or defaulted. + */ + final boolean specified; + + /** + * The attribute value. + */ + final String value; + + /** + * The namespace prefix. + */ + final String prefix; + + /** + * The namespace local-name. + */ + final String localName; + + Attribute(String name, String type, boolean specified, String value) + { + this.name = name; + this.type = type; + this.specified = specified; + this.value = value; + int ci = name.indexOf(':'); + if (ci == -1) + { + prefix = null; + localName = intern(name); + } + else + { + prefix = intern(name.substring(0, ci)); + localName = intern(name.substring(ci + 1)); + } + } + + public boolean equals(Object other) + { + if (other instanceof Attribute) + { + Attribute a = (Attribute) other; + if (namespaceAware) + { + if (!a.localName.equals(localName)) + return false; + String auri = getNamespaceURI(a.prefix); + String uri = getNamespaceURI(prefix); + if (uri == null && (auri == null || + (input.xml11 && "".equals(auri)))) + return true; + if (uri != null) + { + if ("".equals(uri) && input.xml11 && "".equals(auri)) + return true; + return uri.equals(auri); + } + return false; + } + else + return a.name.equals(name); + } + return false; + } + + } + + /** + * Representation of a DTD. + */ + class Doctype + { + + /** + * Name of the root element. + */ + final String rootName; + + /** + * Public ID, if any, of external subset. + */ + final String publicId; + + /** + * System ID (URL), if any, of external subset. + */ + final String systemId; + + /** + * Map of element names to content models. + */ + private final LinkedHashMap elements = new LinkedHashMap(); + + /** + * Map of element names to maps of attribute declarations. + */ + private final LinkedHashMap attlists = new LinkedHashMap(); + + /** + * Map of entity names to entities (String or ExternalIds). + */ + private final LinkedHashMap entities = new LinkedHashMap(); + + /** + * Map of notation names to ExternalIds. + */ + private final LinkedHashMap notations = new LinkedHashMap(); + + /** + * Map of anonymous keys to comments. + */ + private final LinkedHashMap comments = new LinkedHashMap(); + + /** + * Map of anonymous keys to processing instructions (String[2] + * containing {target, data}). + */ + private final LinkedHashMap pis = new LinkedHashMap(); + + /** + * List of keys to all markup entries in the DTD. + */ + private final LinkedList entries = new LinkedList(); + + /** + * Set of the entities defined in the external subset. + */ + private final HashSet externalEntities = new HashSet(); + + /** + * Set of the notations defined in the external subset. + */ + private final HashSet externalNotations = new HashSet(); + + /** + * Counter for making anonymous keys. + */ + private int anon = 1; + + /** + * Constructor. + */ + Doctype(String rootName, String publicId, String systemId) + { + this.rootName = rootName; + this.publicId = publicId; + this.systemId = systemId; + } + + /** + * Adds an element declaration. + * @param name the element name + * @param text the content model text + * @param model the parsed content model + */ + void addElementDecl(String name, String text, ContentModel model) + { + if (elements.containsKey(name)) + return; + model.text = text; + model.external = (inputStack.size() != 1); + elements.put(name, model); + entries.add("E" + name); + } + + /** + * Adds an attribute declaration. + * @param ename the element name + * @param aname the attribute name + * @param decl the attribute declaration details + */ + void addAttributeDecl(String ename, String aname, AttributeDecl decl) + { + LinkedHashMap attlist = (LinkedHashMap) attlists.get(ename); + if (attlist == null) + { + attlist = new LinkedHashMap(); + attlists.put(ename, attlist); + } + else if (attlist.containsKey(aname)) + return; + attlist.put(aname, decl); + String key = "A" + ename; + if (!entries.contains(key)) + entries.add(key); + } + + /** + * Adds an entity declaration. + * @param name the entity name + * @param text the entity replacement text + * @param inExternalSubset if we are in the exernal subset + */ + void addEntityDecl(String name, String text, boolean inExternalSubset) + { + if (entities.containsKey(name)) + return; + entities.put(name, text); + entries.add("e" + name); + if (inExternalSubset) + externalEntities.add(name); + } + + /** + * Adds an entity declaration. + * @param name the entity name + * @param ids the external IDs + * @param inExternalSubset if we are in the exernal subset + */ + void addEntityDecl(String name, ExternalIds ids, boolean inExternalSubset) + { + if (entities.containsKey(name)) + return; + entities.put(name, ids); + entries.add("e" + name); + if (inExternalSubset) + externalEntities.add(name); + } + + /** + * Adds a notation declaration. + * @param name the notation name + * @param ids the external IDs + * @param inExternalSubset if we are in the exernal subset + */ + void addNotationDecl(String name, ExternalIds ids, boolean inExternalSubset) + { + if (notations.containsKey(name)) + return; + notations.put(name, ids); + entries.add("n" + name); + if (inExternalSubset) + externalNotations.add(name); + } + + /** + * Adds a comment. + */ + void addComment(String text) + { + String key = Integer.toString(anon++); + comments.put(key, text); + entries.add("c" + key); + } + + /** + * Adds a processing instruction. + */ + void addPI(String target, String data) + { + String key = Integer.toString(anon++); + pis.put(key, new String[] {target, data}); + entries.add("p" + key); + } + + /** + * Returns the content model for the specified element. + * @param name the element name + */ + ContentModel getElementModel(String name) + { + return (ContentModel) elements.get(name); + } + + /** + * Returns the attribute definition for the given attribute + * @param ename the element name + * @param aname the attribute name + */ + AttributeDecl getAttributeDecl(String ename, String aname) + { + LinkedHashMap attlist = (LinkedHashMap) attlists.get(ename); + return (attlist == null) ? null : (AttributeDecl) attlist.get(aname); + } + + /** + * Indicates whether the specified attribute was declared in the DTD. + * @param ename the element name + * @param aname the attribute name + */ + boolean isAttributeDeclared(String ename, String aname) + { + LinkedHashMap attlist = (LinkedHashMap) attlists.get(ename); + return (attlist == null) ? false : attlist.containsKey(aname); + } + + /** + * Returns an iterator over the entries in the attribute list for the + * given element. + * @param ename the element name + */ + Iterator attlistIterator(String ename) + { + LinkedHashMap attlist = (LinkedHashMap) attlists.get(ename); + return (attlist == null) ? Collections.EMPTY_LIST.iterator() : + attlist.entrySet().iterator(); + } + + /** + * Returns the entity (String or ExternalIds) for the given entity name. + */ + Object getEntity(String name) + { + return entities.get(name); + } + + /** + * Indicates whether the specified entity was declared in the external + * subset. + */ + boolean isEntityExternal(String name) + { + return externalEntities.contains(name); + } + + /** + * Returns an iterator over the entity map entries. + */ + Iterator entityIterator() + { + return entities.entrySet().iterator(); + } + + /** + * Returns the notation IDs for the given notation name. + */ + ExternalIds getNotation(String name) + { + return (ExternalIds) notations.get(name); + } + + /** + * Indicates whether the specified notation was declared in the external + * subset. + */ + boolean isNotationExternal(String name) + { + return externalNotations.contains(name); + } + + /** + * Returns the comment associated with the specified (anonymous) key. + */ + String getComment(String key) + { + return (String) comments.get(key); + } + + /** + * Returns the processing instruction associated with the specified + * (anonymous) key. + */ + String[] getPI(String key) + { + return (String[]) pis.get(key); + } + + /** + * Returns an iterator over the keys of the markup entries in this DTD, + * in the order declared. + */ + Iterator entryIterator() + { + return entries.iterator(); + } + + } + + /** + * Combination of an ExternalID and an optional NDataDecl. + */ + class ExternalIds + { + + /** + * The public ID. + */ + String publicId; + + /** + * The system ID. + */ + String systemId; + + /** + * The notation name declared with the NDATA keyword. + */ + String notationName; + } + + /** + * A content model. + */ + abstract class ContentModel + { + static final int EMPTY = 0; + static final int ANY = 1; + static final int ELEMENT = 2; + static final int MIXED = 3; + + int min; + int max; + final int type; + String text; + boolean external; + + ContentModel(int type) + { + this.type = type; + min = 1; + max = 1; + } + + } + + /** + * The EMPTY content model. + */ + class EmptyContentModel + extends ContentModel + { + + EmptyContentModel() + { + super(ContentModel.EMPTY); + min = 0; + max = 0; + } + + } + + /** + * The ANY content model. + */ + class AnyContentModel + extends ContentModel + { + + AnyContentModel() + { + super(ContentModel.ANY); + min = 0; + max = -1; + } + + } + + /** + * An element content model. + */ + class ElementContentModel + extends ContentModel + { + + LinkedList contentParticles; + boolean or; + String regex; // regular expression cache + + ElementContentModel() + { + super(ContentModel.ELEMENT); + contentParticles = new LinkedList(); + } + + void addContentParticle(ContentParticle cp) + { + contentParticles.add(cp); + } + + } + + class ContentParticle + { + + int min = 1; + int max = 1; + Object content; // Name (String) or ElementContentModel + + } + + /** + * A mixed content model. + */ + class MixedContentModel + extends ContentModel + { + + private HashSet names; + + MixedContentModel() + { + super(ContentModel.MIXED); + names = new HashSet(); + } + + void addName(String name) + { + names.add(name); + } + + boolean containsName(String name) + { + return names.contains(name); + } + + } + + /** + * An attribute definition. + */ + class AttributeDecl + { + + /** + * The attribute type (CDATA, ID, etc). + */ + final String type; + + /** + * The default value. + */ + final String value; + + /** + * The value type (#FIXED, #IMPLIED, etc). + */ + final int valueType; + + /** + * The enumeration text. + */ + final String enumeration; + + /** + * The enumeration tokens. + */ + final HashSet values; + + /** + * Whether this attribute declaration occurred in the external subset. + */ + final boolean external; + + AttributeDecl(String type, String value, + int valueType, String enumeration, + HashSet values, boolean external) + { + this.type = type; + this.value = value; + this.valueType = valueType; + this.enumeration = enumeration; + this.values = values; + this.external = external; + } + + } + + /** + * Compatibility interface that can be used to resolve based on a public + * ID, not just an URL. + */ + interface XMLResolver2 + extends XMLResolver + { + + InputStream resolve(String publicId, String systemId) + throws XMLStreamException; + + } + + /** + * An XML input source. + */ + static class Input + implements Location + { + + int line = 1, markLine; + int column, markColumn; + int offset, markOffset; + final String publicId, systemId, name; + final boolean report; // report start- and end-entity + final boolean normalize; // normalize CR, etc to LF + + InputStream in; + Reader reader; + UnicodeReader unicodeReader; + boolean initialized; + String inputEncoding; + boolean xml11; + + Input(InputStream in, Reader reader, String publicId, String systemId, + String name, String inputEncoding, boolean report, + boolean normalize) + { + if (inputEncoding == null) + inputEncoding = "UTF-8"; + this.inputEncoding = inputEncoding; + this.publicId = publicId; + this.systemId = systemId; + this.name = name; + this.report = report; + this.normalize = normalize; + if (in != null) + { + if (reader != null) + throw new IllegalStateException("both byte and char streams "+ + "specified"); + if (normalize) + in = new CRLFInputStream(in); + in = new BufferedInputStream(in); + this.in = in; + } + else + { + this.reader = normalize ? new CRLFReader(reader) : reader; + unicodeReader = new UnicodeReader(this.reader); + } + initialized = false; + } + + // -- Location -- + + public int getCharacterOffset() + { + return offset; + } + + public int getColumnNumber() + { + return column; + } + + public int getLineNumber() + { + return line; + } + + public String getLocationURI() + { + return systemId; + } + + void init() + throws IOException + { + if (initialized) + return; + if (in != null) + detectEncoding(); + initialized = true; + } + + void mark(int len) + throws IOException + { + //System.out.println(" mark:"+len); + markOffset = offset; + markLine = line; + markColumn = column; + if (unicodeReader != null) + unicodeReader.mark(len); + else + in.mark(len); + } + + /** + * Character read. + */ + int read() + throws IOException + { + offset++; + int ret = (unicodeReader != null) ? unicodeReader.read() : in.read(); + if (normalize && + (ret == 0x0d || (xml11 && (ret == 0x85 || ret == 0x2028)))) + { + // Normalize CR etc to LF + ret = 0x0a; + } + // Locator handling + if (ret == 0x0a) + { + line++; + column = 0; + } + else + column++; + return ret; + } + + /** + * Block read. + */ + int read(int[] b, int off, int len) + throws IOException + { + int ret; + if (unicodeReader != null) + ret = unicodeReader.read(b, off, len); + else + { + byte[] b2 = new byte[len]; + ret = in.read(b2, 0, len); + if (ret != -1) + { + String s = new String(b2, 0, ret, inputEncoding); + int[] c = UnicodeReader.toCodePointArray(s); + ret = c.length; + System.arraycopy(c, 0, b, off, ret); + } + } + if (ret != -1) + { + // Locator handling + for (int i = 0; i < ret; i++) + { + int c = b[off + i]; + if (normalize && + (c == 0x0d || (xml11 && (c == 0x85 || c == 0x2028)))) + { + // Normalize CR etc to LF + c = 0x0a; + b[off + i] = c; + } + if (c == 0x0a) + { + line++; + column = 0; + } + else + column++; + } + } + return ret; + } + + void reset() + throws IOException + { + if (unicodeReader != null) + unicodeReader.reset(); + else + in.reset(); + offset = markOffset; + line = markLine; + column = markColumn; + } + + // Detection of input encoding + + private static final int[] SIGNATURE_UCS_4_1234 = + new int[] { 0x00, 0x00, 0x00, 0x3c }; + private static final int[] SIGNATURE_UCS_4_4321 = + new int[] { 0x3c, 0x00, 0x00, 0x00 }; + private static final int[] SIGNATURE_UCS_4_2143 = + new int[] { 0x00, 0x00, 0x3c, 0x00 }; + private static final int[] SIGNATURE_UCS_4_3412 = + new int[] { 0x00, 0x3c, 0x00, 0x00 }; + private static final int[] SIGNATURE_UCS_2_12 = + new int[] { 0xfe, 0xff }; + private static final int[] SIGNATURE_UCS_2_21 = + new int[] { 0xff, 0xfe }; + private static final int[] SIGNATURE_UCS_2_12_NOBOM = + new int[] { 0x00, 0x3c, 0x00, 0x3f }; + private static final int[] SIGNATURE_UCS_2_21_NOBOM = + new int[] { 0x3c, 0x00, 0x3f, 0x00 }; + private static final int[] SIGNATURE_UTF_8 = + new int[] { 0x3c, 0x3f, 0x78, 0x6d }; + private static final int[] SIGNATURE_UTF_8_BOM = + new int[] { 0xef, 0xbb, 0xbf }; + + /** + * Detect the input encoding. + */ + private void detectEncoding() + throws IOException + { + int[] signature = new int[4]; + in.mark(4); + for (int i = 0; i < 4; i++) + signature[i] = in.read(); + in.reset(); + + // 4-byte encodings + if (equals(SIGNATURE_UCS_4_1234, signature)) + { + in.read(); + in.read(); + in.read(); + in.read(); + setInputEncoding("UTF-32BE"); + } + else if (equals(SIGNATURE_UCS_4_4321, signature)) + { + in.read(); + in.read(); + in.read(); + in.read(); + setInputEncoding("UTF-32LE"); + } + else if (equals(SIGNATURE_UCS_4_2143, signature) || + equals(SIGNATURE_UCS_4_3412, signature)) + throw new UnsupportedEncodingException("unsupported UCS-4 byte ordering"); + + // 2-byte encodings + else if (equals(SIGNATURE_UCS_2_12, signature)) + { + in.read(); + in.read(); + setInputEncoding("UTF-16BE"); + } + else if (equals(SIGNATURE_UCS_2_21, signature)) + { + in.read(); + in.read(); + setInputEncoding("UTF-16LE"); + } + else if (equals(SIGNATURE_UCS_2_12_NOBOM, signature)) + { + //setInputEncoding("UTF-16BE"); + throw new UnsupportedEncodingException("no byte-order mark for UCS-2 entity"); + } + else if (equals(SIGNATURE_UCS_2_21_NOBOM, signature)) + { + //setInputEncoding("UTF-16LE"); + throw new UnsupportedEncodingException("no byte-order mark for UCS-2 entity"); + } + // ASCII-derived encodings + else if (equals(SIGNATURE_UTF_8, signature)) + { + // UTF-8 input encoding implied, TextDecl + } + else if (equals(SIGNATURE_UTF_8_BOM, signature)) + { + in.read(); + in.read(); + in.read(); + setInputEncoding("UTF-8"); + } + } + + private static boolean equals(int[] b1, int[] b2) + { + for (int i = 0; i < b1.length; i++) + { + if (b1[i] != b2[i]) + return false; + } + return true; + } + + void setInputEncoding(String encoding) + throws IOException + { + if (encoding.equals(inputEncoding)) + return; + if ("UTF-16".equalsIgnoreCase(encoding) && + inputEncoding.startsWith("UTF-16")) + return; + if (reader != null) + throw new UnsupportedEncodingException("document is not in its " + + "declared encoding " + + inputEncoding + + ": " + encoding); + inputEncoding = encoding; + finalizeEncoding(); + } + + void finalizeEncoding() + throws IOException + { + if (reader != null) + return; + reader = new BufferedReader(new InputStreamReader(in, inputEncoding)); + unicodeReader = new UnicodeReader(reader); + mark(1); + } + + } + +} + diff --git a/libjava/classpath/gnu/xml/stream/XMLStreamWriterImpl.java b/libjava/classpath/gnu/xml/stream/XMLStreamWriterImpl.java index d677048cfa1..6157296c6b8 100644 --- a/libjava/classpath/gnu/xml/stream/XMLStreamWriterImpl.java +++ b/libjava/classpath/gnu/xml/stream/XMLStreamWriterImpl.java @@ -282,6 +282,8 @@ public class XMLStreamWriterImpl public void writeEndElement() throws XMLStreamException { + if (elements.isEmpty()) + throw new IllegalStateException("no matching start element"); try { endStartElement(); diff --git a/libjava/classpath/gnu/xml/transform/AttributeNode.java b/libjava/classpath/gnu/xml/transform/AttributeNode.java index 1e0eb1e961a..bc5bc30c9c1 100644 --- a/libjava/classpath/gnu/xml/transform/AttributeNode.java +++ b/libjava/classpath/gnu/xml/transform/AttributeNode.java @@ -1,5 +1,5 @@ /* AttributeNode.java -- - Copyright (C) 2004 Free Software Foundation, Inc. + Copyright (C) 2004,2006 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -75,13 +75,9 @@ final class AttributeNode namespace.clone(stylesheet), source); if (children != null) - { - ret.children = children.clone(stylesheet); - } + ret.children = children.clone(stylesheet); if (next != null) - { - ret.next = next.clone(stylesheet); - } + ret.next = next.clone(stylesheet); return ret; } @@ -113,9 +109,7 @@ final class AttributeNode // Use XPath string-value of fragment namespaceValue = Expr.stringValue(fragment); if (namespaceValue.length() == 0) - { - namespaceValue = null; - } + namespaceValue = null; } String prefix = getPrefix(nameValue); @@ -124,9 +118,7 @@ final class AttributeNode if (prefix != null) { if (XMLConstants.XML_NS_PREFIX.equals(prefix)) - { - namespaceValue = XMLConstants.XML_NS_URI; - } + namespaceValue = XMLConstants.XML_NS_URI; else { // Resolve namespace for this prefix @@ -153,9 +145,7 @@ final class AttributeNode // Resolve prefix for this namespace prefix = source.lookupPrefix(namespaceValue); if (prefix != null) - { - nameValue = prefix + ":" + nameValue; - } + nameValue = prefix + ":" + nameValue; else { if (namespaceValue != null) @@ -196,27 +186,19 @@ final class AttributeNode if (attrs != null) { if (namespace != null) - { - attrs.setNamedItemNS(attr); - } + attrs.setNamedItemNS(attr); else - { - attrs.setNamedItem(attr); - } + attrs.setNamedItem(attr); } if (children != null) - { - children.apply(stylesheet, mode, - context, pos, len, - attr, null); - } + children.apply(stylesheet, mode, + context, pos, len, + attr, null); } if (next != null) - { - next.apply(stylesheet, mode, - context, pos, len, - parent, nextSibling); - } + next.apply(stylesheet, mode, + context, pos, len, + parent, nextSibling); } final String getPrefix(String name) @@ -241,13 +223,9 @@ final class AttributeNode public boolean references(QName var) { if (name != null && name.references(var)) - { - return true; - } + return true; if (namespace != null && namespace.references(var)) - { - return true; - } + return true; return super.references(var); } diff --git a/libjava/classpath/gnu/xml/transform/Bindings.java b/libjava/classpath/gnu/xml/transform/Bindings.java index c372ea83091..4ee08322317 100644 --- a/libjava/classpath/gnu/xml/transform/Bindings.java +++ b/libjava/classpath/gnu/xml/transform/Bindings.java @@ -78,6 +78,11 @@ public class Bindings */ final LinkedList withParameters; + /** + * Only search globals. + */ + boolean global; + Bindings(Stylesheet stylesheet) { this.stylesheet = stylesheet; @@ -136,6 +141,12 @@ public class Bindings public boolean containsKey(QName name, int type) { + if (global) + { + Map ctx1 = (Map) variables.getLast(); + Map ctx2 = (Map) parameters.getLast(); + return (ctx1.containsKey(name) || ctx2.containsKey(name)); + } Iterator i = null; switch (type) { @@ -165,6 +176,17 @@ public class Bindings public Object get(QName name, Node context, int pos, int len) { + if (global) + { + Map ctx = (Map) variables.getLast(); + Object ret = ctx.get(name); + if (ret == null) + { + ctx = (Map) parameters.getLast(); + ret = ctx.get(name); + } + return ret; + } //System.err.println("bindings.get: "+name); //System.err.println("\t"+toString()); Object ret = null; diff --git a/libjava/classpath/gnu/xml/transform/ElementAvailableFunction.java b/libjava/classpath/gnu/xml/transform/ElementAvailableFunction.java index 84cb6207af9..aa15981695e 100644 --- a/libjava/classpath/gnu/xml/transform/ElementAvailableFunction.java +++ b/libjava/classpath/gnu/xml/transform/ElementAvailableFunction.java @@ -144,8 +144,8 @@ class ElementAvailableFunction { return elements.contains(localName) ? Boolean.TRUE : Boolean.FALSE; - // TODO extension elements } + // TODO extension elements return Boolean.FALSE; } @@ -153,16 +153,12 @@ class ElementAvailableFunction { NamespaceContext n = nsctx; if (context instanceof NamespaceContext) - { - n = (NamespaceContext) context; - } + n = (NamespaceContext) context; ElementAvailableFunction f = new ElementAvailableFunction(n); int len = args.size(); List args2 = new ArrayList(len); for (int i = 0; i < len; i++) - { - args2.add(((Expr) args.get(i)).clone(context)); - } + args2.add(((Expr) args.get(i)).clone(context)); f.setArguments(args2); return f; } @@ -172,12 +168,15 @@ class ElementAvailableFunction for (Iterator i = args.iterator(); i.hasNext(); ) { if (((Expr) i.next()).references(var)) - { - return true; - } + return true; } return false; } + public String toString() + { + return "element-available(" + args.get(0) + ")"; + } + } diff --git a/libjava/classpath/gnu/xml/transform/ElementNode.java b/libjava/classpath/gnu/xml/transform/ElementNode.java index d8f7f6db6db..092c56a4b64 100644 --- a/libjava/classpath/gnu/xml/transform/ElementNode.java +++ b/libjava/classpath/gnu/xml/transform/ElementNode.java @@ -82,14 +82,10 @@ final class ElementNode elementExcludeResultPrefixes = new HashSet(); StringTokenizer st = new StringTokenizer(attr.getNodeValue()); while (st.hasMoreTokens()) - { - elementExcludeResultPrefixes.add(st.nextToken()); - } + elementExcludeResultPrefixes.add(st.nextToken()); } else - { - elementExcludeResultPrefixes = Collections.EMPTY_SET; - } + elementExcludeResultPrefixes = Collections.EMPTY_SET; } TemplateNode clone(Stylesheet stylesheet) @@ -99,19 +95,15 @@ final class ElementNode namespace.clone(stylesheet), uas, source); if (children != null) - { - ret.children = children.clone(stylesheet); - } + ret.children = children.clone(stylesheet); if (next != null) - { - ret.next = next.clone(stylesheet); - } + ret.next = next.clone(stylesheet); return ret; } void doApply(Stylesheet stylesheet, QName mode, - Node context, int pos, int len, - Node parent, Node nextSibling) + Node context, int pos, int len, + Node parent, Node nextSibling) throws TransformerException { Document doc = (parent instanceof Document) ? (Document) parent : @@ -137,96 +129,70 @@ final class ElementNode // Use XPath string-value of fragment namespaceValue = Expr.stringValue(fragment); if (namespaceValue.length() == 0) - { - namespaceValue = null; - } - } - - String prefix = getPrefix(nameValue); - if (XMLConstants.XMLNS_ATTRIBUTE.equals(prefix)) - { - int ci = nameValue.indexOf(':'); - nameValue = nameValue.substring(ci + 1); + namespaceValue = null; } else - { - // Namespace aliasing - if (prefix == null) - { - prefix = "#default"; - } - String resultPrefix = - (String) stylesheet.namespaceAliases.get(prefix); - if (resultPrefix != null) - { - if ("#default".equals(resultPrefix)) - { - resultPrefix = null; - } - namespaceValue = source.lookupNamespaceURI(resultPrefix); - } - if (prefix == "#default") + { + String prefix = getPrefix(nameValue); + if (XMLConstants.XMLNS_ATTRIBUTE.equals(prefix)) { - prefix = null; + int ci = nameValue.indexOf(':'); + nameValue = nameValue.substring(ci + 1); } - // Look up ordinary namespace for this prefix - if (namespaceValue == null) + else { - if (XMLConstants.XML_NS_PREFIX.equals(prefix)) + // Namespace aliasing + if (prefix == null) + prefix = "#default"; + String resultPrefix = + (String) stylesheet.namespaceAliases.get(prefix); + if (resultPrefix != null) { - namespaceValue = XMLConstants.XML_NS_URI; + if ("#default".equals(resultPrefix)) + resultPrefix = null; + namespaceValue = source.lookupNamespaceURI(resultPrefix); } - else + if (prefix == "#default") + prefix = null; + // Look up ordinary namespace for this prefix + if (namespaceValue == null) { - // Resolve namespace for this prefix - namespaceValue = source.lookupNamespaceURI(prefix); + if (XMLConstants.XML_NS_PREFIX.equals(prefix)) + namespaceValue = XMLConstants.XML_NS_URI; + else + { + // Resolve namespace for this prefix + namespaceValue = source.lookupNamespaceURI(prefix); + } } } - /*if (prefix == null) - { - // Resolve prefix for this namespace - prefix = parent.lookupPrefix(namespaceValue); - if (prefix != null) - { - nameValue = prefix + ":" + nameValue; - } - }*/ } + // Create element Element element = (namespaceValue != null) ? doc.createElementNS(namespaceValue, nameValue) : - doc.createElement(nameValue); + doc.createElement(nameValue); if (nextSibling != null) - { - parent.insertBefore(element, nextSibling); - } + parent.insertBefore(element, nextSibling); else - { - parent.appendChild(element); - } + parent.appendChild(element); stylesheet.addNamespaceNodes(source, element, doc, elementExcludeResultPrefixes); if (uas != null) { StringTokenizer st = new StringTokenizer(uas, " "); while (st.hasMoreTokens()) - { - addAttributeSet(stylesheet, mode, context, pos, len, - element, null, st.nextToken()); - } + addAttributeSet(stylesheet, mode, context, pos, len, + element, null, st.nextToken()); } if (children != null) - { - children.apply(stylesheet, mode, - context, pos, len, - element, null); - } + children.apply(stylesheet, mode, + context, pos, len, + element, null); if (next != null) - { - next.apply(stylesheet, mode, - context, pos, len, - parent, nextSibling); - } + next.apply(stylesheet, mode, + context, pos, len, + parent, nextSibling); } final String getPrefix(String name) @@ -240,41 +206,33 @@ final class ElementNode Node parent, Node nextSibling, String attributeSet) throws TransformerException { + stylesheet.bindings.global = true; for (Iterator i = stylesheet.attributeSets.iterator(); i.hasNext(); ) { AttributeSet as = (AttributeSet) i.next(); if (!as.name.equals(attributeSet)) - { - continue; - } + continue; if (as.uas != null) { StringTokenizer st = new StringTokenizer(as.uas, " "); while (st.hasMoreTokens()) - { - addAttributeSet(stylesheet, mode, context, pos, len, - parent, nextSibling, st.nextToken()); - } + addAttributeSet(stylesheet, mode, context, pos, len, + parent, nextSibling, st.nextToken()); } if (as.children != null) - { - as.children.apply(stylesheet, mode, - context, pos, len, - parent, nextSibling); - } + as.children.apply(stylesheet, mode, + context, pos, len, + parent, nextSibling); } + stylesheet.bindings.global = false; } public boolean references(QName var) { if (name != null && name.references(var)) - { - return true; - } + return true; if (namespace != null && namespace.references(var)) - { - return true; - } + return true; return super.references(var); } @@ -283,6 +241,11 @@ final class ElementNode StringBuffer buf = new StringBuffer(getClass().getName()); buf.append('['); buf.append("name="); + if (namespace != null) + { + buf.append(",namespace="); + buf.append(namespace); + } buf.append(name); if (uas != null) { diff --git a/libjava/classpath/gnu/xml/transform/FunctionAvailableFunction.java b/libjava/classpath/gnu/xml/transform/FunctionAvailableFunction.java index 7daf7ea3f70..ab86401d0e4 100644 --- a/libjava/classpath/gnu/xml/transform/FunctionAvailableFunction.java +++ b/libjava/classpath/gnu/xml/transform/FunctionAvailableFunction.java @@ -147,11 +147,16 @@ class FunctionAvailableFunction uri = nsctx.getNamespaceURI(prefix); if (uri == null) { - return xsltFunctions.contains(localName) || - xpathFunctions.contains(localName) ? + return (xpathFunctions.contains(localName) || + xsltFunctions.contains(localName)) ? Boolean.TRUE : Boolean.FALSE; - // TODO extension functions } + else if (Stylesheet.XSL_NS.equals(uri)) + { + return xsltFunctions.contains(localName) ? + Boolean.TRUE : Boolean.FALSE; + } + // TODO extension functions return Boolean.FALSE; } @@ -159,16 +164,12 @@ class FunctionAvailableFunction { NamespaceContext n = nsctx; if (context instanceof NamespaceContext) - { - n = (NamespaceContext) context; - } + n = (NamespaceContext) context; FunctionAvailableFunction f = new FunctionAvailableFunction(n); int len = args.size(); List args2 = new ArrayList(len); for (int i = 0; i < len; i++) - { - args2.add(((Expr) args.get(i)).clone(context)); - } + args2.add(((Expr) args.get(i)).clone(context)); f.setArguments(args2); return f; } @@ -178,12 +179,15 @@ class FunctionAvailableFunction for (Iterator i = args.iterator(); i.hasNext(); ) { if (((Expr) i.next()).references(var)) - { - return true; - } + return true; } return false; } + public String toString() + { + return "function-available(" + args.get(0) + ")"; + } + } diff --git a/libjava/classpath/gnu/xml/transform/LiteralNode.java b/libjava/classpath/gnu/xml/transform/LiteralNode.java index 0be2b35c84d..453c22c5e0d 100644 --- a/libjava/classpath/gnu/xml/transform/LiteralNode.java +++ b/libjava/classpath/gnu/xml/transform/LiteralNode.java @@ -1,5 +1,5 @@ /* LiteralNode.java -- - Copyright (C) 2004 Free Software Foundation, Inc. + Copyright (C) 2004,2006 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -77,38 +77,28 @@ final class LiteralNode elementExcludeResultPrefixes = new HashSet(); StringTokenizer st = new StringTokenizer(attr.getNodeValue()); while (st.hasMoreTokens()) - { - elementExcludeResultPrefixes.add(st.nextToken()); - } + elementExcludeResultPrefixes.add(st.nextToken()); } else - { - elementExcludeResultPrefixes = Collections.EMPTY_SET; - } + elementExcludeResultPrefixes = Collections.EMPTY_SET; } else - { - elementExcludeResultPrefixes = null; - } + elementExcludeResultPrefixes = null; } TemplateNode clone(Stylesheet stylesheet) { TemplateNode ret = new LiteralNode(source); if (children != null) - { - ret.children = children.clone(stylesheet); - } + ret.children = children.clone(stylesheet); if (next != null) - { - ret.next = next.clone(stylesheet); - } + ret.next = next.clone(stylesheet); return ret; } void doApply(Stylesheet stylesheet, QName mode, - Node context, int pos, int len, - Node parent, Node nextSibling) + Node context, int pos, int len, + Node parent, Node nextSibling) throws TransformerException { Node result = null; @@ -127,17 +117,13 @@ final class LiteralNode { String prefix = source.getPrefix(); if (prefix == null) - { - prefix = "#default"; - } + prefix = "#default"; String resultPrefix = (String) stylesheet.namespaceAliases.get(prefix); if (resultPrefix != null) { if ("#default".equals(resultPrefix)) - { - resultPrefix = null; - } + resultPrefix = null; String uri = source.lookupNamespaceURI(resultPrefix); String name = source.getNodeName(); // Create a new element node in the result document @@ -189,33 +175,23 @@ final class LiteralNode result = result2; } if (nextSibling != null) - { - parent.insertBefore(result, nextSibling); - } + parent.insertBefore(result, nextSibling); else - { - parent.appendChild(result); - } + parent.appendChild(result); if (nodeType == Node.ELEMENT_NODE) - { - stylesheet.addNamespaceNodes(source, result, doc, - elementExcludeResultPrefixes); - } + stylesheet.addNamespaceNodes(source, result, doc, + elementExcludeResultPrefixes); // children if (children != null) - { - children.apply(stylesheet, mode, - context, pos, len, - result, null); - } + children.apply(stylesheet, mode, + context, pos, len, + result, null); } // next sibling if (next != null) - { - next.apply(stylesheet, mode, - context, pos, len, - parent, nextSibling); - } + next.apply(stylesheet, mode, + context, pos, len, + parent, nextSibling); } public String toString() diff --git a/libjava/classpath/gnu/xml/transform/MessageNode.java b/libjava/classpath/gnu/xml/transform/MessageNode.java index 1df716836e7..e8e07c6da29 100644 --- a/libjava/classpath/gnu/xml/transform/MessageNode.java +++ b/libjava/classpath/gnu/xml/transform/MessageNode.java @@ -1,5 +1,5 @@ /* MessageNode.java -- - Copyright (C) 2004 Free Software Foundation, Inc. + Copyright (C) 2004,2006 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -37,6 +37,7 @@ exception statement from your version. */ package gnu.xml.transform; +import java.util.logging.Logger; import javax.xml.namespace.QName; import javax.xml.transform.TransformerException; import org.w3c.dom.Document; @@ -53,6 +54,8 @@ final class MessageNode extends TemplateNode { + static final Logger logger = Logger.getLogger("gnu.xml.transform"); + final boolean terminate; MessageNode(boolean terminate) @@ -64,13 +67,9 @@ final class MessageNode { TemplateNode ret = new MessageNode(terminate); if (children != null) - { - ret.children = children.clone(stylesheet); - } + ret.children = children.clone(stylesheet); if (next != null) - { - ret.next = next.clone(stylesheet); - } + ret.next = next.clone(stylesheet); return ret; } @@ -86,16 +85,12 @@ final class MessageNode DocumentFragment fragment = doc.createDocumentFragment(); children.apply(stylesheet, mode, context, pos, len, fragment, null); String message = Expr.stringValue(fragment); - System.err.println(message); + logger.info(message); if (terminate) - { - stylesheet.terminated = true; - } + stylesheet.terminated = true; } if (next != null && !terminate) - { - next.apply(stylesheet, mode, context, pos, len, parent, nextSibling); - } + next.apply(stylesheet, mode, context, pos, len, parent, nextSibling); } } diff --git a/libjava/classpath/gnu/xml/transform/NamespaceProxy.java b/libjava/classpath/gnu/xml/transform/NamespaceProxy.java new file mode 100644 index 00000000000..b3c233cd7a5 --- /dev/null +++ b/libjava/classpath/gnu/xml/transform/NamespaceProxy.java @@ -0,0 +1,77 @@ +/* NamespaceProxy.java -- + Copyright (C) 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.xml.transform; + +import java.util.Collections; +import java.util.Iterator; +import javax.xml.namespace.NamespaceContext; +import org.w3c.dom.Node; + +/** + * A namespace context using a DOM node to resolve the namespace. + * + * @author Chris Burdess + */ +class NamespaceProxy + implements NamespaceContext +{ + + private final Node node; + + NamespaceProxy(Node node) + { + this.node = node; + } + + public String getNamespaceURI(String prefix) + { + return (node == null) ? null : node.lookupNamespaceURI(prefix); + } + + public String getPrefix(String namespaceURI) + { + return (node == null) ? null : node.lookupPrefix(namespaceURI); + } + + public Iterator getPrefixes(String namespaceURI) + { + // TODO + return Collections.singleton(getPrefix(namespaceURI)).iterator(); + } + +} diff --git a/libjava/classpath/gnu/xml/transform/StreamSerializer.java b/libjava/classpath/gnu/xml/transform/StreamSerializer.java index 74b10057c12..fb8b1a601bc 100644 --- a/libjava/classpath/gnu/xml/transform/StreamSerializer.java +++ b/libjava/classpath/gnu/xml/transform/StreamSerializer.java @@ -1,5 +1,5 @@ /* StreamSerializer.java -- - Copyright (C) 2004 Free Software Foundation, Inc. + Copyright (C) 2004,2006 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -49,6 +49,8 @@ import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedList; import java.util.Map; import javax.xml.XMLConstants; import org.w3c.dom.Attr; @@ -149,16 +151,25 @@ public class StreamSerializer HTML_BOOLEAN_ATTRIBUTES.put("script", set); } + // HTML namespace URIs + static final HashSet HTML_URIS = new HashSet(); + static { + HTML_URIS.add("http://www.w3.org/1999/xhtml"); + } + protected final String encoding; final Charset charset; final CharsetEncoder encoder; final int mode; - final Map namespaces; + final LinkedList namespaces; protected String eol; Collection cdataSectionElements = Collections.EMPTY_SET; protected boolean discardDefaultContent; protected boolean xmlDeclaration = true; + + // has a META element with the encoding been added? + private boolean htmlEncoded; public StreamSerializer() { @@ -174,14 +185,12 @@ public class StreamSerializer { this.mode = mode; if (encoding == null) - { - encoding = "UTF-8"; - } + encoding = (mode == Stylesheet.OUTPUT_HTML) ? "ISO-8859-1" : "UTF-8"; this.encoding = encoding.intern(); charset = Charset.forName(this.encoding); encoder = charset.newEncoder(); this.eol = (eol != null) ? eol : System.getProperty("line.separator"); - namespaces = new HashMap(); + namespaces = new LinkedList(); } void setCdataSectionElements(Collection c) @@ -212,18 +221,14 @@ public class StreamSerializer throws IOException { if (out == null) - { - throw new NullPointerException("no output stream"); - } + throw new NullPointerException("no output stream"); + htmlEncoded = false; String value, prefix; Node children; String uri = node.getNamespaceURI(); - boolean defined = false; short nt = node.getNodeType(); if (convertToCdata && nt == Node.TEXT_NODE) - { - nt = Node.CDATA_SECTION_NODE; - } + nt = Node.CDATA_SECTION_NODE; switch (nt) { case Node.ATTRIBUTE_NODE: @@ -233,27 +238,28 @@ public class StreamSerializer (prefix != null && prefix.startsWith("xmlns:"))) { String nsuri = node.getNodeValue(); - if (isDefined(nsuri)) - { - break; - } + if (isDefined(nsuri, prefix)) + break; String name = node.getLocalName(); if (name == null) { + // Namespace-unaware name = node.getNodeName(); + int ci = name.indexOf(':'); + if (ci != -1) + name = name.substring(ci + 1); } define(nsuri, name); } - else if (uri != null && !isDefined(uri)) + else if (uri != null && !isDefined(uri, prefix)) { prefix = define(uri, prefix); String nsname = (prefix == null) ? "xmlns" : "xmlns:" + prefix; out.write(SPACE); out.write(encodeText(nsname)); out.write(EQ); - String nsvalue = "'" + encode(uri, true, true) + "'"; + String nsvalue = "\"" + encode(uri, true, true) + "\""; out.write(nsvalue.getBytes(encoding)); - defined = true; } out.write(SPACE); String a_nodeName = node.getNodeName(); @@ -262,27 +268,26 @@ public class StreamSerializer if (mode == Stylesheet.OUTPUT_HTML && a_nodeName.equals(a_nodeValue) && isHTMLBoolean((Attr) node, a_nodeName)) - { - break; - } + break; out.write(EQ); - value = "'" + encode(a_nodeValue, true, true) + "'"; + value = "\"" + encode(a_nodeValue, true, true) + "\""; out.write(encodeText(value)); break; case Node.ELEMENT_NODE: + pushNamespaceContext(); value = node.getNodeName(); out.write(BRA); out.write(encodeText(value)); - if (uri != null && !isDefined(uri)) + prefix = node.getPrefix(); + if (uri != null && !isDefined(uri, prefix)) { - prefix = define(uri, node.getPrefix()); + prefix = define(uri, prefix); String nsname = (prefix == null) ? "xmlns" : "xmlns:" + prefix; out.write(SPACE); out.write(encodeText(nsname)); out.write(EQ); - String nsvalue = "'" + encode(uri, true, true) + "'"; + String nsvalue = "\"" + encode(uri, true, true) + "\""; out.write(encodeText(nsvalue)); - defined = true; } NamedNodeMap attrs = node.getAttributes(); if (attrs != null) @@ -296,9 +301,7 @@ public class StreamSerializer // NOOP } else - { - serialize(attr, out, false); - } + serialize(attr, out, false); } } convertToCdata = cdataSectionElements.contains(value); @@ -317,27 +320,35 @@ public class StreamSerializer out.write(encodeText(value)); out.write(KET); } + popNamespaceContext(); break; case Node.TEXT_NODE: value = node.getNodeValue(); if (!"yes".equals(node.getUserData("disable-output-escaping"))) - { - value = encode(value, false, false); - } + value = encode(value, false, false); out.write(encodeText(value)); break; case Node.CDATA_SECTION_NODE: - value = ""; - out.write(encodeText(value)); + value = node.getNodeValue(); + // Where any instanceof of ]]> occur, split into multiple CDATA + // sections + int bbk = value.indexOf("]]>"); + while (bbk != -1) + { + String head = value.substring(0, bbk + 2); + out.write(encodeText("")); + value = value.substring(bbk + 2); + bbk = value.indexOf("]]>"); + } + // Write final tail value + out.write(encodeText("")); break; case Node.COMMENT_NODE: value = ""; out.write(encodeText(value)); Node cp = node.getParentNode(); if (cp != null && cp.getNodeType() == Node.DOCUMENT_NODE) - { - out.write(encodeText(eol)); - } + out.write(encodeText(eol)); break; case Node.DOCUMENT_NODE: case Node.DOCUMENT_FRAGMENT_NODE: @@ -355,29 +366,23 @@ public class StreamSerializer (Document) node : null; String version = (doc != null) ? doc.getXmlVersion() : null; if (version == null) - { - version = (String) node.getUserData("version"); - } + version = (String) node.getUserData("version"); if (version == null) - { - version = "1.0"; - } + version = "1.0"; out.write(BRA); out.write(0x3f); - out.write("xml version='".getBytes("US-ASCII")); + out.write("xml version=\"".getBytes("US-ASCII")); out.write(version.getBytes("US-ASCII")); - out.write(APOS); + out.write(0x22); if (!("UTF-8".equalsIgnoreCase(encoding))) { - out.write(" encoding='".getBytes("US-ASCII")); + out.write(" encoding=\"".getBytes("US-ASCII")); out.write(encoding.getBytes("US-ASCII")); - out.write(APOS); + out.write(0x22); } if ((doc != null && doc.getXmlStandalone()) || "yes".equals(node.getUserData("standalone"))) - { - out.write(" standalone='yes'".getBytes("US-ASCII")); - } + out.write(" standalone=\"yes\"".getBytes("US-ASCII")); out.write(0x3f); out.write(KET); out.write(encodeText(eol)); @@ -387,12 +392,10 @@ public class StreamSerializer } else if (mode == Stylesheet.OUTPUT_HTML) { - // Ensure that encoding is accessible + // Ensure that encoding is accessible if head element is present String mediaType = (String) node.getUserData("media-type"); if (mediaType == null) - { - mediaType = "text/html"; - } + mediaType = "text/html"; String contentType = mediaType + "; charset=" + ((encoding.indexOf(' ') != -1) ? "\"" + encoding + "\"" : @@ -403,133 +406,87 @@ public class StreamSerializer for (Node ctx = node.getFirstChild(); ctx != null; ctx = ctx.getNextSibling()) { - if (ctx.getNodeType() == Node.ELEMENT_NODE) + if (ctx.getNodeType() == Node.ELEMENT_NODE && + isHTMLElement(ctx, "html")) { html = ctx; break; } } - if (html == null) - { - html = doc.createElement("html"); - node.appendChild(html); - } - Node head = null; - for (Node ctx = html.getFirstChild(); ctx != null; - ctx = ctx.getNextSibling()) + if (html != null) { - if (ctx.getNodeType() == Node.ELEMENT_NODE) - { - String name = ctx.getLocalName(); - if (name == null) - { - name = ctx.getNodeName(); - } - if ("head".equalsIgnoreCase(name)) - { - head = ctx; - break; - } - } - } - if (head == null) - { - head = doc.createElement("head"); - Node c1 = null; + Node head = null; for (Node ctx = html.getFirstChild(); ctx != null; ctx = ctx.getNextSibling()) { - if (ctx.getNodeType() == Node.ELEMENT_NODE) + if (isHTMLElement(ctx, "head")) { - c1 = ctx; + head = ctx; break; } } - if (c1 != null) - { - html.insertBefore(head, c1); - } - else - { - html.appendChild(head); - } - } - Node meta = null; - Node metaContent = null; - for (Node ctx = head.getFirstChild(); ctx != null; - ctx = ctx.getNextSibling()) - { - if (ctx.getNodeType() == Node.ELEMENT_NODE) + if (head != null) { - String name = ctx.getLocalName(); - if (name == null) - { - name = ctx.getNodeName(); - } - if ("meta".equalsIgnoreCase(name)) + Node meta = null; + Node metaContent = null; + for (Node ctx = head.getFirstChild(); ctx != null; + ctx = ctx.getNextSibling()) { - NamedNodeMap metaAttrs = ctx.getAttributes(); - int len = metaAttrs.getLength(); - String httpEquiv = null; - Node content = null; - for (int i = 0; i < len; i++) + if (isHTMLElement(ctx, "meta")) { - Node attr = metaAttrs.item(i); - String attrName = attr.getNodeName(); - if ("http-equiv".equalsIgnoreCase(attrName)) + NamedNodeMap metaAttrs = ctx.getAttributes(); + int len = metaAttrs.getLength(); + String httpEquiv = null; + Node content = null; + for (int i = 0; i < len; i++) { - httpEquiv = attr.getNodeValue(); + Node attr = metaAttrs.item(i); + String attrName = attr.getNodeName(); + if ("http-equiv".equalsIgnoreCase(attrName)) + httpEquiv = attr.getNodeValue(); + else if ("content".equalsIgnoreCase(attrName)) + content = attr; } - else if ("content".equalsIgnoreCase(attrName)) + if ("Content-Type".equalsIgnoreCase(httpEquiv)) { - content = attr; + meta = ctx; + metaContent = content; + break; } } - if ("Content-Type".equalsIgnoreCase(httpEquiv)) - { - meta = ctx; - metaContent = content; - break; - } } + if (meta == null) + { + meta = doc.createElement("meta"); + // Insert first + Node first = head.getFirstChild(); + if (first == null) + head.appendChild(meta); + else + head.insertBefore(meta, first); + Node metaHttpEquiv = doc.createAttribute("http-equiv"); + meta.getAttributes().setNamedItem(metaHttpEquiv); + metaHttpEquiv.setNodeValue("Content-Type"); + } + if (metaContent == null) + { + metaContent = doc.createAttribute("content"); + meta.getAttributes().setNamedItem(metaContent); + } + metaContent.setNodeValue(contentType); + htmlEncoded = true; } } - if (meta == null) - { - meta = doc.createElement("meta"); - // Insert first - Node first = head.getFirstChild(); - if (first == null) - { - head.appendChild(meta); - } - else - { - head.insertBefore(meta, first); - } - Node metaHttpEquiv = doc.createAttribute("http-equiv"); - meta.getAttributes().setNamedItem(metaHttpEquiv); - metaHttpEquiv.setNodeValue("Content-Type"); - } - if (metaContent == null) - { - metaContent = doc.createAttribute("content"); - meta.getAttributes().setNamedItem(metaContent); - } - metaContent.setNodeValue(contentType); - // phew } children = node.getFirstChild(); if (children != null) - { - serialize(children, out, convertToCdata); - } + serialize(children, out, convertToCdata); break; case Node.DOCUMENT_TYPE_NODE: DocumentType doctype = (DocumentType) node; out.write(BRA); out.write(BANG); - out.write(encodeText("DOCTYPE ")); + out.write(encodeText("DOCTYPE ")); value = doctype.getNodeName(); out.write(encodeText(value)); String publicId = doctype.getPublicId(); @@ -569,41 +526,73 @@ public class StreamSerializer out.write(encodeText(eol)); } break; + default: + System.err.println("Unhandled node type: "+nt); } - if (defined) + } + + boolean isHTMLElement(Node node, String name) + { + if (node.getNodeType() != Node.ELEMENT_NODE) + return false; + String localName = node.getLocalName(); + if (localName == null) + localName = node.getNodeName(); + if (!name.equalsIgnoreCase(localName)) + return false; + String uri = node.getNamespaceURI(); + return (uri == null || HTML_URIS.contains(uri)); + } + + boolean isDefined(String uri, String prefix) + { + if (XMLConstants.XML_NS_URI.equals(uri)) + return "xml".equals(prefix); + if (XMLConstants.XMLNS_ATTRIBUTE_NS_URI.equals(uri)) + return "xmlns".equals(prefix); + if (prefix == null) + prefix = ""; + for (Iterator i = namespaces.iterator(); i.hasNext(); ) { - undefine(uri); + Map ctx = (Map) i.next(); + String val = (String) ctx.get(uri); + if (val != null && val.equals(prefix)) + return true; } + return false; } - boolean isDefined(String uri) + void pushNamespaceContext() { - return XMLConstants.XML_NS_URI.equals(uri) || - XMLConstants.XMLNS_ATTRIBUTE_NS_URI.equals(uri) || - namespaces.containsKey(uri); + namespaces.addFirst(new HashMap()); } String define(String uri, String prefix) { - while (namespaces.containsValue(prefix)) + if (namespaces.isEmpty()) + return prefix; + HashMap ctx = (HashMap) namespaces.getFirst(); + while (ctx.containsValue(prefix)) { // Fabricate new prefix prefix = prefix + "_"; } - namespaces.put(uri, prefix); + ctx.put(uri, prefix); return prefix; } - void undefine(String uri) + void popNamespaceContext() { - namespaces.remove(uri); + namespaces.removeFirst(); } final byte[] encodeText(String text) throws IOException { encoder.reset(); - if (!encoder.canEncode(text)) + boolean htmlNeedingEncoding = + (mode == Stylesheet.OUTPUT_HTML && !htmlEncoded); + if (!encoder.canEncode(text) || htmlNeedingEncoding) { // Check each character StringBuffer buf = new StringBuffer(); @@ -611,11 +600,7 @@ public class StreamSerializer for (int i = 0; i < len; i++) { char c = text.charAt(i); - if (encoder.canEncode(c)) - { - buf.append(c); - } - else + if (!encoder.canEncode(c)) { // Replace with character entity reference String hex = Integer.toHexString((int) c); @@ -623,6 +608,20 @@ public class StreamSerializer buf.append(hex); buf.append(';'); } + else if (htmlNeedingEncoding) + { + String entityName = getHTMLCharacterEntity(c); + if (entityName != null) + { + buf.append('&'); + buf.append(entityName); + buf.append(';'); + } + else + buf.append(c); + } + else + buf.append(c); } text = buf.toString(); } @@ -656,17 +655,13 @@ public class StreamSerializer if (c == '<') { if (buf == null) - { - buf = new StringBuffer(text.substring(0, i)); - } + buf = new StringBuffer(text.substring(0, i)); buf.append("<"); } else if (c == '>') { if (buf == null) - { - buf = new StringBuffer(text.substring(0, i)); - } + buf = new StringBuffer(text.substring(0, i)); buf.append(">"); } else if (c == '&') @@ -675,25 +670,19 @@ public class StreamSerializer text.charAt(i + 1) == '{') { if (buf != null) - { - buf.append(c); - } + buf.append(c); } else { if (buf == null) - { - buf = new StringBuffer(text.substring(0, i)); - } + buf = new StringBuffer(text.substring(0, i)); buf.append("&"); } } else if (c == '\'' && inAttr) { if (buf == null) - { - buf = new StringBuffer(text.substring(0, i)); - } + buf = new StringBuffer(text.substring(0, i)); if (mode == Stylesheet.OUTPUT_HTML) // HTML does not define ', use character entity ref buf.append("'"); @@ -703,9 +692,7 @@ public class StreamSerializer else if (c == '"' && inAttr) { if (buf == null) - { - buf = new StringBuffer(text.substring(0, i)); - } + buf = new StringBuffer(text.substring(0, i)); buf.append("""); } else if (encodeCtl) @@ -713,23 +700,17 @@ public class StreamSerializer if (c < 0x20) { if (buf == null) - { - buf = new StringBuffer(text.substring(0, i)); - } + buf = new StringBuffer(text.substring(0, i)); buf.append('&'); buf.append('#'); buf.append((int) c); buf.append(';'); } else if (buf != null) - { - buf.append(c); - } + buf.append(c); } else if (buf != null) - { - buf.append(c); - } + buf.append(c); } return (buf == null) ? text : buf.toString(); } @@ -763,4 +744,109 @@ public class StreamSerializer return (attributes != null && attributes.contains(attrName)); } + static String getHTMLCharacterEntity(char c) + { + // Hardcode these here to avoid loading the HTML DTD + switch (c) + { + case 160: return "nbsp"; + case 161: return "iexcl"; + case 162: return "cent"; + case 163: return "pound"; + case 164: return "curren"; + case 165: return "yen"; + case 166: return "brvbar"; + case 167: return "sect"; + case 168: return "uml"; + case 169: return "copy"; + case 170: return "ordf"; + case 171: return "laquo"; + case 172: return "not"; + case 173: return "shy"; + case 174: return "reg"; + case 175: return "macr"; + case 176: return "deg"; + case 177: return "plusmn"; + case 178: return "sup2"; + case 179: return "sup3"; + case 180: return "acute"; + case 181: return "micro"; + case 182: return "para"; + case 183: return "middot"; + case 184: return "cedil"; + case 185: return "sup1"; + case 186: return "ordm"; + case 187: return "raquo"; + case 188: return "frac14"; + case 189: return "frac12"; + case 190: return "frac34"; + case 191: return "iquest"; + case 192: return "Agrave"; + case 193: return "Aacute"; + case 194: return "Acirc"; + case 195: return "Atilde"; + case 196: return "Auml"; + case 197: return "Aring"; + case 198: return "AElig"; + case 199: return "Ccedil"; + case 200: return "Egrave"; + case 201: return "Eacute"; + case 202: return "Ecirc"; + case 203: return "Euml"; + case 204: return "Igrave"; + case 205: return "Iacute"; + case 206: return "Icirc"; + case 207: return "Iuml"; + case 208: return "ETH"; + case 209: return "Ntilde"; + case 210: return "Ograve"; + case 211: return "Oacute"; + case 212: return "Ocirc"; + case 213: return "Otilde"; + case 214: return "Ouml"; + case 215: return "times"; + case 216: return "Oslash"; + case 217: return "Ugrave"; + case 218: return "Uacute"; + case 219: return "Ucirc"; + case 220: return "Uuml"; + case 221: return "Yacute"; + case 222: return "THORN"; + case 223: return "szlig"; + case 224: return "agrave"; + case 225: return "aacute"; + case 226: return "acirc"; + case 227: return "atilde"; + case 228: return "auml"; + case 229: return "aring"; + case 230: return "aelig"; + case 231: return "ccedil"; + case 232: return "egrave"; + case 233: return "eacute"; + case 234: return "ecirc"; + case 235: return "euml"; + case 236: return "igrave"; + case 237: return "iacute"; + case 238: return "icirc"; + case 239: return "iuml"; + case 240: return "eth"; + case 241: return "ntilde"; + case 242: return "ograve"; + case 243: return "oacute"; + case 244: return "ocirc"; + case 245: return "otilde"; + case 246: return "ouml"; + case 247: return "divide"; + case 248: return "oslash"; + case 249: return "ugrave"; + case 250: return "uacute"; + case 251: return "ucirc"; + case 252: return "uuml"; + case 253: return "yacute"; + case 254: return "thorn"; + case 255: return "yuml"; + default: return null; + } + } + } diff --git a/libjava/classpath/gnu/xml/transform/StrippingInstruction.java b/libjava/classpath/gnu/xml/transform/StrippingInstruction.java new file mode 100644 index 00000000000..c6391b9c521 --- /dev/null +++ b/libjava/classpath/gnu/xml/transform/StrippingInstruction.java @@ -0,0 +1,73 @@ +/* StrippingInstruction.java -- + Copyright (C) 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.xml.transform; + +import gnu.xml.xpath.NameTest; + +/** + * An entry in a strip-space or preserve-space list. + * + * @author Chris Burdess + */ +class StrippingInstruction +{ + + final NameTest element; + final int precedence; + + StrippingInstruction(NameTest element, int precedence) + { + this.element = element; + this.precedence = precedence; + } + + /** + * Returns the default priority of the element name test. + * @see http://www.w3.org/TR/xslt#dt-default-priority + */ + float getPriority() + { + if (element.matchesAny()) + return -0.5f; + else if (element.matchesAnyLocalName()) + return -0.25f; + else + return 0.0f; + } + +} diff --git a/libjava/classpath/gnu/xml/transform/Stylesheet.java b/libjava/classpath/gnu/xml/transform/Stylesheet.java index 99431b699d8..51accaa3b9a 100644 --- a/libjava/classpath/gnu/xml/transform/Stylesheet.java +++ b/libjava/classpath/gnu/xml/transform/Stylesheet.java @@ -1,5 +1,5 @@ /* Stylesheet.java -- - Copyright (C) 2004 Free Software Foundation, Inc. + Copyright (C) 2004,2006 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -88,6 +88,8 @@ class Stylesheet { static final String XSL_NS = "http://www.w3.org/1999/XSL/Transform"; + private static final NameTest STYLESHEET_PRESERVE_TEXT = + new NameTest(new QName(XSL_NS, "text"), false, false); static final int OUTPUT_XML = 0; static final int OUTPUT_HTML = 1; @@ -208,6 +210,7 @@ class Stylesheet preserveSpace = new LinkedHashSet(); outputCdataSectionElements = new LinkedHashSet(); xpath = (XPathImpl) factory.xpathFactory.newXPath(); + xpath.setNamespaceContext(this); if (parent == null) { bindings = new Bindings(this); @@ -218,7 +221,6 @@ class Stylesheet keys = new LinkedList(); decimalFormats = new LinkedHashMap(); initDefaultDecimalFormat(); - xpath.setNamespaceContext(this); xpath.setXPathFunctionResolver(this); } else @@ -241,7 +243,6 @@ class Stylesheet templates = root.templates; keys = root.keys; decimalFormats = root.decimalFormats; - xpath.setNamespaceContext(root); xpath.setXPathFunctionResolver(root); } xpath.setXPathVariableResolver(bindings); @@ -276,9 +277,7 @@ class Stylesheet { Stylesheet stylesheet = this; while (stylesheet.parent != null) - { - stylesheet = stylesheet.parent; - } + stylesheet = stylesheet.parent; return stylesheet; } @@ -408,9 +407,7 @@ class Stylesheet throws TransformerException { if (debug) - { - System.err.println("getTemplate: mode="+mode+" context="+context); - } + System.err.println("getTemplate: mode="+mode+" context="+context); Set candidates = new TreeSet(); for (Iterator j = templates.iterator(); j.hasNext(); ) { @@ -425,15 +422,11 @@ class Stylesheet throw new TransformerException(msg); } if (!currentTemplate.imports(t)) - { - isMatch = false; - } + isMatch = false; } //System.err.println("\t"+context+" "+t+"="+isMatch); if (isMatch) - { - candidates.add(t); - } + candidates.add(t); } //System.err.println("\tcandidates="+candidates); if (candidates.isEmpty()) @@ -441,9 +434,7 @@ class Stylesheet // Apply built-in template // Current template is unchanged if (debug) - { - System.err.println("\tbuiltInTemplate context="+context); - } + System.err.println("\tbuiltInTemplate context="+context); switch (context.getNodeType()) { case Node.ELEMENT_NODE: @@ -453,6 +444,7 @@ class Stylesheet case Node.COMMENT_NODE: return builtInNodeTemplate; case Node.TEXT_NODE: + case Node.CDATA_SECTION_NODE: case Node.ATTRIBUTE_NODE: return builtInTextTemplate; default: @@ -465,9 +457,7 @@ class Stylesheet // Set current template currentTemplate = t; if (debug) - { - System.err.println("\ttemplate="+t+" context="+context); - } + System.err.println("\ttemplate="+t+" context="+context); return t.node; } } @@ -475,25 +465,17 @@ class Stylesheet TemplateNode getTemplate(QName mode, QName name) throws TransformerException { - //System.err.println("getTemplate: mode="+mode+" name="+name); Set candidates = new TreeSet(); for (Iterator j = templates.iterator(); j.hasNext(); ) { Template t = (Template) j.next(); boolean isMatch = t.matches(name); - //System.err.println("\t"+name+" "+t+"="+isMatch); if (isMatch) - { - candidates.add(t); - } + candidates.add(t); } if (candidates.isEmpty()) - { - return null; - //throw new TransformerException("template '" + name + "' not found"); - } + return null; Template t = (Template) candidates.iterator().next(); - //System.err.println("\ttemplate="+t+" context="+context); return t.node; } @@ -538,42 +520,30 @@ class Stylesheet output = node; String method = getAttribute(attrs, "method"); if ("xml".equals(method) || method == null) - { - outputMethod = OUTPUT_XML; - } + outputMethod = OUTPUT_XML; else if ("html".equals(method)) - { - outputMethod = OUTPUT_HTML; - } + outputMethod = OUTPUT_HTML; else if ("text".equals(method)) - { - outputMethod = OUTPUT_TEXT; - } + outputMethod = OUTPUT_TEXT; else { String msg = "unsupported output method: " + method; DOMSourceLocator l = new DOMSourceLocator(node); throw new TransformerConfigurationException(msg, l); } - outputPublicId = getAttribute(attrs, "public-id"); - outputSystemId = getAttribute(attrs, "system-id"); + outputPublicId = getAttribute(attrs, "doctype-public"); + outputSystemId = getAttribute(attrs, "doctype-system"); outputEncoding = getAttribute(attrs, "encoding"); String indent = getAttribute(attrs, "indent"); if (indent != null) - { - outputIndent = "yes".equals(indent); - } + outputIndent = "yes".equals(indent); outputVersion = getAttribute(attrs, "version"); String omitXmlDecl = getAttribute(attrs, "omit-xml-declaration"); if (omitXmlDecl != null) - { - outputOmitXmlDeclaration = "yes".equals(omitXmlDecl); - } + outputOmitXmlDeclaration = "yes".equals(omitXmlDecl); String standalone = getAttribute(attrs, "standalone"); if (standalone != null) - { - outputStandalone = "yes".equals(standalone); - } + outputStandalone = "yes".equals(standalone); outputMediaType = getAttribute(attrs, "media-type"); String cdataSectionElements = getAttribute(attrs, "cdata-section-elements"); @@ -581,9 +551,7 @@ class Stylesheet { StringTokenizer st = new StringTokenizer(cdataSectionElements, " "); while (st.hasMoreTokens()) - { - outputCdataSectionElements.add(st.nextToken()); - } + outputCdataSectionElements.add(st.nextToken()); } } @@ -728,9 +696,7 @@ class Stylesheet parse(node.getFirstChild(), false); } else if ("template".equals(name)) - { - templates.add(parseTemplate(node, attrs)); - } + templates.add(parseTemplate(node, attrs)); else if ("param".equals(name) || "variable".equals(name)) { @@ -779,9 +745,7 @@ class Stylesheet factory.newStylesheet(source, precedence + delta, this); } else if ("output".equals(name)) - { - parseOutput(node, attrs); - } + parseOutput(node, attrs); else if ("preserve-space".equals(name)) { String elements = @@ -790,7 +754,9 @@ class Stylesheet " \t\n\r"); while (st.hasMoreTokens()) { - preserveSpace.add(parseNameTest(st.nextToken())); + NameTest element = parseNameTest(st.nextToken()); + preserveSpace.add(new StrippingInstruction(element, + precedence)); } } else if ("strip-space".equals(name)) @@ -801,25 +767,19 @@ class Stylesheet " \t\n\r"); while (st.hasMoreTokens()) { - stripSpace.add(parseNameTest(st.nextToken())); + NameTest element = parseNameTest(st.nextToken()); + stripSpace.add(new StrippingInstruction(element, + precedence)); } } else if ("key".equals(name)) - { - parseKey(node, attrs); - } + parseKey(node, attrs); else if ("decimal-format".equals(name)) - { - parseDecimalFormat(node, attrs); - } + parseDecimalFormat(node, attrs); else if ("namespace-alias".equals(name)) - { - parseNamespaceAlias(node, attrs); - } + parseNamespaceAlias(node, attrs); else if ("attribute-set".equals(name)) - { - parseAttributeSet(node, attrs); - } + parseAttributeSet(node, attrs); } else if (root) { @@ -867,12 +827,10 @@ class Stylesheet final NameTest parseNameTest(String token) { if ("*".equals(token)) - { - return new NameTest(null, true, true); - } + return new NameTest(null, true, true); else if (token.endsWith(":*")) { - QName qName = getQName(token.substring(0, token.length() - 2)); + QName qName = getQName(token); return new NameTest(qName, true, false); } else @@ -984,7 +942,13 @@ class Stylesheet return ret; } - boolean isPreserved(Text text) + /** + * Whitespace stripping. + * @param text the text node + * @param source true if a source node, false if a stylesheet text node + * @see http://www.w3.org/TR/xslt#strip + */ + boolean isPreserved(Text text, boolean source) throws TransformerConfigurationException { // Check characters in text @@ -996,39 +960,73 @@ class Stylesheet { char c = value.charAt(i); if (c != 0x20 && c != 0x09 && c != 0x0a && c != 0x0d) - { - return true; - } + return true; } } // Check parent node Node ctx = text.getParentNode(); - if (!preserveSpace.isEmpty()) + if (source) { - for (Iterator i = preserveSpace.iterator(); i.hasNext(); ) + // Source document text node + boolean preserve = true; + float psPriority = 0.0f, ssPriority = 0.0f; + if (!stripSpace.isEmpty()) { - NameTest preserveTest = (NameTest) i.next(); - if (preserveTest.matches(ctx, 1, 1)) + // Conflict resolution + StrippingInstruction ssi = null, psi = null; + for (Iterator i = stripSpace.iterator(); i.hasNext(); ) + { + StrippingInstruction si = (StrippingInstruction) i.next(); + if (si.element.matches(ctx, 1, 1)) + { + if (ssi != null) + { + if (si.precedence < ssi.precedence) + continue; + float p = si.getPriority(); + if (p < ssPriority) + continue; + } + ssi = si; + } + } + for (Iterator i = preserveSpace.iterator(); i.hasNext(); ) { - boolean override = false; - if (!stripSpace.isEmpty()) + StrippingInstruction si = (StrippingInstruction) i.next(); + if (si.element.matches(ctx, 1, 1)) { - for (Iterator j = stripSpace.iterator(); j.hasNext(); ) + if (psi != null) { - NameTest stripTest = (NameTest) j.next(); - if (stripTest.matches(ctx, 1, 1)) - { - override = true; - break; - } + if (si.precedence < psi.precedence) + continue; + float p = si.getPriority(); + if (p < psPriority) + continue; } + psi = si; } - if (!override) + } + if (ssi != null) + { + if (psi != null) { - return true; + if (psi.precedence < ssi.precedence) + preserve = false; + else if (psPriority < ssPriority) + preserve = false; } + else + preserve = false; } } + if (preserve) + return true; + } + else + { + // Stylesheet text node + if (STYLESHEET_PRESERVE_TEXT.matches(ctx, 1, 1)) + return true; } // Check whether any ancestor specified xml:space while (ctx != null) @@ -1038,24 +1036,14 @@ class Stylesheet Element element = (Element) ctx; String xmlSpace = element.getAttribute("xml:space"); if ("default".equals(xmlSpace)) - { - break; - } + break; else if ("preserve".equals(xmlSpace)) - { - return true; - } + return true; else if (xmlSpace.length() > 0) { String msg = "Illegal value for xml:space: " + xmlSpace; throw new TransformerConfigurationException(msg); } - else if ("text".equals(ctx.getLocalName()) && - XSL_NS.equals(ctx.getNamespaceURI())) - { - // xsl:text implies xml:space='preserve' - return true; - } } ctx = ctx.getParentNode(); } @@ -1071,45 +1059,27 @@ class Stylesheet if ("document".equals(localName) && (arity == 1 || arity == 2)) { if (current == null) - { throw new RuntimeException("current is null"); - } return new DocumentFunction(getRootStylesheet(), current); } else if ("key".equals(localName) && (arity == 2)) - { - return new KeyFunction(getRootStylesheet()); - } + return new KeyFunction(getRootStylesheet()); else if ("format-number".equals(localName) && (arity == 2 || arity == 3)) - { - return new FormatNumberFunction(getRootStylesheet()); - } + return new FormatNumberFunction(getRootStylesheet()); else if ("current".equals(localName) && (arity == 0)) - { - return new CurrentFunction(getRootStylesheet()); - } + return new CurrentFunction(getRootStylesheet()); else if ("unparsed-entity-uri".equals(localName) && (arity == 1)) - { - return new UnparsedEntityUriFunction(); - } + return new UnparsedEntityUriFunction(); else if ("generate-id".equals(localName) && (arity == 1 || arity == 0)) - { - return new GenerateIdFunction(); - } + return new GenerateIdFunction(); else if ("system-property".equals(localName) && (arity == 1)) - { - return new SystemPropertyFunction(); - } + return new SystemPropertyFunction(); else if ("element-available".equals(localName) && (arity == 1)) - { - return new ElementAvailableFunction(this); - } + return new ElementAvailableFunction(new NamespaceProxy(current)); else if ("function-available".equals(localName) && (arity == 1)) - { - return new FunctionAvailableFunction(this); - } + return new FunctionAvailableFunction(new NamespaceProxy(current)); } return null; } @@ -1127,9 +1097,7 @@ class Stylesheet QName mode = (m == null) ? null : getQName(m); String s = getAttribute(attrs, "select"); if (s == null) - { - s = "child::node()"; - } + s = "child::node()"; Node children = node.getFirstChild(); List sortKeys = parseSortKeys(children); List withParams = parseWithParams(children); @@ -1411,13 +1379,9 @@ class Stylesheet if (tnode != null) { if (first == null) - { - first = tnode; - } + first = tnode; if (previous != null) - { - previous.next = tnode; - } + previous.next = tnode; previous = tnode; } node = next; @@ -1438,25 +1402,15 @@ class Stylesheet { String name = node.getLocalName(); if ("apply-templates".equals(name)) - { - return parseApplyTemplates(node); - } + return parseApplyTemplates(node); else if ("call-template".equals(name)) - { - return parseCallTemplate(node); - } + return parseCallTemplate(node); else if ("value-of".equals(name)) - { - return parseValueOf(node); - } + return parseValueOf(node); else if ("for-each".equals(name)) - { - return parseForEach(node); - } + return parseForEach(node); else if ("if".equals(name)) - { - return parseIf(node); - } + return parseIf(node); else if ("choose".equals(name)) { Node children = node.getFirstChild(); @@ -1465,9 +1419,7 @@ class Stylesheet return ret; } else if ("when".equals(name)) - { - return parseWhen(node); - } + return parseWhen(node); else if ("otherwise".equals(name)) { Node children = node.getFirstChild(); @@ -1476,25 +1428,15 @@ class Stylesheet return ret; } else if ("element".equals(name)) - { - return parseElement(node); - } + return parseElement(node); else if ("attribute".equals(name)) - { - return parseAttribute(node); - } + return parseAttribute(node); else if ("text".equals(name)) - { - return parseText(node); - } + return parseText(node); else if ("copy".equals(name)) - { - return parseCopy(node); - } + return parseCopy(node); else if ("processing-instruction".equals(name)) - { - return parseProcessingInstruction(node); - } + return parseProcessingInstruction(node); else if ("comment".equals(name)) { Node children = node.getFirstChild(); @@ -1503,9 +1445,7 @@ class Stylesheet return ret; } else if ("number".equals(name)) - { - return parseNumber(node); - } + return parseNumber(node); else if ("param".equals(name) || "variable".equals(name)) { @@ -1538,13 +1478,9 @@ class Stylesheet return ret; } else if ("copy-of".equals(name)) - { - return parseCopyOf(node); - } + return parseCopyOf(node); else if ("message".equals(name)) - { - return parseMessage(node); - } + return parseMessage(node); else if ("apply-imports".equals(name)) { Node children = node.getFirstChild(); @@ -1562,22 +1498,30 @@ class Stylesheet String prefix = node.getPrefix(); if (extensionElementPrefixes.contains(prefix)) { - // Pass over extension elements + // Check for xsl:fallback + for (Node ctx = node.getFirstChild(); ctx != null; + ctx = ctx.getNextSibling()) + { + String ctxUri = ctx.getNamespaceURI(); + if (XSL_NS.equals(ctxUri) && + "fallback".equals(ctx.getLocalName())) + { + ctx = ctx.getFirstChild(); + return (ctx == null) ? null : parse(ctx); + } + } + // Otherwise pass over extension element return null; } switch (node.getNodeType()) { case Node.TEXT_NODE: + case Node.CDATA_SECTION_NODE: // Determine whether to strip whitespace Text text = (Text) node; - if (!isPreserved(text)) + if (!isPreserved(text, false)) { // Strip - /*String data = text.getData().trim(); - if (data.length() > 0) - { - text.setData(data); - } // else */ text.getParentNode().removeChild(text); return null; } @@ -1623,9 +1567,7 @@ class Stylesheet String aname = attr.getNodeName(); if (Stylesheet.XSL_NS.equals(ans) && "use-attribute-sets".equals(attr.getLocalName())) - { - continue; - } + continue; String value = attr.getNodeValue(); TemplateNode grandchild = parseAttributeValueTemplate(value, node); @@ -1640,8 +1582,9 @@ class Stylesheet } String ename = node.getNodeName(); TemplateNode n = parseAttributeValueTemplate(ename, node); - TemplateNode ns = (namespaceUri == null) ? null : - parseAttributeValueTemplate(namespaceUri, node); + //TemplateNode ns = (namespaceUri == null) ? null : + // parseAttributeValueTemplate(namespaceUri, node); + TemplateNode ns = null; ElementNode ret = new ElementNode(n, ns, useAttributeSets, node); ret.children = child; @@ -1676,9 +1619,7 @@ class Stylesheet NamedNodeMap attrs = node.getAttributes(); String s = getAttribute(attrs, "select"); if (s == null) - { - s = "."; - } + s = "."; Expr select = (Expr) xpath.compile(s); String l = getAttribute(attrs, "lang"); TemplateNode lang = (l == null) ? null : @@ -1728,9 +1669,7 @@ class Stylesheet ret.add(new WithParam(name, expr)); } else - { - ret.add(new WithParam(name, content)); - } + ret.add(new WithParam(name, content)); } node = node.getNextSibling(); } @@ -1757,27 +1696,19 @@ class Stylesheet { String prefix = attr.getLocalName(); if (XMLConstants.XMLNS_ATTRIBUTE.equals(prefix)) - { - prefix = "#default"; - } + prefix = "#default"; String ns = attr.getNodeValue(); // Should the namespace be excluded? if (XSL_NS.equals(ns) || extensionElementPrefixes.contains(prefix) || elementExcludeResultPrefixes.contains(prefix) || excludeResultPrefixes.contains(prefix)) - { - continue; - } + continue; // Is the namespace already defined on the target? if (prefix == "#default") - { - prefix = null; - } + prefix = null; if (target.lookupNamespaceURI(prefix) != null) - { - continue; - } + continue; attr = attr.cloneNode(true); attr = doc.adoptNode(attr); target.getAttributes().setNamedItemNS(attr); @@ -1786,23 +1717,17 @@ class Stylesheet } Node parent = source.getParentNode(); if (parent != null) - { - addNamespaceNodes(parent, target, doc, elementExcludeResultPrefixes); - } + addNamespaceNodes(parent, target, doc, elementExcludeResultPrefixes); } static final String getAttribute(NamedNodeMap attrs, String name) { Node attr = attrs.getNamedItem(name); if (attr == null) - { - return null; - } + return null; String ret = attr.getNodeValue(); if (ret.length() == 0) - { - return null; - } + return null; return ret; } diff --git a/libjava/classpath/gnu/xml/transform/TransformerFactoryImpl.java b/libjava/classpath/gnu/xml/transform/TransformerFactoryImpl.java index dde2017ff6b..16e3363f8c6 100644 --- a/libjava/classpath/gnu/xml/transform/TransformerFactoryImpl.java +++ b/libjava/classpath/gnu/xml/transform/TransformerFactoryImpl.java @@ -1,5 +1,5 @@ /* TransformerFactoryImpl.java -- - Copyright (C) 2004 Free Software Foundation, Inc. + Copyright (C) 2004,2006 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -37,7 +37,11 @@ exception statement from your version. */ package gnu.xml.transform; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.InputStream; import java.io.IOException; +import java.io.OutputStream; import java.net.URL; import java.util.Iterator; import java.util.LinkedHashMap; @@ -73,7 +77,7 @@ public class TransformerFactoryImpl { final XPathFactory xpathFactory; - final XSLURIResolver resolver; + final XSLURIResolver resolver; ErrorListener userListener; URIResolver userResolver; @@ -341,5 +345,46 @@ public class TransformerFactoryImpl { return userListener; } + + /** + * Syntax: TransformerFactoryImpl [ [ []]] + */ + public static void main(String[] args) + throws Exception + { + InputStream stylesheet = null, in = null; + OutputStream out = null; + try + { + if (args.length > 0) + { + stylesheet = new FileInputStream(args[0]); + if (args.length > 1) + { + in = new FileInputStream(args[1]); + if (args.length > 2) + out = new FileOutputStream(args[2]); + } + } + if (in == null) + in = System.in; + if (out == null) + out = System.out; + TransformerFactory f = new TransformerFactoryImpl(); + Transformer t = (stylesheet != null) ? + f.newTransformer(new StreamSource(stylesheet)) : + f.newTransformer(); + t.transform(new StreamSource(in), new StreamResult(out)); + } + finally + { + if (stylesheet != null) + stylesheet.close(); + if (in != null && in instanceof FileInputStream) + in.close(); + if (out != null && out instanceof FileOutputStream) + out.close(); + } + } } diff --git a/libjava/classpath/gnu/xml/transform/TransformerImpl.java b/libjava/classpath/gnu/xml/transform/TransformerImpl.java index cf404884736..b7ff668843b 100644 --- a/libjava/classpath/gnu/xml/transform/TransformerImpl.java +++ b/libjava/classpath/gnu/xml/transform/TransformerImpl.java @@ -1,5 +1,5 @@ /* TransformerImpl.java -- - Copyright (C) 2004, 2005 Free Software Foundation, Inc. + Copyright (C) 2004,2005,2006 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -128,8 +128,6 @@ class TransformerImpl { // Suppress mutation events ((DomDocument) doc).setBuilding(true); - // TODO find a better/more generic way of doing this than - // casting } // Get the result tree Node parent = null, nextSibling = null; @@ -147,8 +145,6 @@ class TransformerImpl DomDocument drdoc = (DomDocument) rdoc; drdoc.setBuilding(true); drdoc.setCheckWellformedness(false); - // TODO find a better/more generic way of doing this than - // casting } } boolean created = false; @@ -184,9 +180,7 @@ class TransformerImpl { // Done transforming, reset document if (doc instanceof DomDocument) - { - ((DomDocument) doc).setBuilding(false); - } + ((DomDocument) doc).setBuilding(false); throw e; } } @@ -214,17 +208,11 @@ class TransformerImpl } Document sourceDoc = context.getOwnerDocument(); if (sourceDoc != resultDoc) - { - clone = resultDoc.adoptNode(clone); - } + clone = resultDoc.adoptNode(clone); if (nextSibling != null) - { - parent.insertBefore(clone, nextSibling); - } + parent.insertBefore(clone, nextSibling); else - { - parent.appendChild(clone); - } + parent.appendChild(clone); } else { @@ -256,15 +244,18 @@ class TransformerImpl DomDocument resultDoc = (DomDocument) parent; Node root = resultDoc.getDocumentElement(); // Add doctype if specified - if ((publicId != null || systemId != null) && - root != null) + if (publicId != null || systemId != null) { - // We must know the name of the root element to - // create the document type - resultDoc.appendChild(new DomDoctype(resultDoc, - root.getNodeName(), - publicId, - systemId)); + if (root != null) + { + // We must know the name of the root element to + // create the document type + DocumentType doctype = new DomDoctype(resultDoc, + root.getNodeName(), + publicId, + systemId); + resultDoc.insertBefore(doctype, root); + } } resultDoc.setBuilding(false); resultDoc.setCheckWellformedness(true); @@ -279,43 +270,25 @@ class TransformerImpl (Document) parent : parent.getOwnerDocument(); DOMImplementation impl = resultDoc.getImplementation(); - DocumentType doctype = - impl.createDocumentType(resultDoc.getNodeName(), - publicId, - systemId); - // Try to insert doctype before first element - Node ctx = parent.getFirstChild(); - for (; ctx != null && - ctx.getNodeType() != Node.ELEMENT_NODE; - ctx = ctx.getNextSibling()) + Node root = resultDoc.getDocumentElement(); + if (root != null) { - } - if (ctx != null) - { - parent.insertBefore(doctype, ctx); - } - else - { - parent.appendChild(doctype); + DocumentType doctype = + impl.createDocumentType(root.getNodeName(), + publicId, + systemId); + resultDoc.insertBefore(doctype, root); } } } if (version != null) - { - parent.setUserData("version", version, stylesheet); - } + parent.setUserData("version", version, stylesheet); if (omitXmlDeclaration) - { - parent.setUserData("omit-xml-declaration", "yes", stylesheet); - } + parent.setUserData("omit-xml-declaration", "yes", stylesheet); if (standalone) - { - parent.setUserData("standalone", "yes", stylesheet); - } + parent.setUserData("standalone", "yes", stylesheet); if (mediaType != null) - { - parent.setUserData("media-type", mediaType, stylesheet); - } + parent.setUserData("media-type", mediaType, stylesheet); if (cdataSectionElements != null) { List list = new LinkedList(); @@ -390,13 +363,9 @@ class TransformerImpl if (ex != null) { if (errorListener != null) - { - errorListener.error(new TransformerException(ex)); - } + errorListener.error(new TransformerException(ex)); else - { - ex.printStackTrace(System.err); - } + ex.printStackTrace(System.err); } } else if (outputTarget instanceof SAXResult) @@ -407,22 +376,16 @@ class TransformerImpl ContentHandler ch = sr.getHandler(); LexicalHandler lh = sr.getLexicalHandler(); if (lh == null && ch instanceof LexicalHandler) - { - lh = (LexicalHandler) ch; - } + lh = (LexicalHandler) ch; SAXSerializer serializer = new SAXSerializer(); serializer.serialize(parent, ch, lh); } catch (SAXException e) { if (errorListener != null) - { - errorListener.error(new TransformerException(e)); - } + errorListener.error(new TransformerException(e)); else - { - e.printStackTrace(System.err); - } + e.printStackTrace(System.err); } } } @@ -430,7 +393,7 @@ class TransformerImpl /** * Strip whitespace from the source tree. */ - void strip(Node node) + boolean strip(Node node) throws TransformerConfigurationException { short nt = node.getNodeType(); @@ -438,44 +401,101 @@ class TransformerImpl { // Replace entity reference with its content Node parent = node.getParentNode(); + Node nextSibling = node.getNextSibling(); Node child = node.getFirstChild(); - if (child != null) - { - strip(child); - } while (child != null) { Node next = child.getNextSibling(); node.removeChild(child); - parent.insertBefore(child, node); + if (nextSibling != null) + parent.insertBefore(child, nextSibling); + else + parent.appendChild(child); child = next; } - parent.removeChild(node); + return true; } if (nt == Node.TEXT_NODE || nt == Node.CDATA_SECTION_NODE) { - if (!stylesheet.isPreserved((Text) node)) + // Denormalize text into whitespace and non-whitespace nodes + String text = node.getNodeValue(); + String[] tokens = tokenizeWhitespace(text); + if (tokens.length > 1) { - node.getParentNode().removeChild(node); - } - else - { - String text = node.getNodeValue(); - String stripped = text.trim(); - if (!text.equals(stripped)) + node.setNodeValue(tokens[0]); + Node parent = node.getParentNode(); + Node nextSibling = node.getNextSibling(); + Document doc = node.getOwnerDocument(); + for (int i = 1; i < tokens.length; i++) { - node.setNodeValue(stripped); + Node newChild = (nt == Node.CDATA_SECTION_NODE) ? + doc.createCDATASection(tokens[i]) : + doc.createTextNode(tokens[i]); + if (nextSibling != null) + parent.insertBefore(newChild, nextSibling); + else + parent.appendChild(newChild); } } + return !stylesheet.isPreserved((Text) node, true); } else { - for (Node child = node.getFirstChild(); child != null; - child = child.getNextSibling()) + Node child = node.getFirstChild(); + while (child != null) { - strip(child); + boolean remove = strip(child); + Node next = child.getNextSibling(); + if (remove) + node.removeChild(child); + child = next; } } + return false; + } + + /** + * Tokenize the specified text into contiguous whitespace-only and + * non-whitespace chunks. + */ + private static String[] tokenizeWhitespace(String text) + { + int len = text.length(); + int start = 0, end = len - 1; + // Find index of text start + for (int i = 0; i < len; i++) + { + char c = text.charAt(i); + boolean whitespace = (c == ' ' || c == '\n' || c == '\t' || c == '\r'); + if (whitespace) + start++; + else + break; + } + if (start == end) // all whitespace + return new String[] { text }; + // Find index of text end + for (int i = end; i > start; i--) + { + char c = text.charAt(i); + boolean whitespace = (c == ' ' || c == '\n' || c == '\t' || c == '\r'); + if (whitespace) + end--; + else + break; + } + if (start == 0 && end == len - 1) // all non-whitespace + return new String[] { text }; + // whitespace, then text, then whitespace + String[] ret = (start > 0 && end < len - 1) ? + new String[3] : new String[2]; + int i = 0; + if (start > 0) + ret[i++] = text.substring(0, start); + ret[i++] = text.substring(start, end + 1); + if (end < len - 1) + ret[i++] = text.substring(end + 1); + return ret; } /** @@ -487,6 +507,7 @@ class TransformerImpl throws IOException { OutputStream out = null; + boolean created = false; try { out = sr.getOutputStream(); @@ -494,9 +515,7 @@ class TransformerImpl { Writer writer = sr.getWriter(); if (writer != null) - { - out = new WriterOutputStream(writer); - } + out = new WriterOutputStream(writer); } if (out == null) { @@ -523,6 +542,7 @@ class TransformerImpl URL url = new URL(systemId); out = new FileOutputStream(url.getPath()); } + created = true; } out = new BufferedOutputStream(out); StreamSerializer serializer = @@ -539,10 +559,8 @@ class TransformerImpl { try { - if (out != null) - { - out.close(); - } + if (out != null && created) + out.close(); } catch (IOException e) { @@ -564,17 +582,13 @@ class TransformerImpl public void setParameter(String name, Object value) { if (stylesheet != null) - { - stylesheet.bindings.set(new QName(null, name), value, Bindings.PARAM); - } + stylesheet.bindings.set(new QName(null, name), value, Bindings.PARAM); } public Object getParameter(String name) { if (stylesheet != null) - { - return stylesheet.bindings.get(new QName(null, name), null, 1, 1); - } + return stylesheet.bindings.get(new QName(null, name), null, 1, 1); return null; } @@ -601,13 +615,9 @@ class TransformerImpl throws IllegalArgumentException { if (oformat == null) - { - outputProperties.clear(); - } + outputProperties.clear(); else - { - outputProperties.putAll(oformat); - } + outputProperties.putAll(oformat); } public Properties getOutputProperties() @@ -689,9 +699,7 @@ class TransformerImpl StringBuffer buf = new StringBuffer(); buf.append('\n'); for (int i = 0; i < offset + 1; i++) - { - buf.append(INDENT_WHITESPACE); - } + buf.append(INDENT_WHITESPACE); String ws = buf.toString(); for (Iterator i = children.iterator(); i.hasNext(); ) { @@ -703,9 +711,7 @@ class TransformerImpl buf.append('\n'); ws = buf.toString(); for (int i = 0; i < offset; i++) - { - buf.append(INDENT_WHITESPACE); - } + buf.append(INDENT_WHITESPACE); node.appendChild(doc.createTextNode(ws)); } } @@ -749,9 +755,7 @@ class TransformerImpl while (ctx != null) { if (ctx.hasChildNodes()) - { - convertCdataSectionElements(doc, ctx, list); - } + convertCdataSectionElements(doc, ctx, list); ctx = ctx.getNextSibling(); } } @@ -761,9 +765,7 @@ class TransformerImpl String ln1 = qname.getLocalPart(); String ln2 = node.getLocalName(); if (ln2 == null) - { - return ln1.equals(node.getNodeName()); - } + return ln1.equals(node.getNodeName()); else { String uri1 = qname.getNamespaceURI(); diff --git a/libjava/classpath/gnu/xml/transform/ValueOfNode.java b/libjava/classpath/gnu/xml/transform/ValueOfNode.java index 430598a94b3..24c229ea3b3 100644 --- a/libjava/classpath/gnu/xml/transform/ValueOfNode.java +++ b/libjava/classpath/gnu/xml/transform/ValueOfNode.java @@ -1,5 +1,5 @@ /* ValueOfNode.java -- - Copyright (C) 2004 Free Software Foundation, Inc. + Copyright (C) 2004,2006 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -69,26 +69,18 @@ final class ValueOfNode TemplateNode ret = new ValueOfNode(select.clone(stylesheet), disableOutputEscaping); if (children != null) - { - ret.children = children.clone(stylesheet); - } + ret.children = children.clone(stylesheet); if (next != null) - { - ret.next = next.clone(stylesheet); - } + ret.next = next.clone(stylesheet); return ret; } void doApply(Stylesheet stylesheet, QName mode, - Node context, int pos, int len, - Node parent, Node nextSibling) + Node context, int pos, int len, + Node parent, Node nextSibling) throws TransformerException { Object ret = select.evaluate(context, pos, len); - /*if (stylesheet.debug) - { - System.err.println("value-of: " + select + " -> " + ret); - }*/ String value; if (ret instanceof Collection) { @@ -100,47 +92,35 @@ final class ValueOfNode } value = buf.toString(); } + else if (ret == null) + value = ""; else - { - value = Expr._string(context, ret); - } + value = Expr._string(context, ret); if (stylesheet.debug) - { - System.err.println("value-of: "+context+" "+ select + " -> "+ value); - } + System.err.println("value-of: "+context+" "+ select + " -> "+ value); if (value != null && value.length() > 0) { Document doc = (parent instanceof Document) ? (Document) parent : parent.getOwnerDocument(); Text textNode = doc.createTextNode(value); if (disableOutputEscaping) - { - textNode.setUserData("disable-output-escaping", "yes", stylesheet); - } + textNode.setUserData("disable-output-escaping", "yes", stylesheet); if (nextSibling != null) - { - parent.insertBefore(textNode, nextSibling); - } + parent.insertBefore(textNode, nextSibling); else - { - parent.appendChild(textNode); - } + parent.appendChild(textNode); } // value-of doesn't process children if (next != null) - { - next.apply(stylesheet, mode, - context, pos, len, - parent, nextSibling); - } + next.apply(stylesheet, mode, + context, pos, len, + parent, nextSibling); } public boolean references(QName var) { if (select != null && select.references(var)) - { - return true; - } + return true; return super.references(var); } @@ -151,9 +131,7 @@ final class ValueOfNode buf.append("select="); buf.append(select); if (disableOutputEscaping) - { - buf.append(",disableOutputEscaping"); - } + buf.append(",disableOutputEscaping"); buf.append(']'); return buf.toString(); } diff --git a/libjava/classpath/gnu/xml/xpath/Expr.java b/libjava/classpath/gnu/xml/xpath/Expr.java index b4b55dcf94e..76fd49eeff5 100644 --- a/libjava/classpath/gnu/xml/xpath/Expr.java +++ b/libjava/classpath/gnu/xml/xpath/Expr.java @@ -1,5 +1,5 @@ /* Expr.java -- - Copyright (C) 2004 Free Software Foundation, Inc. + Copyright (C) 2004,2006 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -233,9 +233,11 @@ public abstract class Expr */ public static String _local_name(Node context, Collection nodeSet) { - Node node = (nodeSet == null || nodeSet.size() == 0) ? context : - firstNode(nodeSet); - return node.getLocalName(); + if (nodeSet == null || nodeSet.isEmpty()) + return ""; + Node node = firstNode(nodeSet); + String ret = node.getLocalName(); + return (ret == null) ? "" : ret; } /** @@ -248,9 +250,11 @@ public abstract class Expr */ public static String _namespace_uri(Node context, Collection nodeSet) { - Node node = (nodeSet == null || nodeSet.size() == 0) ? context : - firstNode(nodeSet); - return node.getNamespaceURI(); + if (nodeSet == null || nodeSet.isEmpty()) + return ""; + Node node = firstNode(nodeSet); + String ret = node.getNamespaceURI(); + return (ret == null) ? "" : ret; } /** @@ -271,17 +275,18 @@ public abstract class Expr */ public static String _name(Node context, Collection nodeSet) { - Node node = (nodeSet == null || nodeSet.size() == 0) ? context : - firstNode(nodeSet); + if (nodeSet == null || nodeSet.isEmpty()) + return ""; + Node node = firstNode(nodeSet); + String ret = null; switch (node.getNodeType()) { case Node.ATTRIBUTE_NODE: case Node.ELEMENT_NODE: case Node.PROCESSING_INSTRUCTION_NODE: - return node.getNodeName(); - default: - return ""; + ret = node.getNodeName(); } + return (ret == null) ? "" : ret; } /** @@ -371,7 +376,10 @@ public abstract class Expr } if (object instanceof Double) { - return ((Double) object).doubleValue() != 0.0; + Double value = (Double) object; + if (value.isNaN()) + return false; + return value.doubleValue() != 0.0; } if (object instanceof String) { @@ -473,4 +481,15 @@ public abstract class Expr } } + static int intValue(Object val) + { + if (val instanceof Double) + { + Double d = (Double) val; + return d.isNaN() ? 0 : d.intValue(); + } + else + return (int) Math.ceil(_number(null, val)); + } + } diff --git a/libjava/classpath/gnu/xml/xpath/LocalNameFunction.java b/libjava/classpath/gnu/xml/xpath/LocalNameFunction.java index f8ace9cd5f9..84fb6d48288 100644 --- a/libjava/classpath/gnu/xml/xpath/LocalNameFunction.java +++ b/libjava/classpath/gnu/xml/xpath/LocalNameFunction.java @@ -1,5 +1,5 @@ /* LocalNameFunction.java -- - Copyright (C) 2004 Free Software Foundation, Inc. + Copyright (C) 2004,2006 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -38,6 +38,7 @@ exception statement from your version. */ package gnu.xml.xpath; import java.util.Collection; +import java.util.Collections; import java.util.List; import javax.xml.namespace.QName; import org.w3c.dom.Node; @@ -70,7 +71,8 @@ final class LocalNameFunction public Object evaluate(Node context, int pos, int len) { - Object val = (arg == null) ? null : arg.evaluate(context, pos, len); + Object val = (arg == null) ? Collections.singleton(context) : + arg.evaluate(context, pos, len); return _local_name(context, (Collection) val); } diff --git a/libjava/classpath/gnu/xml/xpath/NameFunction.java b/libjava/classpath/gnu/xml/xpath/NameFunction.java index dc5e612037a..ea49741fa57 100644 --- a/libjava/classpath/gnu/xml/xpath/NameFunction.java +++ b/libjava/classpath/gnu/xml/xpath/NameFunction.java @@ -1,5 +1,5 @@ /* NameFunction.java -- - Copyright (C) 2004 Free Software Foundation, Inc. + Copyright (C) 2004,2006 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -38,6 +38,7 @@ exception statement from your version. */ package gnu.xml.xpath; import java.util.Collection; +import java.util.Collections; import java.util.List; import javax.xml.namespace.QName; import org.w3c.dom.Node; @@ -78,7 +79,8 @@ final class NameFunction public Object evaluate(Node context, int pos, int len) { - Object val = (arg == null) ? null : arg.evaluate(context, pos, len); + Object val = (arg == null) ? Collections.singleton(context) : + arg.evaluate(context, pos, len); return _name(context, (Collection) val); } diff --git a/libjava/classpath/gnu/xml/xpath/NameTest.java b/libjava/classpath/gnu/xml/xpath/NameTest.java index c89ba4dd16b..1710103903d 100644 --- a/libjava/classpath/gnu/xml/xpath/NameTest.java +++ b/libjava/classpath/gnu/xml/xpath/NameTest.java @@ -1,5 +1,5 @@ /* NameTest.java -- - Copyright (C) 2004 Free Software Foundation, Inc. + Copyright (C) 2004,2006 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -91,29 +91,32 @@ public final class NameTest return false; } if (any) - { - return true; - } + return true; String uri = qName.getNamespaceURI(); String nodeUri = node.getNamespaceURI(); - String nodeLocalName = node.getLocalName(); - if (nodeLocalName != null && !equal(uri, nodeUri)) - { - return false; - } + if (!equal(uri, nodeUri)) + return false; if (anyLocalName) - { - return true; - } + return true; String localName = qName.getLocalPart(); - if (nodeLocalName != null) + String nodeLocalName = getLocalName(node); + return (localName.equals(nodeLocalName)); + } + + static String getLocalName(Node node) + { + String localName = node.getLocalName(); + if (localName == null) { - nodeLocalName = node.getNodeName(); + localName = node.getNodeName(); + int ci = localName.indexOf(':'); + if (ci != -1) + localName = localName.substring(ci + 1); } - return (localName.equals(nodeLocalName)); + return localName; } - final boolean equal(String s1, String s2) + static boolean equal(String s1, String s2) { return (((s1 == null || s1.length() == 0) && (s2 == null || s2.length() == 0)) || @@ -133,9 +136,7 @@ public final class NameTest public String toString () { if (any) - { - return "*"; - } + return "*"; return qName.toString(); } diff --git a/libjava/classpath/gnu/xml/xpath/NamespaceTest.java b/libjava/classpath/gnu/xml/xpath/NamespaceTest.java index 6d41166bcb9..d93b76d9de8 100644 --- a/libjava/classpath/gnu/xml/xpath/NamespaceTest.java +++ b/libjava/classpath/gnu/xml/xpath/NamespaceTest.java @@ -81,28 +81,22 @@ public final class NamespaceTest if (XMLConstants.XMLNS_ATTRIBUTE_NS_URI.equals(uri) || XMLConstants.XMLNS_ATTRIBUTE.equals(node.getPrefix()) || XMLConstants.XMLNS_ATTRIBUTE.equals(node.getNodeName())) - { - break; - } + break; // Fall through default: // Only process namespace attributes return false; } if (any) - { - return true; - } + return true; + String uri = qName.getNamespaceURI(); + String nodeUri = node.getNamespaceURI(); + if (!NameTest.equal(uri, nodeUri)) + return false; if (anyLocalName) - { - return true; - } + return true; String localName = qName.getLocalPart(); - String nodeLocalName = node.getLocalName(); - if (nodeLocalName == null) - { - nodeLocalName = node.getNodeName(); - } + String nodeLocalName = NameTest.getLocalName(node); return (localName.equals(nodeLocalName)); } @@ -119,9 +113,7 @@ public final class NamespaceTest public String toString () { if (any) - { - return "*"; - } + return "*"; return qName.toString(); } diff --git a/libjava/classpath/gnu/xml/xpath/NamespaceUriFunction.java b/libjava/classpath/gnu/xml/xpath/NamespaceUriFunction.java index e67ec42065e..3ae46f7ee71 100644 --- a/libjava/classpath/gnu/xml/xpath/NamespaceUriFunction.java +++ b/libjava/classpath/gnu/xml/xpath/NamespaceUriFunction.java @@ -1,5 +1,5 @@ /* NamespaceUriFunction.java -- - Copyright (C) 2004 Free Software Foundation, Inc. + Copyright (C) 2004,2006 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -38,6 +38,7 @@ exception statement from your version. */ package gnu.xml.xpath; import java.util.Collection; +import java.util.Collections; import java.util.List; import javax.xml.namespace.QName; import org.w3c.dom.Node; @@ -70,7 +71,8 @@ final class NamespaceUriFunction public Object evaluate(Node context, int pos, int len) { - Object val = (arg == null) ? null : arg.evaluate(context, pos, len); + Object val = (arg == null) ? Collections.singleton(context) : + arg.evaluate(context, pos, len); return _namespace_uri(context, (Collection) val); } diff --git a/libjava/classpath/gnu/xml/xpath/NodeTypeTest.java b/libjava/classpath/gnu/xml/xpath/NodeTypeTest.java index 807374132e5..09e92d0d9a2 100644 --- a/libjava/classpath/gnu/xml/xpath/NodeTypeTest.java +++ b/libjava/classpath/gnu/xml/xpath/NodeTypeTest.java @@ -1,5 +1,5 @@ /* NodeTypeTest.java -- - Copyright (C) 2004 Free Software Foundation, Inc. + Copyright (C) 2004,2006 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -39,6 +39,7 @@ package gnu.xml.xpath; import javax.xml.namespace.QName; import org.w3c.dom.Node; +import org.w3c.dom.ProcessingInstruction; /** * Tests whether a node is of a given type. @@ -83,17 +84,20 @@ public final class NodeTypeTest case Node.TEXT_NODE: case Node.CDATA_SECTION_NODE: case Node.COMMENT_NODE: + if (type > 0) + { + if (nodeType != type) + return false; + } + return true; case Node.PROCESSING_INSTRUCTION_NODE: if (type > 0) { if (nodeType != type) - { - return false; - } - if (data != null && !data.equals(node.getNodeValue())) - { - return false; - } + return false; + if (data != null && + !data.equals(((ProcessingInstruction) node).getTarget())) + return false; } return true; default: diff --git a/libjava/classpath/gnu/xml/xpath/Selector.java b/libjava/classpath/gnu/xml/xpath/Selector.java index 598038064b5..93408e48b23 100644 --- a/libjava/classpath/gnu/xml/xpath/Selector.java +++ b/libjava/classpath/gnu/xml/xpath/Selector.java @@ -1,5 +1,5 @@ /* Selector.java -- - Copyright (C) 2004 Free Software Foundation, Inc. + Copyright (C) 2004,2006 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -85,11 +85,13 @@ public final class Selector public Selector(int axis, List tests) { this.axis = axis; - this.tests = new Test[tests.size()]; - tests.toArray(this.tests); - if (axis == NAMESPACE && - this.tests.length > 0 && - this.tests[0] instanceof NameTest) + int len = tests.size(); + this.tests = new Test[(len == 0) ? 1 : len]; + if (len > 0) + tests.toArray(this.tests); + else + this.tests[0] = new NameTest(null, true, true); + if (axis == NAMESPACE && this.tests[0] instanceof NameTest) { NameTest nt = (NameTest) this.tests[0]; this.tests[0] = new NamespaceTest(nt.qName, nt.anyLocalName, nt.any); @@ -111,16 +113,12 @@ public final class Selector { case CHILD: if (nodeType == Node.ATTRIBUTE_NODE) - { - return false; - } + return false; break; case ATTRIBUTE: case NAMESPACE: if (nodeType != Node.ATTRIBUTE_NODE) - { - return false; - } + return false; break; case DESCENDANT_OR_SELF: return true; @@ -132,13 +130,13 @@ public final class Selector { int pos = getContextPosition(context); int len = getContextSize(context); + if (len == 0) + System.err.println("WARNING: context size is 0"); for (int j = 0; j < tlen && len > 0; j++) { Test test = tests[j]; if (!test.matches(context, pos, len)) - { - return false; - } + return false; } } return true; @@ -149,9 +147,7 @@ public final class Selector int pos = 1; for (ctx = ctx.getPreviousSibling(); ctx != null; ctx = ctx.getPreviousSibling()) - { - pos++; - } + pos++; return pos; } @@ -159,15 +155,17 @@ public final class Selector { if (ctx.getNodeType() == Node.ATTRIBUTE_NODE) { - Node parent = ((Attr) ctx).getOwnerElement(); - return parent.getAttributes().getLength(); - } - Node parent = ctx.getParentNode(); - if (parent != null) - { - return parent.getChildNodes().getLength(); + Node owner = ((Attr) ctx).getOwnerElement(); + return owner.getAttributes().getLength(); } - return 1; + int count = 1; + Node sib = ctx.getPreviousSibling(); + for (; sib != null; sib = sib.getPreviousSibling()) + count++; + sib = ctx.getNextSibling(); + for (; sib != null; sib = sib.getNextSibling()) + count++; + return count; } public Object evaluate(Node context, int pos, int len) @@ -175,7 +173,6 @@ public final class Selector Set acc = new LinkedHashSet(); addCandidates(context, acc); List candidates = new ArrayList(acc); - //Collections.sort(candidates, documentOrderComparator); List ret = filterCandidates(candidates, false); return ret; } @@ -184,11 +181,8 @@ public final class Selector { Set acc = new LinkedHashSet(); for (Iterator i = ns.iterator(); i.hasNext(); ) - { - addCandidates((Node) i.next(), acc); - } + addCandidates((Node) i.next(), acc); List candidates = new ArrayList(acc); - //Collections.sort(candidates, documentOrderComparator); List ret = filterCandidates(candidates, true); return ret; } @@ -230,17 +224,7 @@ public final class Selector } } if (test.matches(node, i + 1, len)) - { - successful.add(node); - } - /* - System.err.println("Testing "+node); - int p = getContextPosition(node); - int l = getContextSize(node); - if (test.matches(node, p, l)) - { - successful.add(node); - }*/ + successful.add(node); } candidates = successful; len = candidates.size(); @@ -305,9 +289,7 @@ public final class Selector { acc.add(child); if (recurse) - { - addChildNodes(child, acc, recurse); - } + addChildNodes(child, acc, recurse); child = child.getNextSibling(); } } @@ -320,55 +302,62 @@ public final class Selector { acc.add(parent); if (recurse) - { - addParentNode(parent, acc, recurse); - } + addParentNode(parent, acc, recurse); } } void addFollowingNodes(Node context, Collection acc, boolean recurse) { - Node cur = context.getNextSibling(); + if (context != null && recurse) + addChildNodes(context, acc, true); + Node cur = (context.getNodeType() == Node.ATTRIBUTE_NODE) ? null : + context.getNextSibling(); while (cur != null) { acc.add(cur); if (recurse) - { - addChildNodes(cur, acc, true); - } + addChildNodes(cur, acc, true); cur = cur.getNextSibling(); } if (recurse) { - context = (context.getNodeType() == Node.ATTRIBUTE_NODE) ? - ((Attr) context).getOwnerElement() : context.getParentNode(); - if (context != null) + while (context != null) { - addFollowingNodes(context, acc, recurse); + context = (context.getNodeType() == Node.ATTRIBUTE_NODE) ? + ((Attr) context).getOwnerElement() : context.getParentNode(); + if (context != null) + { + cur = context.getNextSibling(); + while (cur != null) + { + acc.add(cur); + if (recurse) + addChildNodes(cur, acc, true); + cur = cur.getNextSibling(); + } + } } } } void addPrecedingNodes(Node context, Collection acc, boolean recurse) { - Node cur = context.getPreviousSibling(); + Node cur = (context.getNodeType() == Node.ATTRIBUTE_NODE) ? null : + context.getPreviousSibling(); while (cur != null) { acc.add(cur); if (recurse) - { - addChildNodes(cur, acc, true); - } + addChildNodes(cur, acc, true); cur = cur.getPreviousSibling(); } if (recurse) { - context = (context.getNodeType() == Node.ATTRIBUTE_NODE) ? - ((Attr) context).getOwnerElement() : context.getParentNode(); - if (context != null) - { - addPrecedingNodes(context, acc, recurse); - } + cur = context; + cur = (cur.getNodeType() == Node.ATTRIBUTE_NODE) ? + ((Attr) cur).getOwnerElement() : cur.getParentNode(); + if (cur != null) + addPrecedingNodes(cur, acc, recurse); } } @@ -399,9 +388,7 @@ public final class Selector { Node attr = attrs.item(i); if (isNamespaceAttribute(attr)) - { - acc.add(attr); - } + acc.add(attr); } } } @@ -419,9 +406,7 @@ public final class Selector int len = tests.length; List tests2 = new ArrayList(len); for (int i = 0; i < len; i++) - { - tests2.add(tests[i].clone(context)); - } + tests2.add(tests[i].clone(context)); return new Selector(axis, tests2); } @@ -430,9 +415,7 @@ public final class Selector for (int i = 0; i < tests.length; i++) { if (tests[i].references(var)) - { - return true; - } + return true; } return false; } @@ -451,13 +434,9 @@ public final class Selector case ATTRIBUTE: if (tests.length == 0 || (tests[0] instanceof NameTest)) - { - buf.append('@'); - } + buf.append('@'); else - { - buf.append("attribute::"); - } + buf.append("attribute::"); break; case CHILD: //buf.append("child::"); @@ -481,9 +460,7 @@ public final class Selector if (tests.length == 0 || (tests[0] instanceof NodeTypeTest && ((NodeTypeTest) tests[0]).type == 0)) - { - return ".."; - } + return ".."; buf.append("parent::"); break; case PRECEDING: @@ -496,22 +473,16 @@ public final class Selector if (tests.length == 0 || (tests[0] instanceof NodeTypeTest && ((NodeTypeTest) tests[0]).type == 0)) - { - return "."; - } + return "."; buf.append("self::"); break; } if (tests.length == 0) - { - buf.append('*'); - } + buf.append("[error]"); else { for (int i = 0; i < tests.length; i++) - { - buf.append(tests[i]); - } + buf.append(tests[i]); } return buf.toString(); } diff --git a/libjava/classpath/gnu/xml/xpath/SubstringFunction.java b/libjava/classpath/gnu/xml/xpath/SubstringFunction.java index d65c4facb8a..e926f6c7aeb 100644 --- a/libjava/classpath/gnu/xml/xpath/SubstringFunction.java +++ b/libjava/classpath/gnu/xml/xpath/SubstringFunction.java @@ -78,31 +78,20 @@ final class SubstringFunction Object val1 = arg1.evaluate(context, pos, len); Object val2 = arg2.evaluate(context, pos, len); String s = _string(context, val1); - int p = (val2 instanceof Double) ? - ((Double) val2).intValue() : - (int) Math.round(_number(context, val2)); - p--; + int p = Expr.intValue(val2) - 1; if (p < 0) - { - p = 0; - } + p = 0; int l = s.length() - p; if (l <= 0) - { - return ""; - } + return ""; if (arg3 != null) { Object val3 = arg3.evaluate(context, pos, len); - int v3 = (val3 instanceof Double) ? - ((Double) val3).intValue() : - (int) Math.round(_number(context, val3)); + int v3 = Expr.intValue(val3); if (v3 < l) - { - l = v3; - } + l = v3; } return s.substring(p, p + l); -- cgit v1.2.3