Thu, 12 Oct 2017 19:44:07 +0800
merge
1 /*
2 * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
26 package com.sun.xml.internal.txw2;
28 import javax.xml.namespace.QName;
31 /**
32 * Start tag.
33 *
34 * <p>
35 * This object implements {@link NamespaceResolver} for attribute values.
36 *
37 * @author Kohsuke Kawaguchi
38 */
39 class StartTag extends Content implements NamespaceResolver {
40 /**
41 * Tag name of the element.
42 *
43 * <p>
44 * This field is also used as a flag to indicate
45 * whether the start tag has been written.
46 * This field is initially set to non-null, and
47 * then reset to null when it's written.
48 */
49 private String uri;
50 // but we keep the local name non-null so that
51 // we can diagnose an error
52 private final String localName;
54 private Attribute firstAtt;
55 private Attribute lastAtt;
57 /**
58 * If this {@link StartTag} has the parent {@link ContainerElement},
59 * that value. Otherwise null.
60 */
61 private ContainerElement owner;
63 /**
64 * Explicitly given namespace declarations on this element.
65 *
66 * <p>
67 * Additional namespace declarations might be necessary to
68 * generate child {@link QName}s and attributes.
69 */
70 private NamespaceDecl firstNs;
71 private NamespaceDecl lastNs;
73 final Document document;
75 public StartTag(ContainerElement owner, String uri, String localName) {
76 this(owner.document,uri,localName);
77 this.owner = owner;
78 }
80 public StartTag(Document document, String uri, String localName) {
81 assert uri!=null;
82 assert localName!=null;
84 this.uri = uri;
85 this.localName = localName;
86 this.document = document;
88 // TODO: think about a better way to maintain namespace decls.
89 // this requires at least one NamespaceDecl per start tag,
90 // which is rather expensive.
91 addNamespaceDecl(uri,null,false);
92 }
94 public void addAttribute(String nsUri, String localName, Object arg) {
95 checkWritable();
97 // look for the existing ones
98 Attribute a;
99 for(a=firstAtt; a!=null; a=a.next) {
100 if(a.hasName(nsUri,localName)) {
101 break;
102 }
103 }
105 // if not found, declare a new one
106 if(a==null) {
107 a = new Attribute(nsUri,localName);
108 if(lastAtt==null) {
109 assert firstAtt==null;
110 firstAtt = lastAtt = a;
111 } else {
112 assert firstAtt!=null;
113 lastAtt.next = a;
114 lastAtt = a;
115 }
116 if(nsUri.length()>0)
117 addNamespaceDecl(nsUri,null,true);
118 }
120 document.writeValue(arg,this,a.value);
121 }
123 /**
124 * Declares a new namespace URI on this tag.
125 *
126 * @param uri
127 * namespace URI to be bound. Can be empty, but must not be null.
128 * @param prefix
129 * If non-null and non-empty, this prefix is bound to the URI
130 * on this element. If empty, then the runtime will still try to
131 * use the URI as the default namespace, but it may fail to do so
132 * because of the constraints in the XML.
133 * <p>
134 * If this parameter is null, the runtime will allocate an unique prefix.
135 * @param requirePrefix
136 * Used only when the prefix parameter is null. If true, this indicates
137 * that the non-empty prefix must be assigned to this URI. If false,
138 * then this URI might be used as the default namespace.
139 * <p>
140 * Normally you just need to set it to false.
141 */
142 public NamespaceDecl addNamespaceDecl(String uri, String prefix,boolean requirePrefix) {
143 checkWritable();
145 if(uri==null)
146 throw new IllegalArgumentException();
147 if(uri.length()==0) {
148 if(requirePrefix)
149 throw new IllegalArgumentException("The empty namespace cannot have a non-empty prefix");
150 if(prefix!=null && prefix.length()>0)
151 throw new IllegalArgumentException("The empty namespace can be only bound to the empty prefix");
152 prefix = "";
153 }
155 // check for the duplicate
156 for(NamespaceDecl n=firstNs; n!=null; n=n.next) {
157 if(uri.equals(n.uri)) {
158 if(prefix==null) {
159 // reuse this binding
160 n.requirePrefix |= requirePrefix;
161 return n;
162 }
163 if(n.prefix==null) {
164 // reuse this binding
165 n.prefix = prefix;
166 n.requirePrefix |= requirePrefix;
167 return n;
168 }
169 if(prefix.equals(n.prefix)) {
170 // reuse this binding
171 n.requirePrefix |= requirePrefix;
172 return n;
173 }
174 }
175 if(prefix!=null && n.prefix!=null && n.prefix.equals(prefix))
176 throw new IllegalArgumentException(
177 "Prefix '"+prefix+"' is already bound to '"+n.uri+'\'');
178 }
180 NamespaceDecl ns = new NamespaceDecl(document.assignNewId(),uri,prefix,requirePrefix);
181 if(lastNs==null) {
182 assert firstNs==null;
183 firstNs = lastNs = ns;
184 } else {
185 assert firstNs!=null;
186 lastNs.next = ns;
187 lastNs = ns;
188 }
189 return ns;
190 }
192 /**
193 * Throws an error if the start tag has already been committed.
194 */
195 private void checkWritable() {
196 if(isWritten())
197 throw new IllegalStateException(
198 "The start tag of "+this.localName+" has already been written. " +
199 "If you need out of order writing, see the TypedXmlWriter.block method");
200 }
202 /**
203 * Returns true if this start tag has already been written.
204 */
205 boolean isWritten() {
206 return uri==null;
207 }
209 /**
210 * A {@link StartTag} can be only written after
211 * we are sure that all the necessary namespace declarations are given.
212 */
213 boolean isReadyToCommit() {
214 if(owner!=null && owner.isBlocked())
215 return false;
217 for( Content c=getNext(); c!=null; c=c.getNext() )
218 if(c.concludesPendingStartTag())
219 return true;
221 return false;
222 }
224 public void written() {
225 firstAtt = lastAtt = null;
226 uri = null;
227 if(owner!=null) {
228 assert owner.startTag==this;
229 owner.startTag = null;
230 }
231 }
233 boolean concludesPendingStartTag() {
234 return true;
235 }
237 void accept(ContentVisitor visitor) {
238 visitor.onStartTag(uri,localName,firstAtt,firstNs);
239 }
241 public String getPrefix(String nsUri) {
242 NamespaceDecl ns = addNamespaceDecl(nsUri,null,false);
243 if(ns.prefix!=null)
244 // if the prefix has already been declared, use it.
245 return ns.prefix;
246 return ns.dummyPrefix;
247 }
248 }