001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common Development
008: * and Distribution License("CDDL") (collectively, the "License"). You
009: * may not use this file except in compliance with the License. You can obtain
010: * a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html
011: * or glassfish/bootstrap/legal/LICENSE.txt. See the License for the specific
012: * language governing permissions and limitations under the License.
013: *
014: * When distributing the software, include this License Header Notice in each
015: * file and include the License file at glassfish/bootstrap/legal/LICENSE.txt.
016: * Sun designates this particular file as subject to the "Classpath" exception
017: * as provided by Sun in the GPL Version 2 section of the License file that
018: * accompanied this code. If applicable, add the following below the License
019: * Header, with the fields enclosed by brackets [] replaced by your own
020: * identifying information: "Portions Copyrighted [year]
021: * [name of copyright owner]"
022: *
023: * Contributor(s):
024: *
025: * If you wish your version of this file to be governed by only the CDDL or
026: * only the GPL Version 2, indicate your decision by adding "[Contributor]
027: * elects to include this software in this distribution under the [CDDL or GPL
028: * Version 2] license." If you don't indicate a single choice of license, a
029: * recipient has the option to distribute your version of this file under
030: * either the CDDL, the GPL Version 2 or to extend the choice of license to
031: * its licensees as provided above. However, if you add GPL Version 2 code
032: * and therefore, elected the GPL Version 2 license, then the option applies
033: * only if the new code is made subject to such option by the copyright
034: * holder.
035: */
036:
037: package com.sun.xml.bind.v2.runtime.output;
038:
039: import java.io.IOException;
040: import java.io.OutputStream;
041: import java.util.Arrays;
042: import java.util.Collections;
043:
044: import com.sun.xml.bind.api.JAXBRIContext;
045: import com.sun.xml.bind.v2.runtime.Name;
046: import com.sun.istack.FinalArrayList;
047:
048: /**
049: * {@link XmlOutput} that generates canonical XML.
050: *
051: * @see com.sun.xml.bind.api.C14nSupport_ArchitectureDocument
052: * @author Kohsuke Kawaguchi
053: */
054: public class C14nXmlOutput extends UTF8XmlOutput {
055: public C14nXmlOutput(OutputStream out, Encoded[] localNames,
056: boolean namedAttributesAreOrdered) {
057: super (out, localNames);
058: this .namedAttributesAreOrdered = namedAttributesAreOrdered;
059:
060: for (int i = 0; i < staticAttributes.length; i++)
061: staticAttributes[i] = new StaticAttribute();
062: }
063:
064: /**
065: * Hosts statically known attributes.
066: *
067: * {@link StaticAttribute} instances are reused.
068: */
069: private StaticAttribute[] staticAttributes = new StaticAttribute[8];
070: private int len = 0;
071:
072: /**
073: * Used to sort namespace declarations. Reused.
074: */
075: private int[] nsBuf = new int[8];
076:
077: /**
078: * Hosts other attributes whose name are not statically known
079: * (AKA attribute wildcard.)
080: *
081: * As long as this map is empty, there's no need for sorting.
082: * see {@link com.sun.xml.bind.api.C14nSupport_ArchitectureDocument} for more details.
083: */
084: private final FinalArrayList<DynamicAttribute> otherAttributes = new FinalArrayList<DynamicAttribute>();
085:
086: /**
087: * True if {@link JAXBRIContext} is created with c14n support on,
088: * in which case all named attributes are sorted by the marshaller
089: * and we won't have to do it here.
090: */
091: private final boolean namedAttributesAreOrdered;
092:
093: final class StaticAttribute implements Comparable<StaticAttribute> {
094: Name name;
095: String value;
096:
097: public void set(Name name, String value) {
098: this .name = name;
099: this .value = value;
100: }
101:
102: void write() throws IOException {
103: C14nXmlOutput.super .attribute(name, value);
104: }
105:
106: DynamicAttribute toDynamicAttribute() {
107: int nsUriIndex = name.nsUriIndex;
108: int prefix;
109: if (nsUriIndex == -1)
110: prefix = -1;
111: else
112: prefix = nsUriIndex2prefixIndex[nsUriIndex];
113: return new DynamicAttribute(prefix, name.localName, value);
114: }
115:
116: public int compareTo(StaticAttribute that) {
117: return this .name.compareTo(that.name);
118: }
119:
120: }
121:
122: final class DynamicAttribute implements
123: Comparable<DynamicAttribute> {
124: final int prefix;
125: final String localName;
126: final String value;
127:
128: public DynamicAttribute(int prefix, String localName,
129: String value) {
130: this .prefix = prefix;
131: this .localName = localName;
132: this .value = value;
133: }
134:
135: private String getURI() {
136: if (prefix == -1)
137: return "";
138: else
139: return nsContext.getNamespaceURI(prefix);
140: }
141:
142: public int compareTo(DynamicAttribute that) {
143: int r = this .getURI().compareTo(that.getURI());
144: if (r != 0)
145: return r;
146: return this .localName.compareTo(that.localName);
147: }
148: }
149:
150: @Override
151: public void attribute(Name name, String value) throws IOException {
152: if (staticAttributes.length == len) {
153: // reallocate
154: int newLen = len * 2;
155: StaticAttribute[] newbuf = new StaticAttribute[newLen];
156: System.arraycopy(staticAttributes, 0, newbuf, 0, len);
157: for (int i = len; i < newLen; i++)
158: staticAttributes[i] = new StaticAttribute();
159: staticAttributes = newbuf;
160: }
161:
162: staticAttributes[len++].set(name, value);
163: }
164:
165: @Override
166: public void attribute(int prefix, String localName, String value)
167: throws IOException {
168: otherAttributes.add(new DynamicAttribute(prefix, localName,
169: value));
170: }
171:
172: @Override
173: public void endStartTag() throws IOException {
174: if (otherAttributes.isEmpty()) {
175: if (len != 0) {
176: // sort is expensive even for size 0 array,
177: // so it's worth checking len==0
178: if (!namedAttributesAreOrdered)
179: Arrays.sort(staticAttributes, 0, len);
180: // this is the common case
181: for (int i = 0; i < len; i++)
182: staticAttributes[i].write();
183: len = 0;
184: }
185: } else {
186: // this is the exceptional case
187:
188: // sort all the attributes, not just the other attributes
189: for (int i = 0; i < len; i++)
190: otherAttributes.add(staticAttributes[i]
191: .toDynamicAttribute());
192: len = 0;
193: Collections.sort(otherAttributes);
194:
195: // write them all
196: int size = otherAttributes.size();
197: for (int i = 0; i < size; i++) {
198: DynamicAttribute a = otherAttributes.get(i);
199: super .attribute(a.prefix, a.localName, a.value);
200: }
201: otherAttributes.clear();
202: }
203: super .endStartTag();
204: }
205:
206: /**
207: * Write namespace declarations after sorting them.
208: */
209: @Override
210: protected void writeNsDecls(int base) throws IOException {
211: int count = nsContext.getCurrent().count();
212:
213: if (count == 0)
214: return; // quickly reject the most common case
215:
216: if (count > nsBuf.length)
217: nsBuf = new int[count];
218:
219: for (int i = count - 1; i >= 0; i--)
220: nsBuf[i] = base + i;
221:
222: // do a bubble sort. Hopefully # of ns decls are small enough to justify bubble sort.
223: // faster algorithm is more compliated to implement
224: for (int i = 0; i < count; i++) {
225: for (int j = i + 1; j < count; j++) {
226: String p = nsContext.getPrefix(nsBuf[i]);
227: String q = nsContext.getPrefix(nsBuf[j]);
228: if (p.compareTo(q) > 0) {
229: // swap
230: int t = nsBuf[j];
231: nsBuf[j] = nsBuf[i];
232: nsBuf[i] = t;
233: }
234: }
235: }
236:
237: // write them out
238: for (int i = 0; i < count; i++)
239: writeNsDecl(nsBuf[i]);
240: }
241: }
|