001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017:
018: package javax.swing.text.html.parser;
019:
020: import java.io.IOException;
021: import java.util.ArrayList;
022: import java.util.Enumeration;
023: import java.util.Iterator;
024: import java.util.List;
025:
026: import org.apache.harmony.security.asn1.ASN1Sequence;
027: import org.apache.harmony.security.asn1.ASN1SetOf;
028: import org.apache.harmony.security.asn1.ASN1StringType;
029: import org.apache.harmony.security.asn1.ASN1Type;
030: import org.apache.harmony.security.asn1.BerInputStream;
031:
032: /**
033: * It implements ASN.1 codification tools for
034: * <code>javax.swing.text.html.parser.DTD</code>. Given an <code>DTD</code>,
035: * its values are codified in ASN.1 according to the following rule:
036: *
037: * <pre>
038: * BDTD ::= SEQUENCE {
039: * Name UTF8String,
040: * Entity SET OF HTMLEntity,
041: * Element SET OF HTMLElement
042: * }
043: * </pre>
044: *
045: * The class can be used to obtain a byte array representing the codification of
046: * a <code>DTD</code>, as well as a <code>DTD</code> from a byte array
047: * (previously obtained codifying a <code>DTD</code>). In fact, it serves as a
048: * wrapper for the codification and the <code>DTD</code> itself.
049: *
050: */
051: class Asn1Dtd {
052:
053: /**
054: * It stores the definition of a <code>DTD</code> as an ASN.1 valid type
055: * according to the ASN.1 framework.
056: */
057: private static ASN1Type ASN1_DTD;
058:
059: /**
060: * The definition of the ASN1_DTD type according to the rule defined for a
061: * <code>DTD</code>. It also defines a custom encoder/decoder for this
062: * type.
063: */
064: static {
065: ASN1_DTD = new ASN1Sequence(new ASN1Type[] {
066: ASN1StringType.UTF8STRING, // NAME
067: new ASN1SetOf(Asn1Entity.getInstance()), // ENTITY
068: new ASN1SetOf(Asn1Element.getInstance()) // ELEMENT
069: }) {
070:
071: /**
072: * Overrided method used to decodified the information that
073: * represents a <code>DTD</code>. It makes a completely new
074: * <code>DTD</code> with the information interpreted from the
075: * stream.
076: *
077: * @param in
078: * The <code>BerInputStream</code> where the
079: * codificated information will be read from.
080: * @return A <code>DTD</code> filled with the information read
081: * from the stream.
082: */
083: protected Object getDecodedObject(BerInputStream in) {
084: Object values[] = (Object[]) in.content;
085: DTD dtd = new DTD("");
086: dtd.setReading(true);
087:
088: // Name
089: dtd.name = String.valueOf(values[0]);
090:
091: // Entities
092: List lstEntities = (ArrayList) values[1];
093: Entity entity;
094: for (int i = 0; i < lstEntities.size(); i++) {
095: entity = (Entity) lstEntities.get(i);
096: dtd.defineEntity(entity.getName(), entity.type,
097: entity.getData());
098: }
099:
100: // Elements
101: List lstElements = (ArrayList) values[2];
102: Element element;
103: for (int i = 0; i < lstElements.size(); i++) {
104: element = (Element) lstElements.get(i);
105: dtd.defineElement(element.getName(), element
106: .getType(), element.omitStart(), element
107: .omitEnd(), element.getContent(),
108: element.exclusions, element.inclusions,
109: element.getAttributes());
110: }
111: return dtd;
112: }
113:
114: /**
115: * Overrided method used to codify the information stored in a
116: * <code>DTD</code> into an array of bytes, according to its ASN.1
117: * specification.
118: *
119: * @param object
120: * The object where the information to be codified is
121: * stored. It actually consists of a <code>DTD</code>.
122: *
123: * @param values
124: * An array of objects where the dtd's name, entities and
125: * elements information will be stored, ready for
126: * codification.
127: */
128: protected void getValues(Object object, Object[] values) {
129: DTD dtd = (DTD) object;
130:
131: // Name
132: values[0] = dtd.getName();
133:
134: // Entities
135: ArrayList<Asn1Entity> lstEntity = new ArrayList<Asn1Entity>();
136: Iterator itr = dtd.entityHash.values().iterator();
137: while (itr.hasNext()) {
138: lstEntity.add(new Asn1Entity((Entity) itr.next()));
139: }
140: values[1] = lstEntity;
141:
142: // Elements
143: ArrayList<Asn1Element> lstElement = new ArrayList<Asn1Element>();
144: itr = dtd.elements.iterator();
145: while (itr.hasNext()) {
146: lstElement
147: .add(new Asn1Element((Element) itr.next()));
148: }
149: values[2] = lstElement;
150: }
151:
152: };
153: }
154:
155: /**
156: * It returns an <code>ASN1Type</code> value that contains the ASN.1
157: * codification rules for a <code>DTD</code> with its encoder and decoder.
158: *
159: * @return The value that defines an ASN.1 <code>DTD</code> representation
160: * with its encoder/decoder.
161: */
162: static ASN1Type getInstance() {
163: return ASN1_DTD;
164: }
165:
166: /**
167: * An internal copy of the <code>DTD</code> to be codified.
168: */
169: private DTD dtd;
170:
171: /**
172: * An internal copy of the byte array which contains the codification of a
173: * <code>DTD</code>. From this variable, the information used to decodify
174: * a <code>DTD</code> is read from.
175: */
176: private byte[] encoded;
177:
178: /**
179: * An internal copy of the {@link DTD} on which the encoded information
180: * will be extracted to.
181: */
182: private DTD refDTD;
183:
184: /**
185: * Constructs a new instance of a <code>Asn1Dtd</code> class from a byte
186: * array. The byte array received as argument can be later decodified into a
187: * <code>DTD</code>.
188: *
189: * @param encoded
190: * A byte array containing the codified information of a
191: * <code>DTD</code>.
192: */
193: public Asn1Dtd(byte[] encoded) {
194: byte[] copy = new byte[encoded.length];
195: System.arraycopy(encoded, 0, copy, 0, encoded.length);
196: this .encoded = copy;
197: }
198:
199: /**
200: * Constructs a new instance of an <code>Asn1Dtd</code> class from a
201: * <code>DTD</code>. The <code>DTD</code> received as argument can be
202: * then codified into a byte array.
203: * <p>
204: * The value received as argument should not be null. If so, a
205: * <code>NullPointerException</code> is thrown.
206: *
207: * @param dtd
208: * The <code>DTD</code> to be codified.
209: */
210: public Asn1Dtd(DTD dtd) {
211: if (dtd == null) {
212: throw new NullPointerException();
213: }
214: this .dtd = dtd;
215: }
216:
217: /**
218: * Returns the representation of a <code>DTD</code> in ASN.1 codification.
219: * <p>
220: * If the <code >Asn1Dtd</code> object was created with a <code>DTD</code>,
221: * then the <code>DTD</code> is codified and returned as a byte array. On
222: * the other hand, if the instance was created using a byte array, then no
223: * codification process is made.
224: *
225: * @return If at construction time a <code>DTD</code> was given, its
226: * representation in ASN.1 codification. If at construction time a
227: * byte array was given, a copy of the same array is returned.
228: */
229: public byte[] getEncoded() {
230: if (encoded == null) {
231: return ASN1_DTD.encode(dtd);
232: } else {
233: return encoded;
234: }
235: }
236:
237: /**
238: * Returns the <code>DTD</code> obtained from the decodification of an
239: * ASN.1 codified byte array.
240: * <p>
241: * If the <code>Asn1Dtd</code> was created giving a <code>DTD</code>, a
242: * reference to the same <code>DTD</code> is obtained. Otherwise, the byte
243: * array given at construction time is decodificated into a new
244: * <code>DTD</code> object.
245: *
246: * @return If at construction time a <code>DTD</code> was given, the same
247: * <code>DTD</code> is returned. If at construction time a byte
248: * array was given, a <code>DTD</code> constructed with the
249: * information stored in that byte array is returned.
250: * @throws IOException
251: * If the decodification process could not be carried out
252: * properly.
253: */
254: public DTD getDTD(DTD refDTD) throws IOException {
255: this .refDTD = refDTD;
256: if (dtd == null) {
257: return restoreElements(updateInfo((DTD) ASN1_DTD
258: .decode(encoded), refDTD));
259: } else {
260: return dtd;
261: }
262: }
263:
264: /**
265: * Updates the information of the <code>Element</code>'s reference stored
266: * in the <code>ContentModel</code>s.
267: * <p>
268: * When the <code>ContentModel</code> is reconstructed from its ASN.1
269: * codification, no information about other <code>Element</code> is
270: * available at that moment, so information about them must be updated
271: * before returning the whole <code>DTD</code>.
272: *
273: * @param dtd
274: * The <code>DTD</code> whose
275: * <code>Element<code>'s <code>ContentModel</code>s information
276: * must be updated.
277: * @return The <code>DTD</code> with the information updated.
278: */
279: private DTD restoreElements(DTD dtd) {
280: ContentModel model = null;
281: Element tmpElem = null;
282: ArrayList<ContentModel> queue = new ArrayList<ContentModel>();
283: Enumeration itr = dtd.elements.elements();
284: Element e;
285: while (itr.hasMoreElements()) {
286: e = (Element) itr.nextElement();
287: model = e.getContent();
288: if (model != null) {
289: queue.add(model);
290: while (!queue.isEmpty()) {
291: model = queue.remove(0);
292: if (model.content instanceof ContentModel) {
293: queue.add((ContentModel) model.content);
294: } else {
295: tmpElem = dtd.elements
296: .get(((Element) model.content)
297: .getIndex());
298: model.content = tmpElem;
299: }
300: if (model.next != null) {
301: queue.add(model.next);
302: }
303: }
304: }
305: }
306: return dtd;
307: }
308:
309: /**
310: * Updates the information of a {@link DTD} using the information stored in
311: * the previosly decoded {@link DTD}
312: *
313: * @param readDTD the decoded {@link DTD}
314: * @param refDTD the {@link DTD} where the decoded information will be
315: * updated.
316: * @return a reference to the updated {@link DTD}
317: */
318: private DTD updateInfo(DTD readDTD, DTD refDTD) {
319: refDTD.name = readDTD.name;
320: refDTD.entityHash = readDTD.entityHash;
321:
322: for (Element e : readDTD.elements) {
323: if (refDTD.elementHash.containsKey(e.getName())) {
324: Element modifyElem = refDTD.elementHash
325: .get(e.getName());
326: modifyElem.updateElement(e.getIndex(), e.getName(), e
327: .omitStart(), e.omitEnd(), e.exclusions,
328: e.inclusions, e.getType(), e.getContent(), e
329: .getAttributes(), e.data);
330: } else {
331: refDTD.elementHash.put(e.getName(), e);
332: refDTD.elements.add(e);
333: }
334: }
335:
336: return refDTD;
337: }
338: }
|