aoqi@0: /* aoqi@0: * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. aoqi@0: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. aoqi@0: * aoqi@0: * This code is free software; you can redistribute it and/or modify it aoqi@0: * under the terms of the GNU General Public License version 2 only, as aoqi@0: * published by the Free Software Foundation. Oracle designates this aoqi@0: * particular file as subject to the "Classpath" exception as provided aoqi@0: * by Oracle in the LICENSE file that accompanied this code. aoqi@0: * aoqi@0: * This code is distributed in the hope that it will be useful, but WITHOUT aoqi@0: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or aoqi@0: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License aoqi@0: * version 2 for more details (a copy is included in the LICENSE file that aoqi@0: * accompanied this code). aoqi@0: * aoqi@0: * You should have received a copy of the GNU General Public License version aoqi@0: * 2 along with this work; if not, write to the Free Software Foundation, aoqi@0: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. aoqi@0: * aoqi@0: * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA aoqi@0: * or visit www.oracle.com if you need additional information or have any aoqi@0: * questions. aoqi@0: */ aoqi@0: aoqi@0: package com.sun.xml.internal.txw2; aoqi@0: aoqi@0: import javax.xml.namespace.QName; aoqi@0: aoqi@0: aoqi@0: /** aoqi@0: * Start tag. aoqi@0: * aoqi@0: *

aoqi@0: * This object implements {@link NamespaceResolver} for attribute values. aoqi@0: * aoqi@0: * @author Kohsuke Kawaguchi aoqi@0: */ aoqi@0: class StartTag extends Content implements NamespaceResolver { aoqi@0: /** aoqi@0: * Tag name of the element. aoqi@0: * aoqi@0: *

aoqi@0: * This field is also used as a flag to indicate aoqi@0: * whether the start tag has been written. aoqi@0: * This field is initially set to non-null, and aoqi@0: * then reset to null when it's written. aoqi@0: */ aoqi@0: private String uri; aoqi@0: // but we keep the local name non-null so that aoqi@0: // we can diagnose an error aoqi@0: private final String localName; aoqi@0: aoqi@0: private Attribute firstAtt; aoqi@0: private Attribute lastAtt; aoqi@0: aoqi@0: /** aoqi@0: * If this {@link StartTag} has the parent {@link ContainerElement}, aoqi@0: * that value. Otherwise null. aoqi@0: */ aoqi@0: private ContainerElement owner; aoqi@0: aoqi@0: /** aoqi@0: * Explicitly given namespace declarations on this element. aoqi@0: * aoqi@0: *

aoqi@0: * Additional namespace declarations might be necessary to aoqi@0: * generate child {@link QName}s and attributes. aoqi@0: */ aoqi@0: private NamespaceDecl firstNs; aoqi@0: private NamespaceDecl lastNs; aoqi@0: aoqi@0: final Document document; aoqi@0: aoqi@0: public StartTag(ContainerElement owner, String uri, String localName) { aoqi@0: this(owner.document,uri,localName); aoqi@0: this.owner = owner; aoqi@0: } aoqi@0: aoqi@0: public StartTag(Document document, String uri, String localName) { aoqi@0: assert uri!=null; aoqi@0: assert localName!=null; aoqi@0: aoqi@0: this.uri = uri; aoqi@0: this.localName = localName; aoqi@0: this.document = document; aoqi@0: aoqi@0: // TODO: think about a better way to maintain namespace decls. aoqi@0: // this requires at least one NamespaceDecl per start tag, aoqi@0: // which is rather expensive. aoqi@0: addNamespaceDecl(uri,null,false); aoqi@0: } aoqi@0: aoqi@0: public void addAttribute(String nsUri, String localName, Object arg) { aoqi@0: checkWritable(); aoqi@0: aoqi@0: // look for the existing ones aoqi@0: Attribute a; aoqi@0: for(a=firstAtt; a!=null; a=a.next) { aoqi@0: if(a.hasName(nsUri,localName)) { aoqi@0: break; aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: // if not found, declare a new one aoqi@0: if(a==null) { aoqi@0: a = new Attribute(nsUri,localName); aoqi@0: if(lastAtt==null) { aoqi@0: assert firstAtt==null; aoqi@0: firstAtt = lastAtt = a; aoqi@0: } else { aoqi@0: assert firstAtt!=null; aoqi@0: lastAtt.next = a; aoqi@0: lastAtt = a; aoqi@0: } aoqi@0: if(nsUri.length()>0) aoqi@0: addNamespaceDecl(nsUri,null,true); aoqi@0: } aoqi@0: aoqi@0: document.writeValue(arg,this,a.value); aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Declares a new namespace URI on this tag. aoqi@0: * aoqi@0: * @param uri aoqi@0: * namespace URI to be bound. Can be empty, but must not be null. aoqi@0: * @param prefix aoqi@0: * If non-null and non-empty, this prefix is bound to the URI aoqi@0: * on this element. If empty, then the runtime will still try to aoqi@0: * use the URI as the default namespace, but it may fail to do so aoqi@0: * because of the constraints in the XML. aoqi@0: *

aoqi@0: * If this parameter is null, the runtime will allocate an unique prefix. aoqi@0: * @param requirePrefix aoqi@0: * Used only when the prefix parameter is null. If true, this indicates aoqi@0: * that the non-empty prefix must be assigned to this URI. If false, aoqi@0: * then this URI might be used as the default namespace. aoqi@0: *

aoqi@0: * Normally you just need to set it to false. aoqi@0: */ aoqi@0: public NamespaceDecl addNamespaceDecl(String uri, String prefix,boolean requirePrefix) { aoqi@0: checkWritable(); aoqi@0: aoqi@0: if(uri==null) aoqi@0: throw new IllegalArgumentException(); aoqi@0: if(uri.length()==0) { aoqi@0: if(requirePrefix) aoqi@0: throw new IllegalArgumentException("The empty namespace cannot have a non-empty prefix"); aoqi@0: if(prefix!=null && prefix.length()>0) aoqi@0: throw new IllegalArgumentException("The empty namespace can be only bound to the empty prefix"); aoqi@0: prefix = ""; aoqi@0: } aoqi@0: aoqi@0: // check for the duplicate aoqi@0: for(NamespaceDecl n=firstNs; n!=null; n=n.next) { aoqi@0: if(uri.equals(n.uri)) { aoqi@0: if(prefix==null) { aoqi@0: // reuse this binding aoqi@0: n.requirePrefix |= requirePrefix; aoqi@0: return n; aoqi@0: } aoqi@0: if(n.prefix==null) { aoqi@0: // reuse this binding aoqi@0: n.prefix = prefix; aoqi@0: n.requirePrefix |= requirePrefix; aoqi@0: return n; aoqi@0: } aoqi@0: if(prefix.equals(n.prefix)) { aoqi@0: // reuse this binding aoqi@0: n.requirePrefix |= requirePrefix; aoqi@0: return n; aoqi@0: } aoqi@0: } aoqi@0: if(prefix!=null && n.prefix!=null && n.prefix.equals(prefix)) aoqi@0: throw new IllegalArgumentException( aoqi@0: "Prefix '"+prefix+"' is already bound to '"+n.uri+'\''); aoqi@0: } aoqi@0: aoqi@0: NamespaceDecl ns = new NamespaceDecl(document.assignNewId(),uri,prefix,requirePrefix); aoqi@0: if(lastNs==null) { aoqi@0: assert firstNs==null; aoqi@0: firstNs = lastNs = ns; aoqi@0: } else { aoqi@0: assert firstNs!=null; aoqi@0: lastNs.next = ns; aoqi@0: lastNs = ns; aoqi@0: } aoqi@0: return ns; aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Throws an error if the start tag has already been committed. aoqi@0: */ aoqi@0: private void checkWritable() { aoqi@0: if(isWritten()) aoqi@0: throw new IllegalStateException( aoqi@0: "The start tag of "+this.localName+" has already been written. " + aoqi@0: "If you need out of order writing, see the TypedXmlWriter.block method"); aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Returns true if this start tag has already been written. aoqi@0: */ aoqi@0: boolean isWritten() { aoqi@0: return uri==null; aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * A {@link StartTag} can be only written after aoqi@0: * we are sure that all the necessary namespace declarations are given. aoqi@0: */ aoqi@0: boolean isReadyToCommit() { aoqi@0: if(owner!=null && owner.isBlocked()) aoqi@0: return false; aoqi@0: aoqi@0: for( Content c=getNext(); c!=null; c=c.getNext() ) aoqi@0: if(c.concludesPendingStartTag()) aoqi@0: return true; aoqi@0: aoqi@0: return false; aoqi@0: } aoqi@0: aoqi@0: public void written() { aoqi@0: firstAtt = lastAtt = null; aoqi@0: uri = null; aoqi@0: if(owner!=null) { aoqi@0: assert owner.startTag==this; aoqi@0: owner.startTag = null; aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: boolean concludesPendingStartTag() { aoqi@0: return true; aoqi@0: } aoqi@0: aoqi@0: void accept(ContentVisitor visitor) { aoqi@0: visitor.onStartTag(uri,localName,firstAtt,firstNs); aoqi@0: } aoqi@0: aoqi@0: public String getPrefix(String nsUri) { aoqi@0: NamespaceDecl ns = addNamespaceDecl(nsUri,null,false); aoqi@0: if(ns.prefix!=null) aoqi@0: // if the prefix has already been declared, use it. aoqi@0: return ns.prefix; aoqi@0: return ns.dummyPrefix; aoqi@0: } aoqi@0: }