/* * Copyright 1990-2009 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 only, as published by the Free Software Foundation. * * 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. See the GNU * General Public License version 2 for more details (a copy is * included at /legal/license.txt). * * You should have received a copy of the GNU General Public License * version 2 along with this work; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa * Clara, CA 95054 or visit www.sun.com if you need additional * information or have any questions. */ package com.sun.ukit.xml; import java.io.Writer; import javax.xml.stream.XMLOutputFactory; import javax.xml.stream.XMLStreamException; import javax.xml.stream.XMLStreamWriter; /** * XML stream writer which supports namespace repairing. * * @see XMLOutputFactory * @see XMLStreamWriter */ /* pkg */ final class WriterRepStAX extends WriterStAX { private int mAuto = 'a' - 1; // automatic prefix /** * Create a new instance of XMLStreamWriter implementation. * * @param writer the output writer */ /* pkg */ WriterRepStAX(Writer writer) { super(writer); } /** * Creates a new element. * * @param isEmpty If <code>true</code> the element is empty. * @param prefix the prefix of the tag * @param localName local name of the tag * @param namespaceURI the uri to bind the tag to */ /* pkg */ void newElm(boolean isEmpty, String prefix, String namespaceURI, String localName) throws XMLStreamException { if (mIsElm != false) writeElm(); if (mPh > PH_DOCELM) throw new IllegalStateException(INVALID_XML); mPh = PH_DOCELM; mIsElm = true; mElm = pair(mElm); mElm.id = (isEmpty)? ELM_EMPTY: 0; String pref = findName(mPref, namespaceURI); if (pref == null) { // Add new mapping for app defined prefix pref = verifyPref(prefix); writeNamespace(pref, namespaceURI); } // Use the prefix already defined for the NS even if prefix // is different form one provided by app. (replace the prefix) mElm.name = ((pref.length() != 0)? pref + ':': "") + localName; mElm.value = namespaceURI; } /** * Creates a new element. * * @param isEmpty If <code>true</code> the element is empty. * @param localName local name of the tag * @param namespaceURI the uri to bind the tag to */ /* pkg */ void newElm(boolean isEmpty, String namespaceURI, String localName) throws XMLStreamException { if (mIsElm != false) writeElm(); if (mPh > PH_DOCELM) throw new IllegalStateException(INVALID_XML); mPh = PH_DOCELM; mIsElm = true; mElm = pair(mElm); mElm.id = (isEmpty)? ELM_EMPTY: 0; String pref = findName(mPref, namespaceURI); if (pref == null) { // Prefix from hints (bindings) pref = findName(mHint, namespaceURI); if (pref == null) { // Auto prefix pref = getAuto(); } writeNamespace(pref, namespaceURI); } mElm.name = ((pref.length() != 0)? pref + ':': "") + localName; mElm.value = namespaceURI; } /** * Creates a new element. * * @param isEmpty If <code>true</code> the element is empty. * @param localName local name of the tag */ /* pkg */ void newElm(boolean isEmpty, String localName) throws XMLStreamException { if (mIsElm != false) writeElm(); if (mPh > PH_DOCELM) throw new IllegalStateException(INVALID_XML); mPh = PH_DOCELM; mIsElm = true; mElm = pair(mElm); mElm.id = (isEmpty)? ELM_EMPTY: 0; dropPref(mElm, localName); } /** * Writes an attribute to the output stream without a prefix. * * @param localName the local name of the attribute, may not be null * @param value the value of the attribute * @throws IllegalStateException if the current state does not allow * Attribute writing * @throws XMLStreamException * @throws NullPointerException */ public void writeAttribute(String localName, String value) throws XMLStreamException { if (localName == null) throw new NullPointerException(""); if (mIsElm == false) throw new IllegalStateException(INVALID_XML); mElm.list = pair(mElm.list); dropPref(mElm.list, localName); mElm.list.value = escValue(value); } /** * Writes an attribute to the output stream. * * @param prefix the prefix for this attribute, may not be null * @param namespaceURI the uri of the prefix for this attribute, may not be * null * @param localName the local name of the attribute, may not be null * @param value the value of the attribute * @throws IllegalStateException if the current state does not allow * Attribute writing * @throws XMLStreamException if the namespace URI has not been bound to a * prefix and javax.xml.stream.isPrefixDefaulting has not been set to * <code>true</code> * @throws NullPointerException */ public void writeAttribute( String prefix, String namespaceURI, String localName, String value) throws XMLStreamException { if (prefix == null || localName == null || namespaceURI == null) throw new NullPointerException(""); if (mIsElm == false) throw new IllegalStateException(INVALID_XML); String lname = localName.trim(); if (lname.length() == 0) throw new IllegalStateException(INVALID_XML); String pref = findName(mPref, namespaceURI); if (pref == null) { // Add new mapping for app defined prefix pref = verifyPref(prefix); writeNamespace(pref, namespaceURI); } // Use the prefix already defined for the NS even if prefix // is different form one provided by app. (replace the prefix) mElm.list = pair(mElm.list); mElm.list.name = ((pref.length() != 0) ? pref + ':' : "") + lname; mElm.list.value = escValue(value); } /** * Writes an attribute to the output stream. * * @param namespaceURI the uri of the prefix for this attribute, may not be * null * @param localName the local name of the attribute, may not be null * @param value the value of the attribute * @throws IllegalStateException if the current state does not allow * Attribute writing * @throws XMLStreamException if the namespace URI has not been bound to a * prefix and javax.xml.stream.isPrefixDefaulting has not been set to * <code>true</code> * @throws NullPointerException */ public void writeAttribute( String namespaceURI, String localName, String value) throws XMLStreamException { if (localName == null || namespaceURI == null) throw new NullPointerException(""); if (mIsElm == false) throw new IllegalStateException(INVALID_XML); String lname = localName.trim(); if (lname.length() == 0) throw new IllegalStateException(INVALID_XML); String pref = findName(mPref, namespaceURI); if (pref == null) { // Prefix from hints (bindings) pref = findName(mHint, namespaceURI); if (pref == null) { // Auto prefix pref = getAuto(); } writeNamespace(pref, namespaceURI); } mElm.list = pair(mElm.list); mElm.list.name = ((pref.length() != 0) ? pref + ':' : "") + lname; mElm.list.value = escValue(value); } /** * Writes a namespace to the output stream. * If the prefix argument to this method is the empty string, * "xmlns", or null this method will delegate to writeDefaultNamespace * * @param prefix the prefix to bind this namespace to * @param namespaceURI the uri to bind the prefix to, may not be null * @throws IllegalStateException if the current state does not allow * Namespace writing * @throws XMLStreamException * @throws NullPointerException */ public void writeNamespace(String prefix, String namespaceURI) throws XMLStreamException { String pref = findName(mPref, namespaceURI); if (pref == null) super.writeNamespace(verifyPref(prefix), namespaceURI); } /** * Writes the default namespace to the stream. * * @param namespaceURI the uri to bind the default namespace to, may not be * null * @throws IllegalStateException if the current state does not allow * Namespace writing * @throws XMLStreamException * @throws NullPointerException */ public void writeDefaultNamespace(String namespaceURI) throws XMLStreamException { for (Pair pref = mPref; pref != null; pref = pref.next) { if (pref.name.length() != 0) // search for first default NS continue; if (pref.list == mElm) { // is default NS on current element? // is provided namespaceURI equal to default NS? if (pref.list.value.equals(namespaceURI)) return; throw new IllegalStateException(INVALID_XML); } super.writeDefaultNamespace(namespaceURI); return; } super.writeDefaultNamespace(namespaceURI); } /** * Get the value of a feature/property from the underlying implementation * * @param name The name of the property, may not be null * @return The value of the property * @throws IllegalArgumentException if the property is not supported * @throws NullPointerException if the name is null */ public Object getProperty(String name) throws IllegalArgumentException { if (name.equals(XMLOutputFactory.IS_REPAIRING_NAMESPACES) == false) throw new IllegalArgumentException(name); return Boolean.TRUE; } /** * Drops prefix if it is not in current stack. * * @param pair element or attribute descriptor. * @param name qualified name. */ /* pkg */ void dropPref(Pair pair, String name) throws XMLStreamException { int off = name.indexOf(':'); if (off < 0) { // Name without prefix pair.name = name.trim(); if (pair.name.length() == 0) // qname is empty string throw new XMLStreamException(INVALID_XML); return; } // Name with prefix String pref = name.substring(0, off).trim(); pair.value = findValue(mPref, pref); if (pair.value != null) { // Keep the prefix (it is valid) pair.name = pref + ':' + name.substring(off + 1).trim(); if (pair.name.length() <= pref.length() + 1) // no local name throw new XMLStreamException(INVALID_XML); } else { // Remove the prefix (cannot find URI) pair.name = name.substring(off + 1).trim(); if (pair.name.length() == 0) // no local name throw new XMLStreamException(INVALID_XML); } } /** * Returns unique prefix if provided prefix is on current element. * Otherwise the same prefix is returned. * * @param prefix prefix to check. * @return unique prefix. */ /* pkg */ String verifyPref(String prefix) { String pref = prefix.trim(); for (Pair elm = mPref; elm != null && elm.list == mElm; elm = elm.next) { if (pref.equals(elm.name)) return getAuto(); } return pref; } /** * Creates a new prefix. Generates strings from "a" to "z" and then "zNNN", * where NNN is a number starting with 1 so the first string is "z1". * * @return unique prefix which shadows no prefix in current stack. */ /* pkg */ String getAuto() { String auto; if (mAuto < 'z') { mAuto++; auto = String.valueOf((char)mAuto); } else { mAuto += 0x100; auto = "z" + String.valueOf(mAuto >> 8); } return (findValue(mPref, auto) == null)? auto: getAuto(); } }