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 
  26 package build.tools.dtdbuilder;
  27 
  28 import javax.swing.text.html.parser.*;
  29 import java.io.DataOutputStream;
  30 import java.io.File;
  31 import java.io.FileInputStream;
  32 import java.io.IOException;
  33 import java.io.FileNotFoundException;
  34 import java.io.BufferedInputStream;
  35 import java.io.OutputStream;
  36 import java.util.Hashtable;
  37 import java.util.Vector;
  38 import java.util.BitSet;
  39 import java.util.StringTokenizer;
  40 import java.util.Enumeration;
  41 import java.util.Properties;
  42 import java.util.zip.DeflaterOutputStream;
  43 import java.util.zip.Deflater;
  44 import 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  */
  60 public
  61 class 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 }