package org.kxml.parser; import java.io.IOException; import java.util.*; import org.kxml.*; import org.kxml.io.*; /** A class for events indicating the start of a new element */ public class StartTag extends ParseEvent { String name; String namespace = Xml.NO_NAMESPACE; Vector attributes; boolean degenerated; StartTag parent; PrefixMap prefixMap; /** creates a new StartTag. The attributes are not copied and may be reused in e.g. the DOM. So DO NOT CHANGE the attribute vector after handing over, the effects are undefined */ public StartTag (StartTag parent, String namespace, String name, Vector attributes, boolean degenerated) { this.parent = parent; this.namespace = namespace == null ? Xml.NO_NAMESPACE : namespace; this.name = name; this.attributes = attributes; this.degenerated = degenerated; prefixMap = parent == null ? new PrefixMap () : parent.prefixMap; } /** creates a new StartTag from the given string (including attributes) without brakets. It is assumed that the opening braket is already consumed. */ public StartTag (StartTag parent, LookAheadReader reader, boolean relaxed) throws IOException { this.parent = parent; prefixMap = parent == null ? new PrefixMap () : parent.prefixMap; name = reader.readTo ("/> \t\n\r"); //System.out.println ("name: ("+name+")"); attributes = new Vector (); while (true) { reader.skipWhitespace (); int c = reader.peek (); if (c == '/') { degenerated = true; reader.read (); reader.skipWhitespace (); if (reader.read () != '>') throw new ParseException ("illegal element termination", null, reader.getLineNumber (), reader.getColumnNumber ()); break; } if (c == '>') { reader.read (); break; } if (c == -1) throw new ParseException ("unexpected eof", null, reader.getLineNumber (), reader.getColumnNumber ()); String attrName = reader.readTo ("=> "); while (attrName.length () > 0 && attrName.charAt (attrName.length () - 1) attrName = attrName.substring (0, attrName.length ()-1); if (reader.read () != '=') throw new ParseException ("Attribute name "+attrName +"must be followed by '=' immediately!", null, reader.getLineNumber (), reader.getColumnNumber ()); reader.skipWhitespace (); int delimiter = reader.read (); if (delimiter != '\'' && delimiter != '"') { if (!relaxed) throw new ParseException (": invalid delimiter: " + (char) delimiter, null, reader.getLineNumber (), reader.getColumnNumber ()); String s = Xml.decode (""+delimiter + reader.readTo ("> ")); attributes.addElement (new Attribute (null, attrName, s)); } else { String s = Xml.decode (reader.readTo ((char) delimiter)); attributes.addElement (new Attribute (null, attrName, s)); reader.read (); // skip endquote } } } /** resolves namespaces from given prefixMap and updates prefixMap with new namespace definitions */ public void fixNamespaces () { boolean any = false; for (int i = attributes.size () - 1; i >= 0; i--) { Attribute attr = (Attribute) attributes.elementAt (i); String name = attr.getName (); int cut = attr.getName ().indexOf (':'); String prefix; if (cut != -1) { prefix = name.substring (0, cut); name = name.substring (cut+1); } else if (name.equals ("xmlns")) { prefix = name; name = ""; } else continue; if (!prefix.equals ("xmlns")) { if (!prefix.equals ("xml")) any = true; } else { prefixMap = new PrefixMap (prefixMap, name, attr.getValue ()); //System.out.println (prefixMap); attributes.removeElementAt (i); } } int len = attributes.size (); if (any) { for (int i = 0; i < len; i++) { Attribute attr = (Attribute) attributes.elementAt (i); String attrName = attr.getName (); int cut = attr.getName ().indexOf (':'); if (cut != -1) { String attrPrefix = attrName.substring (0, cut); if (!attrPrefix.equals ("xml")) { attrName = attrName.substring (cut+1); String attrNs = prefixMap.getNamespace (attrPrefix); if (attrNs == null) throw new RuntimeException ("Undefined Prefix: "+attrPrefix + " in " + this); attributes.setElementAt (new Attribute (attrNs, attrName, attr.getValue ()), i); } } } } int cut = name.indexOf (':'); String prefix; if (cut == -1) prefix = ""; else { prefix = name.substring (0, cut); name = name.substring (cut+1); } namespace = prefixMap.getNamespace (prefix); if (namespace == null && prefix != "") throw new RuntimeException ("undefined prefix: "+prefix+" in "+prefixMap +" at "+this); } /** returns the attribute at the given index position */ public Attribute getAttribute (int index) { return (Attribute) attributes.elementAt (index); } /** returns the local attribute with the given name. convenience method for getAttribute (null, name); */ public Attribute getAttribute (String name) { return getAttribute ("", name); } /** returns the local attribute with the given qualified name. */ public Attribute getAttribute (String namespace, String name) { int len = attributes.size (); if (namespace == null) namespace = ""; for (int i = 0; i < len; i++) { Attribute attr = (Attribute) attributes.elementAt (i); if (namespace.equals (attr.getNamespace ()) && attr.getName ().equals (name)) return attr; } return null; } /** returns the number of attributes */ public int getAttributeCount () { return attributes.size (); } /** returns the attribute vector */ public Vector getAttributes () { return attributes; } /** retrurns if the element is degenerated */ public boolean getDegenerated () { return degenerated; } /** returns the (local) name of the element started */ public String getName () { return name; } /** returns namespace */ public String getNamespace () { return namespace; } public PrefixMap getPrefixMap () { return prefixMap; } /** Returns the start tag of the parent element. */ public StartTag getParent () { return parent; } /** returns the integer constant assigned to this event type (Xml.START_TAG). */ public int getType () { return Xml.START_TAG; } /** Returns the value of the attribute with the given name. Throws a RuntimeException if the given attribute does not exist. Please use getValueDefault (attrname, null) in order to get a null value if the attribute is not existing. */ public String getValue (String attrName) { Attribute attr = getAttribute (attrName); if (attr == null) throw new RuntimeException ("Attribute "+ attrName + " in " + this + " expected!"); return attr.getValue (); } /** Returns the given attribute value, or the given default value if the attribute is not existing. */ public String getValueDefault (String attrName, String deflt) { Attribute attr = getAttribute (attrName); return attr == null ? deflt : attr.getValue (); } public boolean endCheck (StartTag start) { throw new RuntimeException ("unexpected element: "+this); } /** Simplified (!) toString method for debugging purposes only. In order to actually write valid XML, please use a XmlWriter. */ public String toString () { StringBuffer buf = new StringBuffer (" buf.append (name); for (int i = 0; i < attributes.size (); i++) { Attribute attr = getAttribute (i); buf.append (" "); buf.append (attr.getName ()); buf.append ("=\""); buf.append (attr.getValue ()); buf.append ("\""); } buf.append (">"); if (lineNumber != -1) buf.append (""); return buf.toString (); } public void setPrefixMap (PrefixMap map) { this.prefixMap = map; } }