1/*
2 * Copyright (c) 1998, 2013, 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 */
25
26package build.tools.dtdbuilder;
27
28import javax.swing.text.html.parser.*;
29import java.io.DataOutputStream;
30import java.io.File;
31import java.io.FileInputStream;
32import java.io.IOException;
33import java.io.FileNotFoundException;
34import java.io.BufferedInputStream;
35import java.io.OutputStream;
36import java.util.Hashtable;
37import java.util.Vector;
38import java.util.BitSet;
39import java.util.StringTokenizer;
40import java.util.Enumeration;
41import java.util.Properties;
42import java.util.zip.DeflaterOutputStream;
43import java.util.zip.Deflater;
44import java.net.URL;
45
46/**
47 * The representation of an SGML DTD. This is produced by the DTDParser.
48 * The resulting DTD object describes a document syntax and is needed
49 * to parse HTML documents using the Parser. It contains a list of
50 * elements and their attributes as well as a list of entities defined
51 * in the DTD.
52 *
53 * @see Element
54 * @see AttributeList
55 * @see ContentModel
56 * @see DTDParser
57 * @see Parser
58 * @author Arthur van Hoff
59 */
60public
61class DTDBuilder extends DTD {
62
63    static PublicMapping mapping = null;
64
65    // Hash from name to Integer
66    private Hashtable<String, Integer> namesHash = new Hashtable<>();
67    // Vector of all names
68    private Vector<String> namesVector = new Vector<>();
69
70    /**
71     * Create a new DTD.
72     */
73    protected DTDBuilder(String name) {
74        super(name);
75    }
76
77
78    /**
79     * Save to a stream as a Java class. Instantiating this class will
80     * reproduce a (virtually) identical DTD.
81     */
82    void save(DataOutputStream out, String className) throws IOException {
83
84        out.writeInt(DTD.FILE_VERSION);
85
86        buildNamesTable();
87        int numNames = namesVector.size();
88        out.writeShort((short) (namesVector.size()));
89        for (int i = 0; i < namesVector.size(); i++) {
90            String nm = namesVector.elementAt(i);
91            out.writeUTF(nm);
92        }
93
94        saveEntities(out);
95
96        out.writeShort((short) (elements.size()));
97        for (Enumeration<Element> e = elements.elements() ; e.hasMoreElements() ; ) {
98            saveElement(out, e.nextElement());
99        }
100
101        if (namesVector.size() != numNames) {
102            System.err.println("!!! ERROR!  Names were added to the list!");
103            Thread.dumpStack();
104            System.exit(1);
105        }
106    }
107
108    private void buildNamesTable() {
109        for (Enumeration<Entity> e = entityHash.elements() ; e.hasMoreElements() ; ) {
110            Entity ent = e.nextElement();
111            // Do even if not isGeneral().  That way, exclusions and inclusions
112            // will definitely have their element.
113            getNameId(ent.getName());
114        }
115        for (Enumeration<Element> e = elements.elements() ; e.hasMoreElements() ; ) {
116            Element el = e.nextElement();
117            getNameId(el.getName());
118            for (AttributeList atts = el.getAttributes() ; atts != null ; atts = atts.getNext()) {
119                getNameId(atts.getName());
120                if (atts.getValue() != null) {
121                    getNameId(atts.getValue());
122                }
123                Enumeration<?> vals = atts.getValues();
124                while (vals != null && vals.hasMoreElements()) {
125                    String s = (String) vals.nextElement();
126                    getNameId(s);
127                }
128            }
129        }
130    }
131
132    //
133    // The the id of a name from the list of names
134    //
135    private short getNameId(String name)  {
136        Integer o = namesHash.get(name);
137        if (o != null) {
138            return (short) o.intValue();
139        }
140        int i = namesVector.size();
141        namesVector.addElement(name);
142        namesHash.put(name, new Integer(i));
143        return (short) i;
144    }
145
146
147    /**
148     * Save an entity to a stream.
149     */
150    void saveEntities(DataOutputStream out) throws IOException {
151        int num = 0;
152        for (Enumeration<Entity> e = entityHash.elements() ; e.hasMoreElements() ; ) {
153            Entity ent = e.nextElement();
154            if (ent.isGeneral()) {
155                num++;
156            }
157        }
158
159        out.writeShort((short) num);
160        for (Enumeration<Entity> e = entityHash.elements() ; e.hasMoreElements() ; ) {
161            Entity ent = e.nextElement();
162            if (ent.isGeneral()) {
163                out.writeShort(getNameId(ent.getName()));
164                out.writeByte(ent.getType() & ~GENERAL);
165                out.writeUTF(ent.getString());
166            }
167        }
168    }
169
170
171    /**
172     * Save an element to a stream.
173     */
174
175    public void saveElement(DataOutputStream out, Element elem) throws IOException {
176
177        out.writeShort(getNameId(elem.getName()));
178        out.writeByte(elem.getType());
179
180        byte flags = 0;
181        if (elem.omitStart()) {
182            flags |= 0x01;
183        }
184        if (elem.omitEnd()) {
185            flags |= 0x02;
186        }
187        out.writeByte(flags);
188        saveContentModel(out, elem.getContent());
189
190        // Exclusions
191        if (elem.exclusions == null) {
192            out.writeShort(0);
193        } else {
194            short num = 0;
195            for (int i = 0 ; i < elem.exclusions.size() ; i++) {
196                if (elem.exclusions.get(i)) {
197                    num++;
198                }
199            }
200            out.writeShort(num);
201            for (int i = 0 ; i < elem.exclusions.size() ; i++) {
202                if (elem.exclusions.get(i)) {
203                    out.writeShort(getNameId(getElement(i).getName()));
204                }
205            }
206        }
207
208        // Inclusions
209        if (elem.inclusions == null) {
210            out.writeShort(0);
211        } else {
212            short num = 0;
213            for (int i = 0 ; i < elem.inclusions.size() ; i++) {
214                if (elem.inclusions.get(i)) {
215                    num++;
216                }
217            }
218            out.writeShort(num);
219            for (int i = 0 ; i < elem.inclusions.size() ; i++) {
220                if (elem.inclusions.get(i)) {
221                    out.writeShort(getNameId(getElement(i).getName()));
222                }
223            }
224        }
225
226        // Attributes
227        {
228            short numAtts = 0;
229            for (AttributeList atts = elem.getAttributes() ; atts != null ; atts = atts.getNext()) {
230                numAtts++;
231            }
232            out.writeByte(numAtts);
233            for (AttributeList atts = elem.getAttributes() ; atts != null ; atts = atts.getNext()) {
234                out.writeShort(getNameId(atts.getName()));
235                out.writeByte(atts.getType());
236                out.writeByte(atts.getModifier());
237                if (atts.getValue() == null) {
238                    out.writeShort(-1);
239                } else {
240                    out.writeShort(getNameId(atts.getValue()));
241                }
242                if (atts.values == null) {
243                    out.writeShort(0);
244                } else {
245                    out.writeShort((short) atts.values.size());
246                    for (int i = 0; i < atts.values.size(); i++) {
247                        String s = (String) atts.values.elementAt(i);
248                        out.writeShort(getNameId(s));
249                    }
250                }
251            }
252        }
253    }
254
255
256    /**
257     * Save a content model to a stream. This does a
258     * recursive decent of the entire model.
259     */
260    public void saveContentModel(DataOutputStream out, ContentModel model) throws IOException {
261        if (model == null) {
262            out.writeByte(0);
263        } else if (model.content instanceof ContentModel) {
264            out.writeByte(1);
265            out.writeByte(model.type);
266            saveContentModel(out, (ContentModel)model.content);
267
268            saveContentModel(out, model.next);
269        } else if (model.content instanceof Element) {
270            out.writeByte(2);
271            out.writeByte(model.type);
272            out.writeShort(getNameId(((Element) model.content).getName()));
273
274            saveContentModel(out, model.next);
275        }
276    }
277
278
279    /**
280     * Generate a class representing this DTD.
281     */
282
283    public static void main(String argv[]) {
284
285        String dtd_home = System.getProperty("dtd_home") + File.separator;
286        if (dtd_home == null) {
287            System.err.println("Must set property 'dtd_home'");
288            return;
289        }
290
291        DTDBuilder dtd = null;
292        try {
293            dtd = new DTDBuilder(argv[0]);
294            mapping = new PublicMapping(dtd_home, "public.map");
295            String path = mapping.get(argv[0]);
296            new DTDParser().parse(new FileInputStream(path), dtd);
297
298        } catch (IOException e) {
299            System.err.println("Could not open DTD file "+argv[0]);
300            e.printStackTrace(System.err);
301            System.exit(1);
302        }
303        try {
304            DataOutputStream str = new DataOutputStream(System.out);
305            dtd.save(str, argv[0]);
306            str.close();
307        } catch (IOException ex) {
308            ex.printStackTrace();
309            System.exit(1);
310        }
311    }
312
313}
314