diff options
author | Mark Wielaard <mark@gcc.gnu.org> | 2006-01-17 18:09:40 +0000 |
---|---|---|
committer | Mark Wielaard <mark@gcc.gnu.org> | 2006-01-17 18:09:40 +0000 |
commit | 2127637945ea6b763966398130e0770fa993c860 (patch) | |
tree | c976ca91e3ef0bda3b34b37c0195145638d8d08e /libjava/classpath/gnu | |
parent | bcb36c3e02e3bd2843aad1b9888513dfb5d6e337 (diff) | |
download | gcc-2127637945ea6b763966398130e0770fa993c860.zip gcc-2127637945ea6b763966398130e0770fa993c860.tar.gz gcc-2127637945ea6b763966398130e0770fa993c860.tar.bz2 |
Imported GNU Classpath 0.20
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.
From-SVN: r109831
Diffstat (limited to 'libjava/classpath/gnu')
92 files changed, 14604 insertions, 1532 deletions
diff --git a/libjava/classpath/gnu/CORBA/Poa/AOM.java b/libjava/classpath/gnu/CORBA/Poa/AOM.java index 9faf088..70e787a 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 6f2a019..460a0a6 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. - * <ul> - * <li>If the POA has both the RETAIN and the UNIQUE_ID policy and - * the specified servant is active, the method return the Object Id associated - * with that servant. - * </li><li> - * If the POA has both the RETAIN and the IMPLICIT_ACTIVATION policy and - * either the POA has the MULTIPLE_ID policy or the specified servant is - * inactive, the method activates the servant using a POA-generated Object Id - * and the Interface Id associated with the servant, and returns that - * Object Id. - * </li> - * <li>If the POA has the USE_DEFAULT_SERVANT policy, the servant specified - * is the default servant, and the method is being invoked in the context of - * executing a request on the default servant, the method returns the - * ObjectId associated with the current invocation. - * </li> - * </ul> - * @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. + * <ul> + * <li>If the POA has both the RETAIN and the UNIQUE_ID policy and the + * specified servant is active, the method return the Object Id associated + * with that servant. </li> + * <li> If the POA has both the RETAIN and the IMPLICIT_ACTIVATION policy and + * either the POA has the MULTIPLE_ID policy or the specified servant is + * inactive, the method activates the servant using a POA-generated Object Id + * and the Interface Id associated with the servant, and returns that Object + * Id. </li> + * <li>If the POA has the USE_DEFAULT_SERVANT policy, the servant specified + * is the default servant, and the method is being invoked in the context of + * executing a request on the default servant, the method returns the ObjectId + * associated with the current invocation. </li> + * </ul> + * + * @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 bb8f602..43a37de 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 436a544..82d1d71 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 141c854..8e8eecb 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 d80306c..d7217aa 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 c9ed301..195304d 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 85cb1e4..054ebaa 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 0000000..12d757e --- /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 0000000..7b0656a --- /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; + +/** <p>The <code>ClassPersistenceDelegate</code> creates + * <code>Expression</code> instances which denote class resolutions.</p> + * + * <p>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 <code>Class.forName</code>. However for the <code>Class</code> class + * we call <code>getClass()</code> on a <code>String.class</code> instance. + * This in turn lead to the resolution of the String class which is always + * encoded as <code>"".getClass()</code>. Finally the <code>Encoder</code> + * 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 0000000..f1375d2 --- /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; + +/** <p>A <code>PersistenceDelegate</code> 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.</p> + * + * <p>It is used for <code>Set</code> and <code>List</code> + * implementations.</p> + * + * @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 0000000..9126d49 --- /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 <code>Contect</code> object describes the current state + * and the call number while processing the original object + * tree in the {@link ScanEngine}. + * + * <p>The class allows to distinguish the different calling states + * and is neccessary for the child element skipping feature of + * the {@link GenericScannerState}.</p> + * + * @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 0000000..3c3f8a3 --- /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. + * + * <p>Furthermore it can optionally skip a certain number of child + * elements. The algorithm can cope with the fact that one + * <code>GenericScannerState</code> instance may be called at + * different levels of recursions.</p> + * + * @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 0000000..054f1f0 --- /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. + * + * <p>Consider the call hierarchy: + * <code> + * methodInvocation + * objectInstantiation + * classResolution* + * objectInstantiation + * classResolution + * </code> + * </p> + * + * <p>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 <code>IgnoringScannerState</code>.</p> + * + * <p>Setting the default successor of a <code>IgnoringScannerState</code> + * to itself causes all elements of the call hierarchy to be skipped + * until another state is reached by going back.</p> + * + * @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 0000000..84cdce9 --- /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 0000000..eca5c3d --- /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; + +/** + * <p> + * ObjectId provides an object identification mechanism which gives each object + * a name in the form <code><class><Nameindex></code>. + * </p> + * + * <p> + * 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. + * </p> + * <p> + * An <code>ObjectId</code> 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. + * </p> + * + * @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 : "<unused id>"; + } + + /** + * <p> + * Generates a simple Id by concatenating a class name with a self-increasing + * number. + * </p> + */ + 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 0000000..8cb3705 --- /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 0000000..fb6e061 --- /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 <code>ScannerState</code> 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 0000000..f4eade1 --- /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; + +/** <p><code>Root</code> provides a simple interface to a tree of + * objects.</p> + * + * <p>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 <code>Root</code> and + * {@link gnu.java.beans.encoder.elements.Element} class can provide + * context information which is used to write the best fitting + * XML representation.</p> + * + * @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(); + } + + /** <p>Adds another child element to the tree.</p> + * + * <p>The new element automatically becomes the current + * element.</p> + * + * @param elem The new child element. + */ + public void addChild(Element elem) + { + current.addChild(elem); + + parents.push(current); + current = elem; + } + + /** + * <p>Marks that the end of the current element + * is reached and that no more childs are added to + * it.</p> + * + * <p>The behavior is to return to the nearest parent + * element.</p> + */ + public void end() + { + current = (Element) parents.pop(); + } + + /** + * <p>Goes back to the nearest parent element but + * deletes the just created child.</p> + * + * <p>This is used if something went wrong while + * processing the child element's {@link java.beans.Expression} + * or {@link java.beans.Statement}.</p> + * + */ + public void deleteLast() + { + current = (Element) parents.pop(); + + current.removeLast(); + } + + /** + * <p>Traverses the elements in the object tree + * and creates their XML representation in the output + * stream of the given {@link Writer}.</p> + * + * <p>Finally the <code>Writer</code> is flushed.</p> + * + * @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(); + } + } + + /** <p>A special Element implementation that represents the + * encoder's context.</p> + * + * <p>This element is written only once per Writer.</p> + * + * <p>It is assumed that this element is never empty to simplify + * the implementation.</p> + * + * @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 0000000..edf07d7 --- /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; + +/** <p>The <code>ScanEngine</code> 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.</p> + * + * <p>How does it work?</p> + * <p>The <code>ScanEngine</code> sits below the {@link java.beans.XMLEncoder} + * class and is called by it exclusively. The <code>XMLEncoder</code> sends + * interpretive data by invoking {@link #writeExpression}, {@link #writeStatement} + * and {@link #writeObject}. The invocations of <code>writeExpression</code> and + * <code>writeStatement</code> are usually nested into each other and provide + * more information then necessary to generate the XML representation. + * Furthermore the meaning of certain <code>Expressions</code> differs + * depending on the enclosing elements or the inner elements have to be + * simply discarded.</p> + * + * <p>To cope with this state dependant nature the <code>ScanEngine</code> + * contains a state machine which is programmed statically (no adjustments are + * needed, all <code>ScanEngine</code> engines use the same setup). The + * <code>ScanEngine</code>'s job is to decode the <code>Expression</code>s, + * <code>Statement</code>s and certain objects (namely <code>String</code>, + * <code>null</code> 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 <code>Expression</code> 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 <code>ScanEngine</code> 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.</p> + * + * <p>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 <code>writeStatement</code>, <code>writeExpression</code> + * in the <code>XMLEncoder</code> ends by calling the {@link #end} method. + * Note that due to the inheritance relationship of <code>Encoder</code> + * and <code>XMLEncoder</code> it is impossible for the + * <code>ScanEngine</code> 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.</p> + * + * <p>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 <code>ObjectId</code> + * instance is stored in the <code>ScanEngine</code> and gets cleared whenever + * the {@link #flush} method is called. This method also writes the currently + * built object tree and generates the XML representation.</p> + * + * @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 <code>ScannerState</code> under a certain name. + * + * @param name Name of the state + * @param state The <code>ScannerState</code> 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. + * + * <p>Objects are unsuitable if they are an instance of a primitive wrapper + * or String.</p> + * + * @param value The object to retrieve an id for. + * @return The id for the object or <code>null</code>. + */ + 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 <void> 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. + * + * <p>This involves saving the current state, retrieving it's + * successor and setting it as the current state.</p> + * + * @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. + * + * <p>More details about this method can be found in this + * class' introduction.</p> + * + * @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. + * + * <p>More details about this method can be found in this + * class' introduction.</p> + * + * @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. + * + * <p>More details about this method can be found in this + * class' introduction.</p> + * + * @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. + * + * <p>More details about this method can be found in this + * class' introduction.</p> + * + * @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. + * + * <p>More details about this method can be found in this + * class' introduction.</p> + * + * @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. + * + * <p>More details about this method can be found in this + * class' introduction.</p> + * + * @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. + * + * <p>More details about this method can be found in this + * class' introduction.</p> + * + * @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. + * + * <p>More details about this method can be found in this + * class' introduction.</p> + * + * @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. + * + * <p>More details about this method can be found in this + * class' introduction.</p> + * + * @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. + * + * <p>More details about this method can be found in this + * class' introduction.</p> + * + * @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. + * + * <p>More details about this method can be found in this + * class' introduction.</p> + */ + void listSet() + { + transition(ScannerState.TRANSITION_LIST_SET); + + current.listSet(); + } + + /** Event method that denotes the retrieval of a value in a list. + * + * <p>More details about this method can be found in this + * class' introduction.</p> + */ + 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 0000000..888478a --- /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; + +/** <p>Provides the infrastructure for the state machine and the transition + * mechanism.</p> + * + * <p>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.</p> + * + * @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; + } + + /** + * <p>Stores a successor's state name for a certain transition.</p> + * + * <p>This method is only used at the configuration time of the state + * machine.</p> + * + * @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); + } + + /** <p>Retrieves a the state name of a successor for the given transition + * constant.</p> + * + * <p>Returns the default successor's state name if no special setup was + * prepared.</p> + * + * @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); + + /** + * <p>A special event that does not provoke a direct transition.</p> + * + * <p>Instead the transition is done by the <code>ScanEngine</code>: It goes + * back to the previous state and just uses this method to inform the state + * about this happening.</p> + */ + 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 0000000..fdb5f7d4 --- /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 0000000..57203d2 --- /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 <code>Writer</code> represents a simplified interface to an XML + * writer that is used for the XML persistence mechanism. + * + * <p>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.</p> + * + * <p>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.</p> + * + * <p>The <code>Writer</code> 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.</p> + * + * <p>Write + * <code> + * <element/> + * </code> + * by issuing <code>write("element", true)</code> (or any of the other + * write-variants that allows specifying the <code>isEmpty</code> argument) + * and <code>writeEnd(true)</code>.</p> + * + * <p>Write + * <code> + * <element>body</element> + * </code> + * by issuing <code>writeNoChildren("element", "body")</code> and <code>writeNoChildrenEnd()</code>.</p> + * + * <p> + * Write + * <code> + * <element> + * <child1/> + * <child2/> + * ... + * <element/> + * </code> + * by issuing <code>write("element", false)</code> (or any of the other + * write-variants that allows specifying the <code>isEmpty</code> argument) + * and <code>writeEnd(false)</code>.</p> + * + * <p>Note: It is important that the values of <code>isEmpty</code> and + * <code>wasEmpty</code> match. Otherwise strange things might happen to + * the layout.</p> + * + * @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. + * + * <p>If your tag has not generated any body text or child + * elements provide <code>true</code> 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. + * + * <p>Must be used in combination with {@link writeNoChildren} only.</p> + */ + 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 0000000..a9aef89 --- /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 0000000..ca2ce0f --- /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 0000000..0962320 --- /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 0000000..8e640d1 --- /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 0000000..5681d2b --- /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; + +/** <code>Element</code> is the base class for the object tree elements. + * + * <p>It provides the neccessary infrastructure every element subclass + * needs in order to interact with the {@link gnu.java.beans.encoder.Root} + * class.</p> + * + * @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 + * <code>Element</code>. + * + * <p>This can only be done once.</p> + * + * @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 <code>Element</code>. + * + * @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. + * + * <p>This method is useful to decide which formatting variant + * for the XML element can be chosen.</p> + * + * @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 <code>null</code>. + */ + 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. + * + * <p>By default this does <code>writer.writeEnd(children.isEmpty())</code>. + * Override if neccessary, for example when using the + * {@link gnu.java.beans.encoder.Writer#writeNoChildren}</code> 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 0000000..e73afa8 --- /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 0000000..03c73fd --- /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 0000000..d5b3174 --- /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 0000000..599c4d8 --- /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 <code>null</code> 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 0000000..692227d --- /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 0000000..a44c2ee --- /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 0000000..db08edb --- /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 0000000..7ed935d --- /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 0000000..40c46a5 --- /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 0000000..c368e65 --- /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 d0f9e8c..91fd840 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 339b556..0fcd780 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 05221ff..47d05aa 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 <code>IOException</code> 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 <code>IOException</code> 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 <code>true</code> for stream sockets, <code>false</code> 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. + * * <p> * Note that if the SO_LINGER option is set on this socket, then the * operation could block. + * </p> * - * @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 <code>InputStream</code> for * sockets. It in an internal only class used by <code>PlainSocketImpl</code>. * - * @author Nic Ferrier (nferrier@tapsellferrier.co.uk) + * @author Nic Ferrier <nferrier@tapsellferrier.co.uk> */ 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 * <code>getOutputStream method</code>. It expects only to be used in that * context. * - * @author Nic Ferrier (nferrier@tapsellferrier.co.uk) + * @author Nic Ferrier <nferrier@tapsellferrier.co.uk> */ 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 466f3dd..a557c7d 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 3f25c59..01c2650 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 873e9ec..6fd8b74 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 c1b871f..8097a04 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 b75de5c..4b6bd10 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 0000000..210a3d8 --- /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. + * + * <p>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 <code>entrySet</code> + * has similar phenomenons: The size may spontaneously shrink, or an + * entry, that was in the set before, suddenly disappears. + * + * <p>A weak hash map is not meant for caches; use a normal map, with + * soft references as values instead, or try {@link LinkedHashMap}. + * + * <p>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. + * + * <p>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 <i>null</i>. 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 <code>rehash()</code>. + */ + 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 + * <code>next()</code> call. This is also the entry whose + * bucket should be removed by the <code>remove</code> call. <br> + * + * It is null, if the <code>next</code> method wasn't + * called yet, or if the entry was already removed. <br> + * + * 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 + * <code>next()</code> call. It is <code>null</code> if there + * is no further entry. <br> + * + * 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 <code>next()</code> 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. <br> + * + * 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 + * <code>Math.abs(key.hashCode() % buckets.length)</code>. + * + * 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 <code>Entry</code> 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 <code>entrySet()</code>. + */ + 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. <br> + * + * 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 + * <code>add()</code> 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 16427c7..9ac9b53 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 3165a6f..6291a3c 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/SAXDriver.java b/libjava/classpath/gnu/xml/aelfred2/SAXDriver.java index 6864ff6..e7c4d5a 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 f2abb88..ab2ed16 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 f99e221..026e275 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 29b8dc7..900d03a 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 34509f6..f55b084 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 6f22402..e3f08e4 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 3f29fb1..93f7c6f 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 eb7c1c4..7ac4cc7 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 a850460..aad5ac7 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 <a href='mailto:dog@gnu.org'>Chris Burdess</a> */ 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 389e02b..bdbf928 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). </p> */ 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 6600271..a14fb34 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); - } - } - /** <b>SAX2:</b> 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 0000000..f8287e8 --- /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 <a href='mailto:dog@gnu.org'>Chris Burdess</a> + */ +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 0000000..1d214ce --- /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 <a href='mailto:dog@gnu.org'>Chris Burdess</a> + */ +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 0000000..54c8b36 --- /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: + * <table> + * <tr><th colspan='4'>Features</th></tr> + * <tr><td>http://gnu.org/sax/features/xml-base</td> + * <td colspan='2'>read/write</td> + * <td>Indicates or sets whether XML Base processing is enabled</td></tr> + * <tr><th colspan='4'>Properties</th></tr> + * <tr><td>http://gnu.org/sax/properties/base-uri</td> + * <td>read-only</td><td>String</td> + * <td>Returns the base URI of the current event</td></tr> + * <tr><td>http://gnu.org/sax/properties/document-xml-encoding</td> + * <td>read-only</td><td>String</td> + * <td>Returns the encoding specified in the XML declaration</td></tr> + * </table> + * + * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a> + */ +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 0000000..a2ccea1 --- /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 <a href='mailto:dog@gnu.org'>Chris Burdess</a> + */ +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 0000000..c38516c --- /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 <a href='mailto:dog@gnu.org'>Chris Burdess</a> + */ +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 0000000..e151ac6 --- /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 <a href='mailto:dog@gnu.org'>Chris Burdess</a> + */ +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 c99f564..164774d 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 0000000..6f10b93 --- /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: + * <table> + * <tr><td>gnu.xml.stream.stringInterning</td> + * <td>Boolean</td> + * <td>Indicates whether markup strings will be interned</td></tr> + * <tr><td>gnu.xml.stream.xmlBase</td> + * <td>Boolean</td> + * <td>Indicates whether XML Base processing will be performed</td></tr> + * <tr><td>gnu.xml.stream.baseURI</td> + * <td>String</td> + * <td>Returns the base URI of the current event</td></tr> + * </table> + * + * @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 <a href='mailto:dog@gnu.org'>Chris Burdess</a> + */ +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_COMMENT = "<!--"; + private static final String TEST_PI = "<?"; + private static final String TEST_CDATA = "<![CDATA["; + private static final String TEST_XML_DECL = "<?xml"; + private static final String TEST_DOCTYPE_DECL = "<!DOCTYPE"; + private static final String TEST_ELEMENT_DECL = "<!ELEMENT"; + private static final String TEST_ATTLIST_DECL = "<!ATTLIST"; + private static final String TEST_ENTITY_DECL = "<!ENTITY"; + private static final String TEST_NOTATION_DECL = "<!NOTATION"; + private static final String TEST_KET = ">"; + 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("<![")) + { + // conditional section + expandPE = saved; + if (inputStack.size() < 2) + error("conditional sections illegal in internal subset"); + skipWhitespace(); + if (tryRead("INCLUDE")) + { + skipWhitespace(); + require('['); + skipWhitespace(); + while (!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 d677048..6157296 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 1e0eb1e..bc5bc30 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 c372ea8..4ee0832 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 84cb620..aa15981 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 d8f7f6d..092c56a 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 7daf7ea..ab86401 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 0be2b35..453c22c 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 1df7168..e8e07c6 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 0000000..b3c233c --- /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 <a href='mailto:dog@gnu.org'>Chris Burdess</a> + */ +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 74b1005..fb8b1a6 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 = "<![CDATA[" + node.getNodeValue() + "]]>"; - 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("<![CDATA[" + head + "]]>")); + value = value.substring(bbk + 2); + bbk = value.indexOf("]]>"); + } + // Write final tail value + out.write(encodeText("<![CDATA[" + value + "]]>")); break; case Node.COMMENT_NODE: value = "<!--" + node.getNodeValue() + "-->"; 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/aelfred2/ContentHandler2.java b/libjava/classpath/gnu/xml/transform/StrippingInstruction.java index 1516138..c6391b9 100644 --- a/libjava/classpath/gnu/xml/aelfred2/ContentHandler2.java +++ b/libjava/classpath/gnu/xml/transform/StrippingInstruction.java @@ -1,5 +1,5 @@ -/* ContentHandler2.java -- - Copyright (C) 2004 Free Software Foundation, Inc. +/* StrippingInstruction.java -- + Copyright (C) 2006 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -35,31 +35,39 @@ 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; +package gnu.xml.transform; -import org.xml.sax.ContentHandler; -import org.xml.sax.SAXException; +import gnu.xml.xpath.NameTest; /** - * Extension to the SAX ContentHandler interface to report parsing events - * and parameters required by DOM Level 3 but not supported by SAX. + * An entry in a strip-space or preserve-space list. * * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a> */ -public interface ContentHandler2 - extends ContentHandler +class StrippingInstruction { + final NameTest element; + final int precedence; + + StrippingInstruction(NameTest element, int precedence) + { + this.element = element; + this.precedence = precedence; + } + /** - * 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 + * Returns the <i>default priority</i> of the element name test. + * @see http://www.w3.org/TR/xslt#dt-default-priority */ - void xmlDecl(String version, String encoding, boolean standalone, - String inputEncoding) - throws SAXException; + 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 99431b6..51accaa 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 dde2017..16e3363 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 [<stylesheet> [<input> [<output>]]] + */ + 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 cf40488..b7ff668 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 430598a..24c229e 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 b4b55dc..76fd49ee 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 f8ace9c..84fb6d4 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 dc5e612..ea49741 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 c89ba4d..1710103 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 6d41166..d93b76d 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 e67ec42..3ae46f7 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 8073741..09e92d0 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 5980380..93408e4 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 d65c4fa..e926f6c 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); |