1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/src/share/jaxws_classes/com/sun/xml/internal/txw2/ContainerElement.java Wed Apr 27 01:27:09 2016 +0800 1.3 @@ -0,0 +1,376 @@ 1.4 +/* 1.5 + * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. 1.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 1.7 + * 1.8 + * This code is free software; you can redistribute it and/or modify it 1.9 + * under the terms of the GNU General Public License version 2 only, as 1.10 + * published by the Free Software Foundation. Oracle designates this 1.11 + * particular file as subject to the "Classpath" exception as provided 1.12 + * by Oracle in the LICENSE file that accompanied this code. 1.13 + * 1.14 + * This code is distributed in the hope that it will be useful, but WITHOUT 1.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 1.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 1.17 + * version 2 for more details (a copy is included in the LICENSE file that 1.18 + * accompanied this code). 1.19 + * 1.20 + * You should have received a copy of the GNU General Public License version 1.21 + * 2 along with this work; if not, write to the Free Software Foundation, 1.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 1.23 + * 1.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 1.25 + * or visit www.oracle.com if you need additional information or have any 1.26 + * questions. 1.27 + */ 1.28 + 1.29 +package com.sun.xml.internal.txw2; 1.30 + 1.31 +import com.sun.xml.internal.txw2.annotation.XmlAttribute; 1.32 +import com.sun.xml.internal.txw2.annotation.XmlElement; 1.33 +import com.sun.xml.internal.txw2.annotation.XmlNamespace; 1.34 +import com.sun.xml.internal.txw2.annotation.XmlValue; 1.35 +import com.sun.xml.internal.txw2.annotation.XmlCDATA; 1.36 + 1.37 +import javax.xml.namespace.QName; 1.38 +import java.lang.reflect.InvocationHandler; 1.39 +import java.lang.reflect.InvocationTargetException; 1.40 +import java.lang.reflect.Method; 1.41 +import java.lang.reflect.Proxy; 1.42 + 1.43 +/** 1.44 + * Dynamically implements {@link TypedXmlWriter} interfaces. 1.45 + * 1.46 + * @author Kohsuke Kawaguchi 1.47 + */ 1.48 +final class ContainerElement implements InvocationHandler, TypedXmlWriter { 1.49 + 1.50 + final Document document; 1.51 + 1.52 + /** 1.53 + * Initially, point to the start tag token, but 1.54 + * once we know we are done with the start tag, we will reset it to null 1.55 + * so that the token sequence can be GC-ed. 1.56 + */ 1.57 + StartTag startTag; 1.58 + final EndTag endTag = new EndTag(); 1.59 + 1.60 + /** 1.61 + * Namespace URI of this element. 1.62 + */ 1.63 + private final String nsUri; 1.64 + 1.65 + /** 1.66 + * When this element can accept more child content, this value 1.67 + * is non-null and holds the last child {@link Content}. 1.68 + * 1.69 + * If this element is committed, this parameter is null. 1.70 + */ 1.71 + private Content tail; 1.72 + 1.73 + /** 1.74 + * Uncommitted {@link ContainerElement}s form a doubly-linked list, 1.75 + * so that the parent can close them recursively. 1.76 + */ 1.77 + private ContainerElement prevOpen; 1.78 + private ContainerElement nextOpen; 1.79 + private final ContainerElement parent; 1.80 + private ContainerElement lastOpenChild; 1.81 + 1.82 + /** 1.83 + * Set to true if the start eleent is blocked. 1.84 + */ 1.85 + private boolean blocked; 1.86 + 1.87 + public ContainerElement(Document document,ContainerElement parent,String nsUri, String localName) { 1.88 + this.parent = parent; 1.89 + this.document = document; 1.90 + this.nsUri = nsUri; 1.91 + this.startTag = new StartTag(this,nsUri,localName); 1.92 + tail = startTag; 1.93 + 1.94 + if(isRoot()) 1.95 + document.setFirstContent(startTag); 1.96 + } 1.97 + 1.98 + private boolean isRoot() { 1.99 + return parent==null; 1.100 + } 1.101 + 1.102 + private boolean isCommitted() { 1.103 + return tail==null; 1.104 + } 1.105 + 1.106 + public Document getDocument() { 1.107 + return document; 1.108 + } 1.109 + 1.110 + boolean isBlocked() { 1.111 + return blocked && !isCommitted(); 1.112 + } 1.113 + 1.114 + public void block() { 1.115 + blocked = true; 1.116 + } 1.117 + 1.118 + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 1.119 + if(method.getDeclaringClass()==TypedXmlWriter.class || method.getDeclaringClass()==Object.class) { 1.120 + // forward to myself 1.121 + try { 1.122 + return method.invoke(this,args); 1.123 + } catch (InvocationTargetException e) { 1.124 + throw e.getTargetException(); 1.125 + } 1.126 + } 1.127 + 1.128 + XmlAttribute xa = method.getAnnotation(XmlAttribute.class); 1.129 + XmlValue xv = method.getAnnotation(XmlValue.class); 1.130 + XmlElement xe = method.getAnnotation(XmlElement.class); 1.131 + 1.132 + 1.133 + if(xa!=null) { 1.134 + if(xv!=null || xe!=null) 1.135 + throw new IllegalAnnotationException(method.toString()); 1.136 + 1.137 + addAttribute(xa,method,args); 1.138 + return proxy; // allow method chaining 1.139 + } 1.140 + if(xv!=null) { 1.141 + if(xe!=null) 1.142 + throw new IllegalAnnotationException(method.toString()); 1.143 + 1.144 + _pcdata(args); 1.145 + return proxy; // allow method chaining 1.146 + } 1.147 + 1.148 + return addElement(xe,method,args); 1.149 + } 1.150 + 1.151 + /** 1.152 + * Writes an attribute. 1.153 + */ 1.154 + private void addAttribute(XmlAttribute xa, Method method, Object[] args) { 1.155 + assert xa!=null; 1.156 + 1.157 + checkStartTag(); 1.158 + 1.159 + String localName = xa.value(); 1.160 + if(xa.value().length()==0) 1.161 + localName = method.getName(); 1.162 + 1.163 + _attribute(xa.ns(),localName,args); 1.164 + } 1.165 + 1.166 + private void checkStartTag() { 1.167 + if(startTag==null) 1.168 + throw new IllegalStateException("start tag has already been written"); 1.169 + } 1.170 + 1.171 + /** 1.172 + * Writes a new element. 1.173 + */ 1.174 + private Object addElement(XmlElement e, Method method, Object[] args) { 1.175 + Class<?> rt = method.getReturnType(); 1.176 + 1.177 + // the last precedence: default name 1.178 + String nsUri = "##default"; 1.179 + String localName = method.getName(); 1.180 + 1.181 + if(e!=null) { 1.182 + // then the annotation on this method 1.183 + if(e.value().length()!=0) 1.184 + localName = e.value(); 1.185 + nsUri = e.ns(); 1.186 + } 1.187 + 1.188 + if(nsUri.equals("##default")) { 1.189 + // look for the annotation on the declaring class 1.190 + Class<?> c = method.getDeclaringClass(); 1.191 + XmlElement ce = c.getAnnotation(XmlElement.class); 1.192 + if(ce!=null) { 1.193 + nsUri = ce.ns(); 1.194 + } 1.195 + 1.196 + if(nsUri.equals("##default")) 1.197 + // then default to the XmlNamespace 1.198 + nsUri = getNamespace(c.getPackage()); 1.199 + } 1.200 + 1.201 + 1.202 + 1.203 + if(rt==Void.TYPE) { 1.204 + // leaf element with just a value 1.205 + 1.206 + boolean isCDATA = method.getAnnotation(XmlCDATA.class)!=null; 1.207 + 1.208 + StartTag st = new StartTag(document,nsUri,localName); 1.209 + addChild(st); 1.210 + for( Object arg : args ) { 1.211 + Text text; 1.212 + if(isCDATA) text = new Cdata(document,st,arg); 1.213 + else text = new Pcdata(document,st,arg); 1.214 + addChild(text); 1.215 + } 1.216 + addChild(new EndTag()); 1.217 + return null; 1.218 + } 1.219 + if(TypedXmlWriter.class.isAssignableFrom(rt)) { 1.220 + // sub writer 1.221 + return _element(nsUri,localName,(Class)rt); 1.222 + } 1.223 + 1.224 + throw new IllegalSignatureException("Illegal return type: "+rt); 1.225 + } 1.226 + 1.227 + /** 1.228 + * Decides the namespace URI of the given package. 1.229 + */ 1.230 + private String getNamespace(Package pkg) { 1.231 + if(pkg==null) return ""; 1.232 + 1.233 + String nsUri; 1.234 + XmlNamespace ns = pkg.getAnnotation(XmlNamespace.class); 1.235 + if(ns!=null) 1.236 + nsUri = ns.value(); 1.237 + else 1.238 + nsUri = ""; 1.239 + return nsUri; 1.240 + } 1.241 + 1.242 + /** 1.243 + * Appends this child object to the tail. 1.244 + */ 1.245 + private void addChild(Content child) { 1.246 + tail.setNext(document,child); 1.247 + tail = child; 1.248 + } 1.249 + 1.250 + public void commit() { 1.251 + commit(true); 1.252 + } 1.253 + 1.254 + public void commit(boolean includingAllPredecessors) { 1.255 + _commit(includingAllPredecessors); 1.256 + document.flush(); 1.257 + } 1.258 + 1.259 + private void _commit(boolean includingAllPredecessors) { 1.260 + if(isCommitted()) return; 1.261 + 1.262 + addChild(endTag); 1.263 + if(isRoot()) 1.264 + addChild(new EndDocument()); 1.265 + tail = null; 1.266 + 1.267 + // _commit predecessors if so told 1.268 + if(includingAllPredecessors) { 1.269 + for( ContainerElement e=this; e!=null; e=e.parent ) { 1.270 + while(e.prevOpen!=null) { 1.271 + e.prevOpen._commit(false); 1.272 + // e.prevOpen should change as a result of committing it. 1.273 + } 1.274 + } 1.275 + } 1.276 + 1.277 + // _commit all children recursively 1.278 + while(lastOpenChild!=null) 1.279 + lastOpenChild._commit(false); 1.280 + 1.281 + // remove this node from the link 1.282 + if(parent!=null) { 1.283 + if(parent.lastOpenChild==this) { 1.284 + assert nextOpen==null : "this must be the last one"; 1.285 + parent.lastOpenChild = prevOpen; 1.286 + } else { 1.287 + assert nextOpen.prevOpen==this; 1.288 + nextOpen.prevOpen = this.prevOpen; 1.289 + } 1.290 + if(prevOpen!=null) { 1.291 + assert prevOpen.nextOpen==this; 1.292 + prevOpen.nextOpen = this.nextOpen; 1.293 + } 1.294 + } 1.295 + 1.296 + this.nextOpen = null; 1.297 + this.prevOpen = null; 1.298 + } 1.299 + 1.300 + public void _attribute(String localName, Object value) { 1.301 + _attribute("",localName,value); 1.302 + } 1.303 + 1.304 + public void _attribute(String nsUri, String localName, Object value) { 1.305 + checkStartTag(); 1.306 + startTag.addAttribute(nsUri,localName,value); 1.307 + } 1.308 + 1.309 + public void _attribute(QName attributeName, Object value) { 1.310 + _attribute(attributeName.getNamespaceURI(),attributeName.getLocalPart(),value); 1.311 + } 1.312 + 1.313 + public void _namespace(String uri) { 1.314 + _namespace(uri,false); 1.315 + } 1.316 + 1.317 + public void _namespace(String uri, String prefix) { 1.318 + if(prefix==null) 1.319 + throw new IllegalArgumentException(); 1.320 + checkStartTag(); 1.321 + startTag.addNamespaceDecl(uri,prefix,false); 1.322 + } 1.323 + 1.324 + public void _namespace(String uri, boolean requirePrefix) { 1.325 + checkStartTag(); 1.326 + startTag.addNamespaceDecl(uri,null,requirePrefix); 1.327 + } 1.328 + 1.329 + public void _pcdata(Object value) { 1.330 + // we need to allow this method even when startTag has already been completed. 1.331 + // checkStartTag(); 1.332 + addChild(new Pcdata(document,startTag,value)); 1.333 + } 1.334 + 1.335 + public void _cdata(Object value) { 1.336 + addChild(new Cdata(document,startTag,value)); 1.337 + } 1.338 + 1.339 + public void _comment(Object value) throws UnsupportedOperationException { 1.340 + addChild(new Comment(document,startTag,value)); 1.341 + } 1.342 + 1.343 + public <T extends TypedXmlWriter> T _element(String localName, Class<T> contentModel) { 1.344 + return _element(nsUri,localName,contentModel); 1.345 + } 1.346 + 1.347 + public <T extends TypedXmlWriter> T _element(QName tagName, Class<T> contentModel) { 1.348 + return _element(tagName.getNamespaceURI(),tagName.getLocalPart(),contentModel); 1.349 + } 1.350 + 1.351 + public <T extends TypedXmlWriter> T _element(Class<T> contentModel) { 1.352 + return _element(TXW.getTagName(contentModel),contentModel); 1.353 + } 1.354 + 1.355 + public <T extends TypedXmlWriter> T _cast(Class<T> facadeType) { 1.356 + return facadeType.cast(Proxy.newProxyInstance(facadeType.getClassLoader(),new Class[]{facadeType},this)); 1.357 + } 1.358 + 1.359 + public <T extends TypedXmlWriter> T _element(String nsUri, String localName, Class<T> contentModel) { 1.360 + ContainerElement child = new ContainerElement(document,this,nsUri,localName); 1.361 + addChild(child.startTag); 1.362 + tail = child.endTag; 1.363 + 1.364 + // update uncommitted link list 1.365 + if(lastOpenChild!=null) { 1.366 + assert lastOpenChild.parent==this; 1.367 + 1.368 + assert child.prevOpen==null; 1.369 + assert child.nextOpen==null; 1.370 + child.prevOpen = lastOpenChild; 1.371 + assert lastOpenChild.nextOpen==null; 1.372 + lastOpenChild.nextOpen = child; 1.373 + } 1.374 + 1.375 + this.lastOpenChild = child; 1.376 + 1.377 + return child._cast(contentModel); 1.378 + } 1.379 +}