001    /*
002    // $Id: //open/util/resgen/src/org/eigenbase/xom/MetaGenerator.java#8 $
003    // package org.eigenbase.xom is an XML Object Mapper
004    // Copyright (C) 2005-2010 The Eigenbase Project
005    // Copyright (C) 2005-2010 Disruptive Tech
006    // Copyright (C) 2005-2010 Red Square, Inc.
007    // Portions Copyright (C) 2000-2008 Kana Software, Inc. and others.
008    // All Rights Reserved.
009    //
010    // This library is free software; you can redistribute it and/or modify it
011    // under the terms of the GNU Lesser General Public License as published by
012    // the Free Software Foundation; either version 2.1 of the License, or
013    // (at your option) any later version.
014    //
015    // This library is distributed in the hope that it will be useful, but
016    // WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
017    // or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
018    // License for more details.
019    //
020    // You should have received a copy of the GNU Lesser General Public License
021    // along with this library; if not, write to the Free Software Foundation, Inc.,
022    // 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
023    //
024    // dsommerfield, 26 December, 2000
025    */
026    
027    package org.eigenbase.xom;
028    import java.io.*;
029    import java.util.*;
030    
031    /**
032     * <code>MetaGenerator</code> is a utility class which reads a XOM Meta
033     * Model description in XML and generates the corresponding .dtd and .java
034     * definition files.  MetaGenerator is invoked during the build process to help
035     * generate files for the build.
036     **/
037    public class MetaGenerator {
038    
039        /**
040         * Private member to hold the active model to be generated.
041         */
042        private MetaDef.Model model;
043    
044        /**
045         * Private member.  This is model.prefix, except that it is "" if
046         * model.prefix is null, rather than null.
047         */
048        private String prefix;
049    
050        private Hashtable keywordMap;
051        private Hashtable typeMap;
052        private Hashtable infoMap;
053        private Hashtable subclassMap;
054        private Vector allTypes;
055        private boolean testMode;
056    
057        private static final String newLine = System.getProperty("line.separator");
058        private static final char fileSep = System.getProperty("file.separator").charAt(0);
059    
060        /**
061         * This helper class contains all necessary information about a type.
062         * The information is collected here to help support inheritence and
063         * other advanced features.
064         */
065        private class TypeInfo
066        {
067            // XML definition of the type.  Includes defined attributes,
068            // content, and other information.
069            public MetaDef.Definition def;
070    
071            // Documentation and code, here for easy reference.
072            public String doc;
073            public String code;
074    
075            // Name of the class and associated XML tag.
076            public String name;
077            public String className;
078            public String tagName;
079    
080            // This array holds all attributes, inherited or otherwise, that may
081            // be used by this type.
082            public MetaDef.Attribute[] allAttributes;
083    
084            // This array holds all attributes that are overridden by this type.
085            public MetaDef.Attribute[] ovrAttributes;
086    
087            // This array holds all new attributes defined only in this type
088            // and not overriding any inherited attributes.
089            public MetaDef.Attribute[] newAttributes;
090    
091            // This array holds all content, inherited or otherwise, that may
092            // be used by this type.
093            public MetaDef.Content[] allContent;
094    
095            // This array holds all new content defined only in this type.
096            public MetaDef.Content[] newContent;
097    
098            // True if content is <Any> (either defined or inherited).
099            public boolean isAny;
100    
101            // True if content is <CData> (either defined or inherited).
102            public boolean isCData;
103    
104            // References to superclass info
105            public TypeInfo[] superInfos;
106    
107            // Class to use when importing elements.
108            public Class impClass;
109            public String impName; // e.g. "foo.MetaDef.Tag"
110    
111            public String contentModel;
112    
113            public TypeInfo(MetaDef.Definition elt)
114                throws XOMException
115            {
116                def = elt;
117    
118                // Get the name and superclass name
119                name = null;
120                MetaDef.Attribute[] attributes;
121                MetaDef.Content[] content;
122                contentModel = "sequential";
123                Vector superInfoList = new Vector();
124                if (elt instanceof MetaDef.Element) {
125                    MetaDef.Element element = (MetaDef.Element) elt;
126                    name = element.type;
127                    if (element.dtdName != null) {
128                        tagName = element.dtdName;
129                    } else {
130                        tagName = prefix + name;
131                    }
132                    registerSuper(superInfoList, element._class);
133                    attributes = element.attributes;
134                    content = element.content;
135                    contentModel = element.contentModel;
136                    doc = element.doc;
137                    code = element.code;
138                    impClass = null;
139                    impName = null;
140                } else if (elt instanceof MetaDef.Plugin) {
141                    name = ((MetaDef.Plugin)elt).type;
142                    tagName = prefix + name;
143                    registerSuper(superInfoList, ((MetaDef.Plugin)elt)._class);
144                    attributes = ((MetaDef.Plugin)elt).attributes;
145                    content = new MetaDef.Content[0];
146                    doc = ((MetaDef.Plugin)elt).doc;
147                    code = ((MetaDef.Plugin)elt).code;
148                    impClass = null;
149                    impName = null;
150                } else if (elt instanceof MetaDef.Class) {
151                    name = ((MetaDef.Class)elt)._class;
152                    tagName = "(%" + name + ";)";
153                    registerSuper(superInfoList, ((MetaDef.Class)elt).superclass);
154                    attributes = ((MetaDef.Class)elt).attributes;
155                    content = ((MetaDef.Class)elt).content;
156                    doc = ((MetaDef.Class)elt).doc;
157                    code = ((MetaDef.Class)elt).code;
158                    impClass = null;
159                    impName = null;
160                } else if (elt instanceof MetaDef.StringElement) {
161                    name = ((MetaDef.StringElement)elt).type;
162                    tagName = prefix + name;
163                    attributes = new MetaDef.Attribute[0];
164                    content = new MetaDef.Content[0];
165                    doc = ((MetaDef.StringElement)elt).doc;
166                    code = null;
167                    impClass = null;
168                    impName = null;
169                } else if (elt instanceof MetaDef.Import) {
170                    MetaDef.Import imp = (MetaDef.Import)elt;
171                    name = imp.type;
172                    if (imp.dtdName != null) {
173                        tagName = imp.dtdName;
174                    } else {
175                        tagName = prefix + name;
176                    }
177                    attributes = new MetaDef.Attribute[0];
178                    content = new MetaDef.Content[0];
179                    doc = null;
180                    code = null;
181                    try {
182                        impName = imp.defPackage + "." + imp.defClass + "." + name;
183                        impClass = Class.forName(imp.defPackage + "."
184                                                 + imp.defClass + "$"
185                                                 + name);
186                    } catch (ClassNotFoundException ex) {
187    //                      throw new XOMException(
188    //                          "Import " + name + " references Java Class "
189    //                          + imp.defPackage + "." + imp.defClass
190    //                          + "." + name + " that does not exist.");
191                    }
192                } else {
193                    throw new XOMException("Illegal element type "
194                                           + elt.getClass().getName());
195                }
196                className = XOMUtil.capitalize(name);
197                superInfos =
198                    (TypeInfo[]) superInfoList.toArray(
199                        new TypeInfo[superInfoList.size()]);
200    
201                // Check for special content (<Any> or <CData>).  If we find it,
202                // it must be the only content defined.
203                boolean newAny = false;
204                boolean newCData = false;
205                if (content.length == 1) {
206                    if (content[0] instanceof MetaDef.CData) {
207                        newCData = true;
208                    } else if (content[0] instanceof MetaDef.Any) {
209                        newAny = true;
210                    }
211                }
212    
213                // Make sure that <Any> or <CData> occurs only by itself.
214                if (!newAny && !newCData) {
215                    for (int i = 0; i < content.length; i++) {
216                        if (content[i] instanceof MetaDef.CData
217                            || content[i] instanceof MetaDef.Any) {
218                            throw new XOMException(
219                                "Type " + name + " defines <Any> or <CData> "
220                                + "content as well as other content.");
221                        }
222                    }
223                }
224    
225                // Do we have a superclass/supertype?
226                if (superInfos.length == 0) {
227                    // No supertype, so consider this type by itself.
228                    allAttributes = attributes;
229                    ovrAttributes = new MetaDef.Attribute[0];
230                    newAttributes = allAttributes;
231    
232                    if (newAny || newCData) {
233                        isAny = newAny;
234                        isCData = newCData;
235                        allContent = new MetaDef.Content[0];
236                    } else {
237                        isAny = isCData = false;
238                        allContent = content;
239                    }
240                    newContent = allContent;
241                } else {
242                    // Reconcile attributes.
243                    Hashtable attrHash = new Hashtable();
244                    Hashtable ovrHash = new Hashtable();
245                    Vector allAttrs = new Vector();
246                    Vector ovrAttrs = new Vector();
247                    Vector newAttrs = new Vector();
248    
249                    for (int j = 0; j < superInfos.length; j++) {
250                        TypeInfo superInfo = superInfos[j];
251    
252                        for (int i = 0; i < superInfo.allAttributes.length; i++) {
253                            attrHash.put(
254                                superInfo.allAttributes[i].name,
255                                superInfo.allAttributes[i]);
256                        }
257                    }
258                    for (int i = 0; i < attributes.length; i++) {
259                        // Does the attribute already exist?
260                        MetaDef.Attribute inhAttr =
261                            (MetaDef.Attribute)(attrHash.get(attributes[i].name));
262                        if (inhAttr == null) {
263                            // attribute doesn't exist, so add to all and new.
264                            allAttrs.addElement(attributes[i]);
265                            newAttrs.addElement(attributes[i]);
266                        } else {
267                            // attribute does exist.  Type must match exactly.
268                            if (!(attributes[i].type.equals(inhAttr.type))) {
269                                throw new XOMException(
270                                    "Element " + name + " inherits attribute "
271                                    + inhAttr.name + " of type " + inhAttr.type
272                                    + " but redefines it to be of type "
273                                    + attributes[i].type);
274                            }
275                            // Add to overridden vector and overridden hashtable
276                            ovrAttrs.addElement(attributes[i]);
277                            ovrHash.put(attributes[i].name,
278                                        attributes[i]);
279                        }
280                    }
281    
282                    // Add all non-overridden attributes to the allAttributes vector
283                    boolean superAny = false, superCData = false;
284                    for (int j = 0; j < superInfos.length; j++) {
285                        TypeInfo superInfo = superInfos[j];
286                        for (int i = 0; i < superInfo.allAttributes.length; i++) {
287                            if (ovrHash.get(superInfo.allAttributes[i].name)
288                                == null)
289                            {
290                                allAttrs.addElement(superInfo.allAttributes[i]);
291                            }
292                        }
293    
294                        if (superInfo.isAny) {
295                            superAny = true;
296                        }
297                        if (superInfo.isCData) {
298                            superCData = true;
299                        }
300                    }
301    
302                    // Add all overridden attributes to the allAttributes vector
303                    for (int i = 0; i < ovrAttrs.size(); i++) {
304                        allAttrs.addElement(ovrAttrs.elementAt(i));
305                    }
306                    allAttributes = new MetaDef.Attribute[allAttrs.size()];
307                    for (int i = 0; i < allAttributes.length; i++) {
308                        allAttributes[i] =
309                            (MetaDef.Attribute) allAttrs.elementAt(i);
310                    }
311                    ovrAttributes = new MetaDef.Attribute[ovrAttrs.size()];
312                    for (int i = 0; i < ovrAttributes.length; i++) {
313                        ovrAttributes[i] =
314                            (MetaDef.Attribute) ovrAttrs.elementAt(i);
315                    }
316                    newAttributes = new MetaDef.Attribute[newAttrs.size()];
317                    for (int i = 0; i < newAttributes.length; i++) {
318                        newAttributes[i] =
319                            (MetaDef.Attribute) newAttrs.elementAt(i);
320                    }
321                    // Reconcile content.  First check for specials.
322                    if (newAny || newCData) {
323                        for (int j = 0; j < superInfos.length; j++) {
324                            TypeInfo superInfo = superInfos[j];
325                            if (superInfo.isAny || superInfo.isCData) {
326                                throw new XOMException(
327                                    "Element " + name + " both defines and "
328                                    + "inherits <CData> or <Any> content.");
329                            }
330                            if (superInfo.allContent.length > 0) {
331                                throw new XOMException(
332                                    "Element " + name + " inherits standard "
333                                    + "content but defines <CData> or <Any> "
334                                    + "content.");
335                            }
336                        }
337                        isAny = newAny;
338                        isCData = newCData;
339                        allContent = new MetaDef.Content[0];
340                        newContent = new MetaDef.Content[0];
341                    } else if (superAny || superCData) {
342                        if (content.length > 0) {
343                            throw new XOMException(
344                                "Element " + name + " inherits <CData> or <Any> "
345                                + "content but defines standard content.");
346                        }
347                        isAny = superAny;
348                        isCData = superCData;
349                        allContent = new MetaDef.Content[0];
350                        newContent = new MetaDef.Content[0];
351                    } else {
352                        isAny = isCData = false;
353    
354                        // Overriding of content is forbidden.
355                        Hashtable contentHash = new Hashtable();
356                        Vector allContentVec = new Vector();
357                        Vector newContentVec = new Vector();
358                        newContentVec.addAll(Arrays.asList(content));
359                        for (int j = 0; j < superInfos.length; j++) {
360                            TypeInfo superInfo = superInfos[j];
361                            for (int i = 0; i < superInfo.allContent.length; i++) {
362                                if (!superInfo.isInterface()) {
363                                    contentHash.put(
364                                        getContentName(superInfo.allContent[i]),
365                                        superInfo.allContent[i]);
366                                } else {
367                                    newContentVec.addElement(
368                                        superInfo.allContent[i]);
369                                }
370                                allContentVec.addElement(superInfo.allContent[i]);
371                            }
372                        }
373                        for (int i = 0; i < content.length; i++) {
374                            MetaDef.Content inhContent =
375                                (MetaDef.Content)
376                                (contentHash.get(getContentName(content[i])));
377                            if (inhContent != null) {
378                                throw new XOMException(
379                                    "Content named " + getContentName(content[i])
380                                    + " defined in element " + name + " was "
381                                    + "already defined in an inherited element.");
382                            }
383                            allContentVec.addElement(content[i]);
384                        }
385                        allContent = new MetaDef.Content[allContentVec.size()];
386                        for (int i = 0; i < allContent.length; i++) {
387                            allContent[i] =
388                                (MetaDef.Content) allContentVec.elementAt(i);
389                        }
390                        newContent = new MetaDef.Content[newContentVec.size()];
391                        for (int i = 0; i < newContent.length; i++) {
392                            newContent[i] =
393                                (MetaDef.Content) newContentVec.elementAt(i);
394                        }
395                    }
396                }
397    
398                // Add ourself to the hashtable if we're not already there
399                if (infoMap.get(name) == null) {
400                    infoMap.put(name, this);
401                }
402            }
403    
404            private void registerSuper(Vector superInfoList, String className)
405                throws XOMException
406            {
407                if (className == null) {
408                    return;
409                }
410                final String[] classNames = className.split(",");
411                for (int i = 0; i < classNames.length; i++) {
412                    superInfoList.add(lookupSuper(classNames[i].trim()));
413                }
414            }
415    
416            /**
417             * Get the TypeInfo record for the superclass.  If we don't find
418             * it, we'll have to create it by looking up its definition.
419             *
420             * @param superName Name of superClass
421             * @throws XOMException
422             */
423            private TypeInfo lookupSuper(String superName) throws XOMException {
424                TypeInfo superInfo = (TypeInfo) infoMap.get(superName);
425                if (superInfo == null) {
426                    MetaDef.Definition superDef =
427                        (MetaDef.Definition) infoMap.get(superName);
428                    if (superDef == null) {
429                        throw new XOMException(
430                            "Parent class " + superName + " of element "
431                            + name + " was never defined.");
432                    }
433                    superInfo = new TypeInfo(superDef);
434                }
435                return superInfo;
436            }
437    
438            /**
439             * Returns whether this type is implemented by an Java interface.
440             */
441            private boolean isInterface() {
442                return def instanceof MetaDef.Class;
443            }
444    
445            public void writeJavaClass(PrintWriter out)
446                throws XOMException
447            {
448                // Documentation first
449                if (doc != null) {
450                    writeJavaDoc(out, 1, doc);
451                }
452                // Then create the inner class.
453                String classDesc;
454                StringBuffer extendsList = new StringBuffer();
455                StringBuffer implementsList = new StringBuffer();
456                if (def instanceof MetaDef.Class) {
457                    classDesc = "interface";
458                    for (int j = 0; j < superInfos.length; j++) {
459                        TypeInfo superInfo = superInfos[j];
460                        if (superInfo.isInterface()) {
461                            append(
462                                extendsList, " extends ", ", ",
463                                superInfo.className);
464                        } else {
465                            throw new RuntimeException(
466                                "Superclass of a Class must be a Class: "
467                                + className + " extends " + superInfo.className);
468                        }
469                    }
470                    if (extendsList.length() == 0) {
471                        extendsList.append(" extends org.eigenbase.xom.NodeDef");
472                    }
473                } else {
474                    final MetaDef.Element element = (MetaDef.Element) def;
475                    classDesc =
476                        "static "
477                        + (element._abstract != null
478                           && element._abstract.booleanValue()
479                            ? "abstract "
480                            : "")
481                        + "class";
482                    for (int j = 0; j < superInfos.length; j++) {
483                        TypeInfo superInfo = superInfos[j];
484                        if (superInfo.isInterface()) {
485                            append(
486                                implementsList, " implements ", ", ",
487                                superInfo.className);
488                        } else {
489                            append(
490                                extendsList, " extends ", ", ",
491                                superInfo.className);
492                        }
493                    }
494                    if (extendsList.length() == 0) {
495                        extendsList.append(" extends org.eigenbase.xom.ElementDef");
496                    }
497                }
498                out.println("\tpublic " + classDesc + " " + className
499                            + extendsList + implementsList);
500                if (isAny) {
501                    out.println("\t\timplements org.eigenbase.xom.Any");
502                }
503    
504                if (def instanceof MetaDef.Class) {
505                    out.println("\t{");
506    
507                    // Add the code section, if defined
508                    if (code != null) {
509                        writeJavaCode(out, 2, code);
510                    }
511    
512                    // Complete the class definition and finish with a blank.
513                    out.println("\t}");
514                    out.println();
515                    return;
516                }
517    
518                out.println("\t{");
519    
520                // Default constructor
521                out.println("\t\tpublic " + className + "()");
522                out.println("\t\t{");
523                out.println("\t\t}");
524                out.println();
525    
526                // org.eigenbase.xom.DOMWrapper Constructor
527                out.println("\t\tpublic " + className
528                            + "(org.eigenbase.xom.DOMWrapper _def)");
529                out.println("\t\t\tthrows org.eigenbase.xom.XOMException");
530                out.println("\t\t{");
531    
532                // Body of constructor.  Special case for completely empty
533                // model (no content and no attributes) to avoid warnings
534                // about unused things.
535                boolean mixed = contentModel.equals("mixed");
536                if (allContent.length == 0 && allAttributes.length == 0 &&
537                   !isAny && !isCData &&
538                    !(def instanceof MetaDef.Plugin)) {
539                    // constructor has no body
540                } else {
541                    if (def instanceof MetaDef.Element &&
542                        booleanValue(
543                            new Boolean[] {
544                                ((MetaDef.Element) def).keepDef,
545                                model.defaultKeepDef,
546                                Boolean.FALSE})) {
547                        out.println("\t\t\tthis._def = _def;");
548                    }
549    
550                    out.println("\t\t\ttry {");
551    
552                    // Plugins: read defPackage and defClass here.
553                    if (def instanceof MetaDef.Plugin) {
554                        out.println("\t\t\t\tdefPackage = "
555                                    + "org.eigenbase.xom.DOMElementParser."
556                                    + "requiredDefAttribute("
557                                    + "_def, \"defPackage\", \"org.eigenbase.xom\");");
558                        out.println("\t\t\t\tdefClass = org.eigenbase.xom.DOMElementParser."
559                                    + "requiredDefAttribute("
560                                    + "_def, \"defClass\", null);");
561    
562                        // Get the enclosure class we'll be using
563                        out.println("\t\t\t\tClass _pluginClass = "
564                                    + "org.eigenbase.xom.DOMElementParser.getPluginClass("
565                                    + "defPackage, defClass);");
566                    }
567    
568                    // Create the parser.  If using a Plugin, parse from a
569                    // different enclosure class.
570                    out.print("\t\t\t\torg.eigenbase.xom.DOMElementParser _parser "
571                              + "= new org.eigenbase.xom.DOMElementParser("
572                              + "_def, ");
573                    if (def instanceof MetaDef.Plugin) {
574                        out.println("\"\", _pluginClass);");
575                    } else {
576                        if (model.prefix == null) {
577                            out.print("\"\", ");
578                        } else {
579                            out.print("\"" + model.prefix + "\", ");
580                        }
581                        out.println(model.className + ".class);");
582                    }
583    
584                    // Define a temp array if any Array elements are used
585                    if (hasContentType(allContent, MetaDef.Array.class)) {
586                        out.println("\t\t\t\torg.eigenbase.xom.NodeDef[] "
587                                    + "_tempArray;");
588                    }
589    
590                    // Generate statements to read in all attributes.
591                    for (int i = 0; i < allAttributes.length; i++) {
592                        writeJavaGetAttribute(out, allAttributes[i]);
593                    }
594    
595                    // Generate statements to read in all content.
596                    if (def instanceof MetaDef.Plugin) {
597                        writeJavaGetPluginContent(out, mixed);
598                    } else if (isAny) {
599                        writeJavaGetAnyContent(out, mixed);
600                    } else if (isCData) {
601                        writeJavaGetCDataContent(out);
602                    } else {
603                        for (int i = 0; i < allContent.length; i++) {
604                            writeJavaGetContent(out, allContent[i]);
605                        }
606                    }
607    
608                    out.println("\t\t\t} catch(org.eigenbase.xom.XOMException _ex) {");
609                    out.println("\t\t\t\tthrow new org.eigenbase.xom.XOMException("
610                                + "\"In \" + getName() + \": \" + _ex.getMessage());");
611                    out.println("\t\t\t}");
612                }
613    
614                // Finish the constructor
615                out.println("\t\t}");
616                out.println();
617    
618                // Declare all attributes inherited from superclass, if superclass
619                // is an interface. (Because interfaces can't have attributes.)
620                for (int j = 0; j < superInfos.length; j++) {
621                    TypeInfo superInfo = superInfos[j];
622                    if (superInfo.isInterface()) {
623                        for (int i = 0; i < superInfo.newAttributes.length; i++) {
624                            writeJavaDeclareAttribute(
625                                out, superInfo.newAttributes[i]);
626                        }
627                    }
628                }
629    
630                // Declare all new attributes
631                for (int i = 0; i < newAttributes.length; i++) {
632                    writeJavaDeclareAttribute(out, newAttributes[i]);
633                }
634                if (def instanceof MetaDef.Plugin) {
635                    writeJavaDeclarePluginAttributes(out);
636                }
637                if (def instanceof MetaDef.Element &&
638                    booleanValue(
639                        new Boolean[] {
640                            ((MetaDef.Element) def).keepDef,
641                            model.defaultKeepDef,
642                            Boolean.FALSE})) {
643                    out.println("\t\tpublic org.eigenbase.xom.DOMWrapper _def;");
644                }
645                out.println();
646    
647                // Declare all new content
648                if (def instanceof MetaDef.Plugin) {
649                    writeJavaDeclarePluginContent(out, mixed);
650                } else if (isAny) {
651                    writeJavaDeclareAnyContent(out, mixed);
652                } else if (isCData) {
653                    writeJavaDeclareCDataContent(out);
654                } else {
655                    for (int i = 0; i < newContent.length; i++) {
656                        writeJavaDeclareContent(out, newContent[i]);
657                    }
658                }
659                out.println();
660    
661                // Create the getName() function
662                out.println("\t\tpublic String getName()");
663                out.println("\t\t{");
664                out.println("\t\t\treturn \"" + className + "\";");
665                out.println("\t\t}");
666                out.println();
667    
668                // Create the display() function
669                out.println("\t\tpublic void display(java.io.PrintWriter _out, "
670                            + "int _indent)");
671                out.println("\t\t{");
672                if (def instanceof MetaDef.Class && !isAny && !isCData &&
673                    allContent.length == 0 && allAttributes.length == 0) {
674                } else {
675                    out.println("\t\t\t_out.println(getName());");
676                }
677                for (int i = 0; i < allAttributes.length; i++) {
678                    writeJavaDisplayAttribute(out, allAttributes[i]);
679                }
680                if (def instanceof MetaDef.Plugin) {
681                    writeJavaDisplayPluginAttributes(out);
682                }
683                if (def instanceof MetaDef.Plugin) {
684                    writeJavaDisplayPluginContent(out);
685                } else if (isAny) {
686                    writeJavaDisplayAnyContent(out);
687                } else if (isCData) {
688                    writeJavaDisplayCDataContent(out);
689                } else {
690                    for (int i = 0; i < allContent.length; i++) {
691                        writeJavaDisplayContent(out, allContent[i]);
692                    }
693                }
694                out.println("\t\t}");
695    
696                // Create the displayXML() function
697                out.println("\t\tpublic void displayXML("
698                            + "org.eigenbase.xom.XMLOutput _out, "
699                            + "int _indent)");
700                out.println("\t\t{");
701                out.println("\t\t\t_out.beginTag(\""
702                            + tagName + "\", "
703                            + "new org.eigenbase.xom.XMLAttrVector()");
704                for (int i = 0; i < allAttributes.length; i++) {
705                    writeJavaDisplayXMLAttribute(out, allAttributes[i]);
706                }
707                if (def instanceof MetaDef.Plugin) {
708                    writeJavaDisplayXMLPluginAttributes(out);
709                }
710                out.println("\t\t\t\t);");
711    
712                if (def instanceof MetaDef.Plugin) {
713                    writeJavaDisplayXMLPluginContent(out);
714                } else if (isAny) {
715                    writeJavaDisplayXMLAnyContent(out);
716                } else if (isCData) {
717                    writeJavaDisplayXMLCDataContent(out);
718                } else {
719                    for (int i = 0; i < allContent.length; i++) {
720                        writeJavaDisplayXMLContent(out, allContent[i]);
721                    }
722                }
723                out.println("\t\t\t_out.endTag(\"" + tagName + "\");");
724                out.println("\t\t}");
725    
726                // Create the displayDiff() function
727                out.println("\t\tpublic boolean displayDiff("
728                            + "org.eigenbase.xom.ElementDef _other, "
729                            + "java.io.PrintWriter _out, "
730                            + "int _indent)");
731                out.println("\t\t{");
732                if (allAttributes.length > 0 ||
733                    allContent.length > 0 || isAny || isCData ||
734                    def instanceof MetaDef.Plugin) {
735                    out.println("\t\t\t" + className + " _cother = ("
736                                + className + ")_other;");
737                }
738                int[] diffCount = {0};
739                for (int i = 0; i < newAttributes.length; i++) {
740                    writeJavaDisplayDiffAttribute(out, diffCount, allAttributes[i]);
741                }
742                if (def instanceof MetaDef.Plugin) {
743                    writeJavaDisplayDiffPluginAttributes(out, diffCount);
744                }
745                if (def instanceof MetaDef.Plugin) {
746                    writeJavaDisplayDiffPluginContent(out, diffCount);
747                } else if (isAny) {
748                    writeJavaDisplayDiffAnyContent(out, diffCount);
749                } else if (isCData) {
750                    writeJavaDisplayDiffCDataContent(out, diffCount);
751                } else {
752                    for (int i = 0; i < allContent.length; i++) {
753                        writeJavaDisplayDiffContent(out, diffCount, allContent[i]);
754                    }
755                }
756                out.println("\t\t\treturn "
757                    + (diffCount[0] > 0 ? "_diff" : "true")
758                    + ";");
759                out.println("\t\t}");
760    
761                // Add the code section, if defined
762                if (code != null) {
763                    writeJavaCode(out, 2, code);
764                }
765    
766                // Complete the class definition and finish with a blank.
767                out.println("\t}");
768                out.println();
769            }
770        }
771    
772        private void append(
773            StringBuffer buf, String first, String next, String s)
774        {
775            if (buf.length() == 0) {
776                buf.append(first);
777            } else {
778                buf.append(next);
779            }
780            buf.append(s);
781        }
782    
783        /**
784         * Converts a {@link Boolean} object into a {@code boolean} value,
785         * falling back to successive defaults if values are null.
786         *
787         * <p>If all of the values are null, returns {@code false}; but we
788         * recommend passing in an explicit {@code true} or {@code false} as the
789         * last argument.</p>
790         *
791         * <p>For example,
792         * {@code booleanValue(null, true, false)} returns {@code true};
793         * {@code booleanValue(null, null)} returns {@code false}.</p>
794         *
795         * @param bs One or more boolean values
796         * @return Boolean value
797         */
798        private static boolean booleanValue(Boolean[] bs)
799        {
800            for (int i = 0; i < bs.length; i++) {
801                Boolean b = bs[i];
802                if (b != null) {
803                    return b.booleanValue();
804                }
805            }
806            return false;
807        }
808    
809        /**
810         * Get the name of any piece of content of any type.
811         * @return the name of the piece of content.
812         * @throws XOMException if the content is <Any> or <CData>.
813         */
814        private static String getContentName(MetaDef.Content content)
815            throws XOMException
816        {
817            if (content instanceof MetaDef.Object) {
818                return ((MetaDef.Object)content).name;
819            } else if (content instanceof MetaDef.Array) {
820                return ((MetaDef.Array)content).name;
821            } else {
822                throw new XOMException(
823                    "Content of type " + content.getClass().getName()
824                    + " does not have a name.");
825            }
826        }
827    
828        /**
829         * Return the TypeInfo class associated with the given name.
830         *
831         * @post fail == false || return != null
832         * @exception XOMException if the type has not been defined
833         */
834        public TypeInfo getTypeInfo(String name, boolean fail)
835            throws XOMException
836        {
837            TypeInfo info = (TypeInfo) infoMap.get(name);
838            if (info == null && fail == true) {
839                throw new XOMException(
840                    "Type " + name + " does not exist.");
841            }
842            return info;
843        }
844    
845        /**
846         * Construct a MetaGenerator from an XML file.  The XML should meet the
847         * specifications of the XOM Meta Model.
848         * @param xmlFile a filename for the xml description of the model to be
849         * processed.
850         */
851        public MetaGenerator(String xmlFile, boolean testMode)
852                throws XOMException, IOException {
853            this(xmlFile, testMode, null);
854        }
855    
856        protected MetaGenerator(String xmlFile, boolean testMode, String className)
857                throws XOMException, IOException {
858            this.testMode = testMode;
859            // Create a non-validating XML parser to parse the file
860            FileInputStream in = new FileInputStream(xmlFile);
861            Parser parser = XOMUtil.createDefaultParser();
862            try {
863                DOMWrapper def = parser.parse(in);
864                model = new MetaDef.Model(def);
865            } catch (XOMException ex) {
866                throw new XOMException(ex, "Failed to parse XML file: " + xmlFile);
867            }
868    
869            // check that class names are consistent
870            if (className != null) {
871                if (model.className == null) {
872                    model.className = className;
873                } else {
874                    String modelClassName = model.className;
875                    if (model.packageName != null &&
876                        !model.packageName.equals("")) {
877                        modelClassName = model.packageName + "." +
878                            model.className;
879                    }
880                    if (!className.equals(modelClassName)) {
881                        throw new XOMException(
882                            "className parameter (" + className +
883                            ") is inconsistent with model's packageName and " +
884                            "className attributes (" + modelClassName + ")");
885                    }
886                }
887            }
888    
889            // Construct the meta model from its XML description
890            prefix = model.prefix;
891            if (prefix == null) {
892                prefix = "";
893            }
894            // Setup the Hashtable maps
895            initKeywordMap();
896            initTypeMap();
897            initSubclassMap();
898        }
899    
900        /**
901         * Initialize the keyword map.  This class maps all Java keywords to safe
902         * versions (prepended with an underscore) which may be used for generated
903         * names.  Java keywords are listed in the java spec at
904         * <a href="http://java.sun.com/docs/books/jls/html/3.doc.html#229308">
905         * http://java.sun.com/docs/books/jls/html/3.doc.html#229308</a>
906         */
907        private void initKeywordMap()
908        {
909            keywordMap = new Hashtable();
910            keywordMap.put("abstract", "_abstract");
911            keywordMap.put("boolean", "_boolean");
912            keywordMap.put("break", "_break");
913            keywordMap.put("byte", "_byte");
914            keywordMap.put("case", "_case");
915            keywordMap.put("catch", "_catch");
916            keywordMap.put("char", "_char");
917            keywordMap.put("class", "_class");
918            keywordMap.put("const", "_const");
919            keywordMap.put("continue", "_continue");
920            keywordMap.put("default", "_default");
921            keywordMap.put("do", "_do");
922            keywordMap.put("double", "_double");
923            keywordMap.put("else", "_else");
924            keywordMap.put("extends", "_extends");
925            keywordMap.put("final", "_final");
926            keywordMap.put("finally", "_finally");
927            keywordMap.put("float", "_float");
928            keywordMap.put("for", "_for");
929            keywordMap.put("if", "_if");
930            keywordMap.put("implements", "_implements");
931            keywordMap.put("import", "_import");
932            keywordMap.put("instanceof", "_instanceof");
933            keywordMap.put("int", "_int");
934            keywordMap.put("interface", "_interface");
935            keywordMap.put("long", "_long");
936            keywordMap.put("native", "_native");
937            keywordMap.put("new", "_new");
938            keywordMap.put("goto", "_goto");
939            keywordMap.put("package", "_package");
940            keywordMap.put("private", "_private");
941            keywordMap.put("protected", "_protected");
942            keywordMap.put("public", "_public");
943            keywordMap.put("return", "_return");
944            keywordMap.put("short", "_short");
945            keywordMap.put("static", "_static");
946            keywordMap.put("super", "_super");
947            keywordMap.put("switch", "_switch");
948            keywordMap.put("synchronized", "_synchronized");
949            keywordMap.put("this", "_this");
950            keywordMap.put("throw", "_throw");
951            keywordMap.put("throws", "_throws");
952            keywordMap.put("transient", "_transient");
953            keywordMap.put("try", "_try");
954            keywordMap.put("void", "_void");
955            keywordMap.put("volatile", "_volatile");
956            keywordMap.put("while", "_while");
957            keywordMap.put("true", "_true");
958            keywordMap.put("false", "_false");
959            keywordMap.put("null", "_null");
960        }
961    
962        /**
963         * All Elements in the meta model have an associated type name which
964         * identifies the element.  The type map allows the XMLDef.ElementType
965         * object describing an element to be retrieved from its name.  It is
966         * used to resolve references to element type names appearing
967         * throughout a model.
968         */
969        private void initTypeMap()
970            throws XOMException
971        {
972            typeMap = new Hashtable();
973            allTypes = new Vector();
974            for (int i = 0; i < model.elements.length; i++) {
975                MetaDef.Definition elt = model.elements[i];
976                String name = null;
977                if (elt instanceof MetaDef.Element) {
978                    name = ((MetaDef.Element)elt).type;
979                } else if (elt instanceof MetaDef.Plugin) {
980                    name = ((MetaDef.Plugin)elt).type;
981                } else if (elt instanceof MetaDef.Class) {
982                    name = ((MetaDef.Class)elt)._class;
983                } else if (elt instanceof MetaDef.StringElement) {
984                    name = ((MetaDef.StringElement)elt).type;
985                } else if (elt instanceof MetaDef.Import) {
986                    name = ((MetaDef.Import)elt).type;
987                } else {
988                    throw new XOMException(
989                        "Illegal element type "
990                        + elt.getClass().getName());
991                }
992                typeMap.put(name, elt);
993                allTypes.addElement(name);
994            }
995    
996            infoMap = new Hashtable();
997            for (int i = 0; i < model.elements.length; i++) {
998                // Get the element
999                MetaDef.Definition elt = model.elements[i];
1000    
1001                // Construct the new TypeInfo object and add to the hashtable
1002                TypeInfo info = new TypeInfo(elt);
1003                infoMap.put(info.name, info);
1004            }
1005        }
1006    
1007        /**
1008         * In a few cases, a complete list of all subclasses of a class
1009         * object is required.  The subclass map maps each class object
1010         * (identified by its name) to a Vector containing all of its
1011         * subclasses.  Currently, all subclasses must be Element types.
1012         */
1013        private void initSubclassMap()
1014            throws XOMException
1015        {
1016            subclassMap = new Hashtable();
1017    
1018            // First, iterate through all Class elements in the model,
1019            // initializing a location in the hashtable for each.
1020            for (int i = 0; i < model.elements.length; i++) {
1021                MetaDef.Definition elt = model.elements[i];
1022                if (elt instanceof MetaDef.Class) {
1023                    MetaDef.Class _class = (MetaDef.Class)elt;
1024                    subclassMap.put(_class._class, new Vector());
1025                }
1026                if (elt instanceof MetaDef.Element) {
1027                    MetaDef.Element element = (MetaDef.Element)elt;
1028                    subclassMap.put(element.type, new Vector());
1029                }
1030            }
1031    
1032            // Now, iterate through all Element elements in the model.
1033            // For each one, go through all of its superclasses and add itself to
1034            // the vector of each.
1035            // If a class is not found, it is an error.
1036            for (int i = 0; i < model.elements.length; i++) {
1037                MetaDef.Definition elt = model.elements[i];
1038                if (elt instanceof MetaDef.Element) {
1039                    MetaDef.Element elem = (MetaDef.Element)elt;
1040                    TypeInfo info = getTypeInfo(elem.type, true);
1041                    addToSubclassMap(elem, info);
1042                }
1043            }
1044        }
1045    
1046        /**
1047         * Helper method for initSubclassMap:
1048         * Add this element to the subclass map for each superclass of info.
1049         */
1050        private void addToSubclassMap(MetaDef.Element elem, TypeInfo info)
1051            throws XOMException
1052        {
1053            // Add to all superclasses as well
1054            for (int j = 0; j < info.superInfos.length; j++) {
1055                TypeInfo superInfo = info.superInfos[j];
1056    
1057                // Add the element to this class's vector.
1058                Vector vec = (Vector)(subclassMap.get(superInfo.name));
1059                if (vec == null) {
1060                    throw new XOMException("Class " + superInfo.name +
1061                                           " of element " + elem.type
1062                                           + " is not defined.");
1063                }
1064                vec.addElement(elem);
1065    
1066                addToSubclassMap(elem, superInfo);
1067            }
1068        }
1069    
1070        /**
1071         * Create all files associated with the metamodel, including a Java class
1072         * and a DTD file.  The DTD is primarily for reference--it will not work
1073         * if any advanced features (plugins, includes) are used.
1074         * @param outputDirName the output directory in which to generate the files.
1075         */
1076        public void writeFiles(String outputDirName, String dtdFileName)
1077            throws XOMException, IOException
1078        {
1079            // Compute the output file names
1080            if (dtdFileName != null) {
1081                if (model.dtdName == null) {
1082                    model.dtdName = dtdFileName;
1083                } else {
1084                    if (!dtdFileName.equals(model.dtdName)) {
1085                        throw new XOMException(
1086                            "dtdFileName parameter (" + dtdFileName +
1087                            ") is inconsistent with model's dtdName " +
1088                            "attribute (" + model.dtdName + ")");
1089                    }
1090                }
1091            }
1092            File javaOutputDir = new File(outputDirName);
1093    
1094            if (!testMode &&
1095                model.packageName != null &&
1096                !model.packageName.equals("")) {
1097                javaOutputDir = new File(
1098                        javaOutputDir, model.packageName.replace('.',fileSep));
1099            }
1100            File javaFile = new File(javaOutputDir, model.className + ".java");
1101            File outputDir = javaFile.getParentFile();
1102            File dtdFile = new File(outputDir, model.dtdName);
1103    
1104            // If the output file is MetaDef.java, and we start writing to
1105            // MetaDef.java before we have loaded MetaDef.class, the system thinks
1106            // that the class is out of date.  So load MetaDef.class before that
1107            // point.
1108            XOMUtil.discard(new MetaDef());
1109    
1110            // Create directories if necessary.
1111            outputDir.mkdir();
1112    
1113            // Open the files for writing
1114            FileWriter dtdWriter = new FileWriter(dtdFile);
1115            PrintWriter dtdOut = new PrintWriter(dtdWriter);
1116            FileWriter javaWriter = new FileWriter(javaFile);
1117            PrintWriter javaOut = new PrintWriter(javaWriter);
1118    
1119            if (!testMode) {
1120                System.out.println("Writing " + dtdFile);
1121            }
1122            writeDtd(dtdOut);
1123            dtdOut.flush();
1124            dtdWriter.close();
1125    
1126            if (!testMode) {
1127                System.out.println("Writing " + javaFile);
1128            }
1129            writeJava(javaOut);
1130            javaOut.flush();
1131            javaWriter.close();
1132    
1133            if (!testMode) {
1134                System.out.println("Done");
1135            }
1136        }
1137    
1138        public void writeDtd(PrintWriter out)
1139            throws XOMException
1140        {
1141            // Write header information for the dtd
1142            out.println("<!--");
1143            out.println("     This dtd file was automatically generated from "
1144                      + "XOM model " + model.name + ".");
1145            out.println("     Do not edit this file by hand.");
1146            out.println("  -->");
1147            out.println();
1148    
1149            // Write toplevel documentation here
1150            writeDtdDoc(out, model.doc);
1151    
1152            // For each CLASS definition, write an entity definition.  These must
1153            // be done before regular elements because entities must be defined
1154            // before use.
1155            for (int i = 0; i < model.elements.length; i++) {
1156                if (model.elements[i] instanceof MetaDef.Class) {
1157                    writeDtdEntity(out, (MetaDef.Class)(model.elements[i]));
1158                }
1159            }
1160    
1161            // Write each element in turn
1162            for (int i = 0; i < model.elements.length; i++) {
1163                writeDtdElement(out, model.elements[i]);
1164            }
1165        }
1166    
1167        public void writeJava(PrintWriter out)
1168            throws XOMException
1169        {
1170            // Write header information for the java file
1171            out.println("/" + "*");
1172            out.println("/" + "/ This java file was automatically generated");
1173            out.println("/" + "/ from XOM model '" + model.name + "'");
1174            if (!testMode) {
1175                out.println("/" + "/ on " + new Date().toString());
1176            }
1177            out.println("/" + "/ Do not edit this file by hand.");
1178            out.println("*" + "/");
1179            out.println();
1180    
1181            if (!testMode &&
1182                !(model.packageName == null || model.packageName.equals(""))) {
1183                out.println("package " + model.packageName + ";");
1184            }
1185            if (!testMode &&
1186                !(model.importName == null || model.importName.equals(""))) {
1187                // generate import statements (separated by : when more than one)
1188                int colonLoc = model.importName.indexOf(":");
1189                int start = 0;
1190                while (colonLoc != -1) {
1191                    out.println("import " + model.importName.substring(start, colonLoc) + ";");
1192                    start = colonLoc + 1;
1193                    colonLoc = model.importName.indexOf(":", start);
1194                }
1195                out.println("import " + model.importName.substring(start) + ";");
1196            }
1197    
1198            // Write the toplevel documentation for the package.  This becomes
1199            // the toplevel documentation for the class and is also placed at
1200            // the top of the Dtd.
1201            String extraDoc = newLine + "<p>This class was generated from XOM model '"
1202                + model.name + "' on " + new Date().toString();
1203            if (testMode) {
1204                extraDoc = "";
1205            }
1206            writeJavaDoc(out, 0, model.doc + extraDoc);
1207    
1208            // Begin the class.  Include a getXMLDefClass() function which
1209            // simply returns this class.
1210            out.println("public class " + model.className + " {");
1211            out.println();
1212            out.println("\tpublic static java.lang.Class getXMLDefClass()");
1213            out.println("\t{");
1214            out.println("\t\treturn " + model.className + ".class;");
1215            out.println("\t}");
1216            out.println();
1217    
1218            // Create a static member that names all Elements that may be
1219            // used within this class.
1220            out.println("\tpublic static String[] _elements = {");
1221            for (int i = 0; i < allTypes.size(); i++) {
1222                String type = (String) allTypes.elementAt(i);
1223                out.print("\t\t\"" + type + "\"");
1224                if (i < allTypes.size() - 1) {
1225                    out.println(",");
1226                } else {
1227                    out.println();
1228                }
1229            }
1230            out.println("\t};");
1231            out.println();
1232    
1233            // Create an inner class for each Class/Object definition.
1234            for (int i = 0; i < model.elements.length; i++) {
1235                writeJavaElement(out, model.elements[i]);
1236            }
1237    
1238            // End the class
1239            out.println();
1240            out.println("}");
1241        }
1242    
1243        /**
1244         * Writes an entity definition based on a defined Class.  Because entity
1245         * definitions must appear before use in a DTD, this function must be
1246         * called for each defined class before processing the rest of the model.
1247         * @param out PrintWriter to write the DTD.
1248         * @param _class Class definition on which the Entity will be based.
1249         */
1250        private void writeDtdEntity(PrintWriter out, MetaDef.Class _class)
1251        {
1252            // Documentation first
1253            if (_class.doc != null) {
1254                writeDtdDoc(out, _class.doc);
1255            }
1256    
1257            // Lookup the subclass vector for this class.  Use this to generate
1258            // the entity definition.
1259            Vector subclassVec = (Vector)(subclassMap.get(_class._class));
1260            out.print("<!ENTITY % " + _class._class + " \"");
1261            if (subclassVec == null) {
1262                throw new AssertFailure(
1263                    "Missing subclass vector for class " + _class._class);
1264            }
1265    
1266            for (int i = 0; i < subclassVec.size(); i++) {
1267                MetaDef.Element elem =
1268                    (MetaDef.Element)(subclassVec.elementAt(i));
1269    
1270                // Print the dtd version of the element name
1271                if (elem.dtdName != null) {
1272                    out.print(elem.dtdName);
1273                } else {
1274                    out.print(prefix + elem.type);
1275                }
1276                if (i < subclassVec.size() - 1) {
1277                    out.print("|");
1278                }
1279            }
1280            out.println("\">");
1281            out.println();
1282        }
1283    
1284        private void writeDtdElement(PrintWriter out, MetaDef.Definition elt)
1285            throws XOMException
1286        {
1287            // What is written into the dtd depends on the class of elt.
1288            if (elt instanceof MetaDef.Element) {
1289                // Get the info class for this element.
1290                MetaDef.Element element = (MetaDef.Element)elt;
1291                TypeInfo info = getTypeInfo(element.type, false);
1292                if (info == null) {
1293                    throw new AssertFailure(
1294                        "Element type " + element.type + " is missing from the "
1295                        + "type map.");
1296                }
1297    
1298                // Documentation first
1299                if (element.doc != null) {
1300                    writeDtdDoc(out, element.doc);
1301                }
1302    
1303                // Then content model.  Special case empty models.
1304                out.print("<!ELEMENT " + info.tagName + " ");
1305                if (info.allContent.length == 0 && !info.isAny && !info.isCData) {
1306                    out.print("EMPTY");
1307                } else {
1308                    if (info.isAny) {
1309                        out.print("ANY");
1310                    } else if (info.isCData) {
1311                        out.print("(#PCDATA)");
1312                    } else {
1313                        out.print("(");
1314                        for (int i = 0; i < info.allContent.length; i++) {
1315                            writeDtdContent(out, info.allContent[i]);
1316                            if (i < info.allContent.length - 1) {
1317                                out.print(",");
1318                            }
1319                        }
1320                        out.print(")");
1321                    }
1322                }
1323                out.println(">");
1324    
1325                // Finally, attribute list
1326                if (info.allAttributes.length > 0) {
1327                    out.println("<!ATTLIST " + info.tagName);
1328                    for (int i = 0; i < info.allAttributes.length; i++) {
1329                        writeDtdAttribute(out, info.allAttributes[i]);
1330                    }
1331                    out.println(">");
1332                }
1333    
1334                // Finish with a blank
1335                out.println();
1336            } else if (elt instanceof MetaDef.Class) {
1337                // Do nothing--entities are handled ahead of time.
1338            } else if (elt instanceof MetaDef.StringElement) {
1339                // Get the info class for this element.
1340                MetaDef.StringElement element = (MetaDef.StringElement)elt;
1341                TypeInfo info = (TypeInfo)(infoMap.get(element.type));
1342                if (info == null) {
1343                    throw new AssertFailure(
1344                        "StringElement type " + element.type +
1345                        " is missing from the type map.");
1346                }
1347    
1348                // Documentation first
1349                if (element.doc != null) {
1350                    writeDtdDoc(out, element.doc);
1351                }
1352    
1353                // Then content model.  It is always (#PCDATA).
1354                out.println("<!ELEMENT " + info.tagName + " (#PCDATA)>");
1355                out.println();
1356            } else if (elt instanceof MetaDef.Plugin) {
1357                // Get the info class for this element.
1358                MetaDef.Plugin plugin = (MetaDef.Plugin)elt;
1359                TypeInfo info = (TypeInfo)(infoMap.get(plugin.type));
1360                if (info == null) {
1361                    throw new AssertFailure(
1362                        "Plugin element " + plugin.type +
1363                        " is missing from the type map.");
1364                }
1365    
1366                // Documentation first
1367                if (plugin.doc != null) {
1368                    writeDtdDoc(out, plugin.doc);
1369                }
1370    
1371                // Then content model.  It is always ANY.
1372                out.println("<!ELEMENT " + info.tagName + " ANY>");
1373    
1374                // Finally, attribute list.  Don't allow use of plugin reserved
1375                // attributes defPackage and defClass.
1376                out.println("<!ATTLIST " + info.tagName);
1377                for (int i = 0; i < info.allAttributes.length; i++) {
1378                    if (info.allAttributes[i].name.equals("defPackage") ||
1379                       info.allAttributes[i].name.equals("defClass"))
1380                        throw new XOMException(
1381                            "The attribute \"" + info.allAttributes[i].name
1382                            + "\" is reserved and may not be redefined in "
1383                            + "or inherited by a Plugin.");
1384                    writeDtdAttribute(out, info.allAttributes[i]);
1385                }
1386    
1387                // Add attribute definitions for defPackage and defClass
1388                out.println("defPackage CDATA \"org.eigenbase.xom\"");
1389                out.println("defClass CDATA #REQUIRED");
1390    
1391                // Complete the attribute list
1392                out.println(">");
1393                out.println();
1394            } else if (elt instanceof MetaDef.Import) {
1395                // Get the info class for this element.
1396                MetaDef.Import imp = (MetaDef.Import)elt;
1397                TypeInfo info = getTypeInfo(imp.type, true);
1398    
1399                // Imports can't really be handled, so just generate a placeholder
1400                // ANY element for show.
1401                out.println("<!ELEMENT " + info.name + " ANY>");
1402                out.println();
1403            } else {
1404                throw new XOMException("Unrecognized element type definition: "
1405                                          + elt.getClass().getName());
1406            }
1407        }
1408    
1409        private void writeDtdDoc(PrintWriter out, String doc)
1410        {
1411            out.println("<!--");
1412    
1413            // Process the String line-by-line.  Trim whitespace from each
1414            // line and ignore fully blank lines.
1415            try {
1416                LineNumberReader reader = new LineNumberReader(new StringReader(doc));
1417                String line;
1418                while ((line = reader.readLine()) != null) {
1419                    String trimLine = line.trim();
1420                    if (!trimLine.equals("")) {
1421                        out.print("     ");
1422                        out.println(trimLine);
1423                    }
1424                }
1425            } catch (IOException ex) {
1426                throw new AssertFailure(ex);
1427            }
1428    
1429            out.println("  -->");
1430        }
1431    
1432        private void writeJavaDoc(PrintWriter out, int indent, String doc)
1433        {
1434            for (int i = 0; i < indent; i++) {
1435                out.print("\t");
1436            }
1437            out.println("/" + "**");
1438    
1439            // Process the String line-by-line.  Trim whitespace from each
1440            // line and ignore fully blank lines.
1441            try {
1442                LineNumberReader reader = new LineNumberReader(new StringReader(doc));
1443                String line;
1444                while ((line = reader.readLine()) != null) {
1445                    String trimLine = line.trim();
1446                    if (!trimLine.equals("")) {
1447                        for (int i = 0; i < indent; i++) {
1448                            out.print("\t");
1449                        }
1450                        out.print(" * ");
1451                        out.println(trimLine);
1452                    }
1453                }
1454            } catch (IOException ex) {
1455                throw new AssertFailure(ex);
1456            }
1457    
1458            for (int i = 0; i < indent; i++) {
1459                out.print("\t");
1460            }
1461            out.println(" *" + "/");
1462        }
1463    
1464        private void writeJavaCode(PrintWriter out, int indent, String code)
1465        {
1466            for (int i = 0; i < indent; i++) {
1467                out.print("\t");
1468            }
1469            out.println("/" + "/ BEGIN pass-through code block ---");
1470    
1471            // Process the String line-by-line.  Don't trim lines--just echo
1472            try {
1473                LineNumberReader reader = new LineNumberReader(new StringReader(code));
1474                String line;
1475                while ((line = reader.readLine()) != null) {
1476                    out.println(line);
1477                }
1478            } catch (IOException ex) {
1479                throw new AssertFailure(ex);
1480            }
1481    
1482            for (int i = 0; i < indent; i++) {
1483                out.print("\t");
1484            }
1485            out.println("/" + "/ END pass-through code block ---");
1486        }
1487    
1488        private MetaDef.Definition getType(String name)
1489            throws XOMException
1490        {
1491            // The type mapping hash table maps element type names to their
1492            // MetaDef.Definition objects.  First, look up the element type associated
1493            // with the name.
1494            MetaDef.Definition type = (MetaDef.Definition) typeMap.get(name);
1495            if (type == null) {
1496                throw new XOMException(
1497                    "Element type name " + name + " was never defined.");
1498            }
1499            return type;
1500        }
1501    
1502        /**
1503         * Deterimines if a name conflicts with a Java keyword.  If so, it returns
1504         * an alternate form of the name (typically the same name with an
1505         * underscore preprended).
1506         * @param name a name to be used in a Java program.
1507         * @return a safe form of the name; either the name itself or a modified
1508         * version if the name is a keyword.
1509         */
1510        private String getDeclaredName(String name)
1511        {
1512            String mappedName = (String) keywordMap.get(name);
1513            if (mappedName == null) {
1514                return name;
1515            } else {
1516                return mappedName;
1517            }
1518        }
1519    
1520        private void writeDtdContent(PrintWriter out, MetaDef.Content content)
1521            throws XOMException
1522        {
1523            if (content instanceof MetaDef.Object) {
1524                MetaDef.Object obj = (MetaDef.Object)content;
1525                TypeInfo info = (TypeInfo)(infoMap.get(obj.type));
1526                if (info == null) {
1527                    throw new XOMException(
1528                        "Object " + obj.name + " has undefined type "
1529                        + obj.type);
1530                }
1531                out.print(info.tagName);
1532                if (!obj.required.booleanValue()) {
1533                    out.print("?");
1534                }
1535            } else if (content instanceof MetaDef.Array) {
1536                MetaDef.Array array = (MetaDef.Array)content;
1537                TypeInfo info = (TypeInfo)(infoMap.get(array.type));
1538                if (info == null) {
1539                    throw new XOMException(
1540                        "Array " + array.name + " has undefined type "
1541                        + array.type);
1542                }
1543                out.print("(" + info.tagName + ")");
1544                if (array.min.intValue() > 0) {
1545                    out.print("+");
1546                } else {
1547                    out.print("*");
1548                }
1549            } else {
1550                throw new XOMException("Unrecognized content type definition: "
1551                                          + content.getClass().getName());
1552            }
1553        }
1554    
1555        private void writeDtdAttribute(PrintWriter out, MetaDef.Attribute attr)
1556        {
1557            // Attribute name
1558            out.print(attr.name + " ");
1559    
1560            // Values, or CDATA if unspecified
1561            if (attr.values == null || attr.values.length == 0) {
1562                if (attr.type.equalsIgnoreCase("Boolean")) {
1563                    out.print("(true|false) ");
1564                } else {
1565                    out.print("CDATA ");
1566                }
1567            } else {
1568                out.print("(");
1569                for (int i = 0; i < attr.values.length; i++) {
1570                    out.print(attr.values[i]);
1571                    if (i < attr.values.length - 1) {
1572                        out.print("|");
1573                    }
1574                }
1575                out.print(") ");
1576            }
1577    
1578            // Default value
1579            if (attr._default == null) {
1580                if (attr.required.booleanValue()) {
1581                    out.println("#REQUIRED");
1582                } else {
1583                    out.println("#IMPLIED");
1584                }
1585            } else {
1586                out.print("\"" + attr._default + "\"");
1587                out.println();
1588            }
1589        }
1590    
1591        /**
1592         * This helper function returns true if any member of the given content
1593         * array is of the specified type.
1594         * @param content an array of content descriptors.
1595         * @param match a Class describing the class to match.
1596         * @return true if any member of the given content array matches
1597         * the given match type.
1598         */
1599        private static boolean hasContentType(MetaDef.Content[] content,
1600                                              Class match)
1601        {
1602            for (int i = 0; i < content.length; i++) {
1603                if (content[i].getClass() == match) {
1604                    return true;
1605                }
1606            }
1607            return false;
1608        }
1609    
1610        private void writeJavaElement(PrintWriter out, MetaDef.Definition elt)
1611            throws XOMException
1612        {
1613            // What is written into the dtd depends on the class of elt.
1614            if (elt instanceof MetaDef.Element) {
1615                MetaDef.Element element = (MetaDef.Element)elt;
1616                TypeInfo info = (TypeInfo)(infoMap.get(element.type));
1617                if (info == null) {
1618                    throw new XOMException(
1619                        "Element type " + element.type + " was never defined.");
1620                }
1621                info.writeJavaClass(out);
1622            } else if (elt instanceof MetaDef.Plugin) {
1623                MetaDef.Plugin plugin = (MetaDef.Plugin)elt;
1624                TypeInfo info = (TypeInfo)(infoMap.get(plugin.type));
1625                if (info == null) {
1626                    throw new XOMException(
1627                        "Plugin type " + plugin.type + " was never defined.");
1628                }
1629                info.writeJavaClass(out);
1630            } else if (elt instanceof MetaDef.Class) {
1631                MetaDef.Class _class = (MetaDef.Class)elt;
1632                TypeInfo info = (TypeInfo)(infoMap.get(_class._class));
1633                if (info == null) {
1634                    throw new XOMException(
1635                        "Class type " + _class._class + " was never defined.");
1636                }
1637                info.writeJavaClass(out);
1638            } else if (elt instanceof MetaDef.StringElement) {
1639                // Documentation first
1640                MetaDef.StringElement element = (MetaDef.StringElement)elt;
1641                if (element.doc != null) {
1642                    writeJavaDoc(out, 1, element.doc);
1643                }
1644    
1645                // Declare the name as a constant
1646                out.println("\tpublic static final String "
1647                            + element.type + " = \""
1648                            + element.type + "\";");
1649                out.println();
1650            } else if (elt instanceof MetaDef.Import) {
1651                // Do nothing--imports are handled inline
1652            } else {
1653                throw new XOMException("Unrecognized element type definition: "
1654                                          + elt.getClass().getName());
1655            }
1656        }
1657    
1658        public void writeJavaGetAttribute(PrintWriter out,
1659                                          MetaDef.Attribute attr)
1660            throws XOMException
1661        {
1662            out.print("\t\t\t\t" + getDeclaredName(attr.name) + " = ");
1663            out.print("(" + attr.type + ")_parser.getAttribute(");
1664            out.print("\"" + attr.name + "\", \"" + attr.type + "\", ");
1665            if (attr._default == null) {
1666                out.print("null, ");
1667            } else {
1668                out.print("\"" + attr._default + "\", ");
1669            }
1670            if (attr.values == null || attr.values.length == 0) {
1671                out.print("null, ");
1672            } else {
1673                out.print("_" + getDeclaredName(attr.name)
1674                          + "_values, ");
1675            }
1676            if (attr.required.booleanValue()) {
1677                out.print("true");
1678            } else {
1679                out.print("false");
1680            }
1681            out.println(");");
1682        }
1683    
1684        public void writeJavaDeclareAttribute(PrintWriter out,
1685                                              MetaDef.Attribute attr)
1686            throws XOMException
1687        {
1688            // Setup an array for attribute values if required
1689            if (attr.values != null && attr.values.length > 0) {
1690                out.println("\t\t/** Allowable values for {@link #"
1691                        + getDeclaredName(attr.name) + "}. */");
1692                out.print("\t\tpublic static final String[] _"
1693                        + getDeclaredName(attr.name) + "_values = {");
1694                for (int i = 0; i < attr.values.length; i++) {
1695                    out.print("\"" + attr.values[i] + "\"");
1696                    if (i < attr.values.length - 1) {
1697                        out.print(", ");
1698                    }
1699                }
1700                out.println("};");
1701            }
1702    
1703            // Generate the declaration, including a quick comment
1704            out.print("\t\tpublic " + attr.type + " "
1705                      + getDeclaredName(attr.name) + ";  /" + "/ ");
1706            if (attr._default != null) {
1707                out.print("attribute default: " + attr._default);
1708            } else if (attr.required.booleanValue()) {
1709                out.print("required attribute");
1710            } else {
1711                out.print("optional attribute");
1712            }
1713            out.println();
1714        }
1715    
1716        public void writeJavaDisplayAttribute(PrintWriter out,
1717                                              MetaDef.Attribute attr)
1718            throws XOMException
1719        {
1720            // Generate the display line
1721            out.println("\t\t\tdisplayAttribute(_out, \"" + attr.name + "\", "
1722                        + getDeclaredName(attr.name) + ", _indent+1);");
1723        }
1724    
1725        public void writeJavaDisplayXMLAttribute(PrintWriter out,
1726                                                 MetaDef.Attribute attr)
1727            throws XOMException
1728        {
1729            out.println("\t\t\t\t.add(\"" + attr.name
1730                        + "\", " + getDeclaredName(attr.name) + ")");
1731        }
1732    
1733        public void writeJavaDisplayDiffAttribute(
1734            PrintWriter out,
1735            int[] diffCount, MetaDef.Attribute attr)
1736            throws XOMException
1737        {
1738            out.println("\t\t\t" + prefix(diffCount) + "displayAttributeDiff(\"" + attr.name
1739                        + "\", " + getDeclaredName(attr.name)
1740                        + ", _cother." + getDeclaredName(attr.name)
1741                        + ", _out, _indent+1);");
1742        }
1743    
1744        public void writeJavaGetContent(PrintWriter out,
1745                                        MetaDef.Content content)
1746            throws XOMException
1747        {
1748            if (content instanceof MetaDef.Object) {
1749                // Get the object and its type
1750                MetaDef.Object obj = (MetaDef.Object)content;
1751                MetaDef.Definition type = getType(obj.type);
1752                TypeInfo info = getTypeInfo(obj.type, true);
1753    
1754                out.print("\t\t\t\t"
1755                          + getDeclaredName(obj.name) + " = ");
1756    
1757                // Behavior depends on the type
1758                if (type != null && type instanceof MetaDef.Import) {
1759                    // Get the info object for the import
1760                    info = getTypeInfo(((MetaDef.Import)type).type, true);
1761    
1762                    // Call the class constructor directly.
1763                    out.print("(" + info.impName + ")_parser.getElement(");
1764                    out.print(info.impName + ".class, ");
1765                } else if (type != null && type instanceof MetaDef.StringElement) {
1766                    out.print("_parser.getString(" + info.className + ", ");
1767                } else {
1768                    out.print("(" + info.className + ")_parser.getElement(");
1769                    out.print(info.className + ".class, ");
1770                }
1771    
1772                if (obj.required.booleanValue()) {
1773                    out.print("true");
1774                } else {
1775                    out.print("false");
1776                }
1777                out.println(");");
1778            } else if (content instanceof MetaDef.Array) {
1779                // Get the object and its type
1780                MetaDef.Array array = (MetaDef.Array)content;
1781                MetaDef.Definition type = getType(array.type);
1782                String typeName = getTypeInfo(array.type, true).className;
1783    
1784                if (type instanceof MetaDef.Import) {
1785                    // Get the info object for the import
1786                    TypeInfo info = getTypeInfo(((MetaDef.Import)type).type, true);
1787    
1788                    // Construct the array
1789                    out.print("\t\t\t\t_tempArray = _parser.getArray(");
1790                    out.print(info.impName + ".class, ");
1791                    out.println(array.min + ", " + array.max + ");");
1792                    out.println("\t\t\t\t"
1793                                + getDeclaredName(array.name)
1794                                + " = new " + info.impName + "[_tempArray.length];");
1795                    out.println("\t\t\t\tfor (int _i = 0; _i < "
1796                                + getDeclaredName(array.name)
1797                                + ".length; _i++)");
1798                    out.println("\t\t\t\t\t" + getDeclaredName(array.name) + "[_i] = "
1799                                + "(" + typeName + ")_tempArray[_i];");
1800                } else if (type instanceof MetaDef.StringElement) {
1801                    out.print("\t\t\t\t" + getDeclaredName(array.name)
1802                              + " = _parser.getStringArray(");
1803                    out.println("\"" + typeName + "\", " + array.min
1804                                + ", " + array.max + ");");
1805                } else {
1806                    out.print("\t\t\t\t_tempArray = _parser.getArray(");
1807                    out.print(typeName + ".class, ");
1808                    out.println(array.min + ", " + array.max + ");");
1809                    out.println("\t\t\t\t"
1810                                + getDeclaredName(array.name)
1811                                + " = new " + typeName + "[_tempArray.length];");
1812                    out.println("\t\t\t\tfor (int _i = 0; _i < "
1813                                + getDeclaredName(array.name)
1814                                + ".length; _i++)");
1815                    out.println("\t\t\t\t\t" + getDeclaredName(array.name) + "[_i] = "
1816                                + "(" + typeName + ")_tempArray[_i];");
1817                }
1818            } else {
1819                throw new XOMException("Unrecognized content type definition: "
1820                                          + content.getClass().getName());
1821            }
1822        }
1823    
1824        public void writeJavaGetAnyContent(PrintWriter out, boolean mixed)
1825        {
1826            if (mixed) {
1827                out.println("\t\t\t\tchildren = getMixedChildren(" +
1828                            "_def, " +
1829                            model.className + ".class, " +
1830                            "\"" + prefix + "\");");
1831            } else {
1832                out.println("\t\t\t\tchildren = getElementChildren(" +
1833                            "_def, " +
1834                            model.className + ".class, " +
1835                            "\"" + prefix + "\");");
1836            }
1837        }
1838    
1839        public void writeJavaGetCDataContent(PrintWriter out)
1840        {
1841            out.println("\t\t\t\tcdata = _parser.getText();");
1842        }
1843    
1844        public void writeJavaDeclareContent(PrintWriter out,
1845                                            MetaDef.Content content)
1846            throws XOMException
1847        {
1848            if (content instanceof MetaDef.Object) {
1849                // Write documentation (if any)
1850                MetaDef.Object obj = (MetaDef.Object)content;
1851                if (obj.doc != null) {
1852                    writeJavaDoc(out, 2, obj.doc);
1853                }
1854    
1855                // Handle includes
1856                MetaDef.Definition type = getType(obj.type);
1857                String typeName = getTypeInfo(obj.type, true).className;
1858    
1859                // Write content declaration.
1860                if (type instanceof MetaDef.Import) {
1861                    // Get the info object for the import
1862                    TypeInfo info = getTypeInfo(((MetaDef.Import)type).type, true);
1863                    typeName = info.impName;
1864                    out.print("\t\tpublic " + typeName + " "
1865                              + getDeclaredName(obj.name) + ";  /" + "/");
1866                } else if (type instanceof MetaDef.StringElement) {
1867                    out.print("\t\tpublic String "
1868                              + getDeclaredName(obj.name) + ";  /" + "/");
1869                } else {
1870                    out.print("\t\tpublic " + typeName + " "
1871                              + getDeclaredName(obj.name) + ";  /" + "/");
1872                }
1873                // Write a brief comment.
1874                if (obj.required.booleanValue()) {
1875                    out.println("required element");
1876                } else {
1877                    out.println("optional element");
1878                }
1879            } else if (content instanceof MetaDef.Array) {
1880                // Write documentation (if any)
1881                MetaDef.Array array = (MetaDef.Array)content;
1882                if (array.doc != null) {
1883                    writeJavaDoc(out, 2, array.doc);
1884                }
1885    
1886                MetaDef.Definition type = getType(array.type);
1887                String typeName = getTypeInfo(array.type, true).className;
1888    
1889                // Write content declaration.
1890                if (type instanceof MetaDef.Import) {
1891                    // Get the info object for the import
1892                    TypeInfo info = getTypeInfo(((MetaDef.Import)type).type, true);
1893                    typeName = info.impName;
1894                    out.print("\t\tpublic " + typeName + "[] "
1895                              + getDeclaredName(array.name) + ";  /" + "/");
1896                } else if (type instanceof MetaDef.StringElement) {
1897                    out.print("\t\tpublic String[] "
1898                              + getDeclaredName(array.name) + ";  /" + "/");
1899                } else {
1900                    out.print("\t\tpublic " + typeName + "[] "
1901                              + getDeclaredName(array.name) + ";  /" + "/");
1902                }
1903                // Write a brief comment.
1904                if (array.min.intValue() <= 0 &&
1905                   array.max.intValue() <= 0) {
1906                    out.println("optional array");
1907                } else {
1908                    if (array.min.intValue() > 0) {
1909                        out.print("min " + array.min);
1910                    }
1911                    if (array.max.intValue() > 0) {
1912                        out.print("max " + array.max);
1913                    }
1914                    out.println();
1915                }
1916            } else {
1917                throw new XOMException("Unrecognized content type definition: "
1918                                          + content.getClass().getName());
1919            }
1920        }
1921    
1922        public void writeJavaDeclareAnyContent(PrintWriter out, boolean mixed)
1923        {
1924            out.println("\t\tpublic org.eigenbase.xom." +
1925                        (mixed ? "NodeDef" : "ElementDef") +
1926                        "[] children;  /" + "/holder for variable-type children");
1927            out.println("\t\t// implement Any");
1928            out.println("\t\tpublic org.eigenbase.xom.NodeDef[] getChildren()");
1929            out.println("\t\t{");
1930            out.println("\t\t\treturn children;");
1931            out.println("\t\t}");
1932            out.println("\t\t// implement Any");
1933            out.println("\t\tpublic void setChildren(org.eigenbase.xom.NodeDef[] children)");
1934            out.println("\t\t{");
1935            out.println("\t\t\tthis.children = " +
1936                        (mixed ? "" : "(org.eigenbase.xom.ElementDef[]) ") +
1937                        "children;");
1938            out.println("\t\t}");
1939        }
1940    
1941        public void writeJavaDeclareCDataContent(PrintWriter out)
1942        {
1943            out.print("\t\tpublic String cdata;  /"
1944                      + "/ All text goes here");
1945        }
1946    
1947        public void writeJavaDisplayContent(PrintWriter out,
1948                                            MetaDef.Content content)
1949            throws XOMException
1950        {
1951            if (content instanceof MetaDef.Object) {
1952                MetaDef.Object obj = (MetaDef.Object)content;
1953                MetaDef.Definition type = getType(obj.type);
1954    
1955                if (type instanceof MetaDef.StringElement) {
1956                    out.println("\t\t\tdisplayString(_out, \""
1957                                + obj.name + "\", " + getDeclaredName(obj.name)
1958                                + ", _indent+1);");
1959                } else {
1960                    out.println("\t\t\tdisplayElement(_out, \""
1961                                + obj.name + "\", (org.eigenbase.xom.ElementDef) "
1962                                + getDeclaredName(obj.name)
1963                                + ", _indent+1);");
1964                }
1965            } else if (content instanceof MetaDef.Array) {
1966                MetaDef.Array array = (MetaDef.Array)content;
1967                MetaDef.Definition type = getType(array.type);
1968    
1969                if (type instanceof MetaDef.StringElement) {
1970                    out.println("\t\t\tdisplayStringArray(_out, \""
1971                                + array.name + "\", " + getDeclaredName(array.name)
1972                                + ", _indent+1);");
1973                } else {
1974                    out.println("\t\t\tdisplayElementArray(_out, \""
1975                                + array.name + "\", " + getDeclaredName(array.name)
1976                                + ", _indent+1);");
1977                }
1978            } else {
1979                throw new XOMException("Unrecognized content type definition: "
1980                                          + content.getClass().getName());
1981            }
1982        }
1983    
1984        public void writeJavaDisplayAnyContent(PrintWriter out)
1985        {
1986            // Display the fixed children array
1987            out.println("\t\t\tdisplayElementArray(_out, \"children\""
1988                        + ", children, _indent+1);");
1989        }
1990    
1991        public void writeJavaDisplayCDataContent(PrintWriter out)
1992        {
1993            // Display the text as "cdata"
1994            out.println("\t\t\tdisplayString(_out, \"cdata\", "
1995                        + "cdata, _indent+1);");
1996        }
1997    
1998        public void writeJavaDisplayXMLContent(PrintWriter out,
1999                                               MetaDef.Content content)
2000            throws XOMException
2001        {
2002            if (content instanceof MetaDef.Object) {
2003                MetaDef.Object obj = (MetaDef.Object)content;
2004                MetaDef.Definition type = getType(obj.type);
2005    
2006                if (type instanceof MetaDef.StringElement) {
2007                    out.println("\t\t\tdisplayXMLString(_out, \""
2008                                + getTypeInfo(obj.type, true).tagName + "\", "
2009                                + getDeclaredName(obj.name) + ");");
2010                } else {
2011                    out.println("\t\t\tdisplayXMLElement(_out,"
2012                                + " (org.eigenbase.xom.ElementDef) "
2013                                + getDeclaredName(obj.name) + ");");
2014                }
2015            } else if (content instanceof MetaDef.Array) {
2016                MetaDef.Array array = (MetaDef.Array)content;
2017                MetaDef.Definition type = getType(array.type);
2018    
2019                if (type instanceof MetaDef.StringElement) {
2020                    out.println("\t\t\tdisplayXMLStringArray(_out, \""
2021                                + getTypeInfo(array.type, true).tagName + "\", "
2022                                + getDeclaredName(array.name) + ");");
2023                } else {
2024                    out.println("\t\t\tdisplayXMLElementArray(_out, "
2025                                + getDeclaredName(array.name) + ");");
2026                }
2027            } else if (content instanceof MetaDef.Any) {
2028                // Display the fixed children array
2029                out.println("\t\t\tdisplayXMLElementArray(_out, children);");
2030            } else if (content instanceof MetaDef.CData) {
2031                // Display the CDATA section
2032                out.println("\t\t\t_out.cdata(cdata);");
2033            } else {
2034                throw new XOMException("Unrecognized content type definition: "
2035                                          + content.getClass().getName());
2036            }
2037        }
2038    
2039        public void writeJavaDisplayXMLAnyContent(PrintWriter out)
2040        {
2041            // Display the fixed children array
2042            out.println("\t\t\tdisplayXMLElementArray(_out, children);");
2043        }
2044    
2045        public void writeJavaDisplayXMLCDataContent(PrintWriter out)
2046        {
2047            // Display the CDATA section
2048            out.println("\t\t\t_out.cdata(cdata);");
2049        }
2050    
2051        public void writeJavaDisplayDiffContent(
2052            PrintWriter out,
2053            int[] diffCount, MetaDef.Content content)
2054            throws XOMException
2055        {
2056            if (content instanceof MetaDef.Object) {
2057                MetaDef.Object obj = (MetaDef.Object)content;
2058                MetaDef.Definition type = getType(obj.type);
2059    
2060                if (type instanceof MetaDef.StringElement) {
2061                    out.println("\t\t\t" + prefix(diffCount) + "displayStringDiff(\""
2062                                + obj.name + "\", "
2063                                + getDeclaredName(obj.name) + ", "
2064                                + "_cother." + getDeclaredName(obj.name) + ", "
2065                                + "_out, _indent+1);");
2066                } else {
2067                    out.println("\t\t\t" + prefix(diffCount) + "displayElementDiff(\""
2068                                + obj.name + "\", "
2069                                + getDeclaredName(obj.name) + ", "
2070                                + "_cother." + getDeclaredName(obj.name) + ", "
2071                                + "_out, _indent+1);");
2072                }
2073            } else if (content instanceof MetaDef.Array) {
2074                MetaDef.Array array = (MetaDef.Array)content;
2075                MetaDef.Definition type = getType(array.type);
2076    
2077                if (type instanceof MetaDef.StringElement) {
2078                    out.println("\t\t\t" + prefix(diffCount) + "displayStringArrayDiff(\""
2079                                + array.name + "\", "
2080                                + getDeclaredName(array.name) + ", "
2081                                + "_cother." + getDeclaredName(array.name) + ", "
2082                                + "_out, _indent+1);");
2083                } else {
2084                    out.println("\t\t\t" + prefix(diffCount) + "displayElementArrayDiff(\""
2085                                + array.name + "\", "
2086                                + getDeclaredName(array.name) + ", "
2087                                + "_cother." + getDeclaredName(array.name) + ", "
2088                                + "_out, _indent+1);");
2089                }
2090            } else {
2091                throw new XOMException("Unrecognized content type definition: "
2092                                          + content.getClass().getName());
2093            }
2094        }
2095    
2096        private String prefix(int[] diffCount) {
2097            if (diffCount[0]++ == 0) {
2098                return "boolean _diff = ";
2099            } else {
2100                return "_diff = _diff && ";
2101            }
2102        }
2103    
2104        public void writeJavaDisplayDiffAnyContent(
2105            PrintWriter out, int[] diffCount)
2106        {
2107            // Display the fixed children array
2108            out.println("\t\t\t" + prefix(diffCount) + "displayElementArrayDiff(\"children\", "
2109                        + "children, _cother.children, _out, _indent+1);");
2110        }
2111    
2112        public void writeJavaDisplayDiffCDataContent(
2113            PrintWriter out, int[] diffCount)
2114        {
2115            out.println("\t\t\t" + prefix(diffCount) + "displayStringDiff(\"cdata\", "
2116                        + "cdata, _cother.cdata, _out, _indent+1);");
2117        }
2118    
2119        public void writeJavaDeclarePluginAttributes(PrintWriter out)
2120        {
2121            writeJavaDoc(out, 2, "defPackage is a built-in attribute "
2122                         + "defining the package of the plugin class.");
2123            out.println("\t\tpublic String defPackage;");
2124            out.println();
2125    
2126            writeJavaDoc(out, 2, "defClass is a built-in attribute "
2127                         + "definition the plugin parser class.");
2128            out.println("\t\tpublic String defClass;");
2129            out.println();
2130        }
2131    
2132        public void writeJavaDisplayPluginAttributes(PrintWriter out)
2133        {
2134            // Generate two display lines
2135            out.println("\t\t\tdisplayAttribute(_out, \"defPackage\", "
2136                        + "defPackage, _indent+1);");
2137            out.println("\t\t\tdisplayAttribute(_out, \"defClass\", "
2138                        + "defClass, _indent+1);");
2139        }
2140    
2141        public void writeJavaDisplayXMLPluginAttributes(PrintWriter out)
2142        {
2143            out.println("\t\t\t\t.add(\"defPackage\", defPackage)");
2144            out.println("\t\t\t\t.add(\"defClass\", defClass)");
2145        }
2146    
2147        public void writeJavaDisplayDiffPluginAttributes(
2148            PrintWriter out, int[] diffCount)
2149        {
2150            out.println("\t\t\t" + prefix(diffCount) + "displayAttributeDiff(\""
2151                        + "defPackage\", defPackage, _cother.defPackage"
2152                        + ", _out, _indent+1);");
2153            out.println("\t\t\t" + prefix(diffCount) + "displayAttributeDiff(\""
2154                        + "defClass\", defClass, _cother.defClass"
2155                        + ", _out, _indent+1);");
2156        }
2157    
2158        public void writeJavaGetPluginContent(PrintWriter out, boolean mixed)
2159        {
2160            if (mixed) {
2161                out.println("\t\t\t\tchildren = getMixedChildren(" +
2162                            "_def, _pluginClass, \"\");");
2163            } else {
2164                out.println("\t\t\t\tchildren = getElementChildren(" +
2165                            "_def, _pluginClass, \"\");");
2166            }
2167        }
2168    
2169        public void writeJavaDeclarePluginContent(PrintWriter out, boolean mixed)
2170        {
2171            out.println("\t\tpublic org.eigenbase.xom." +
2172                        (mixed ? "NodeDef" : "ElementDef") +
2173                        "[] children;  /" + "/holder for variable-type children");
2174        }
2175    
2176        public void writeJavaDisplayPluginContent(PrintWriter out)
2177        {
2178            // Display the fixed children array
2179            out.println("\t\t\tdisplayElementArray(_out, \"children\""
2180                        + ", children, _indent+1);");
2181        }
2182    
2183        public void writeJavaDisplayXMLPluginContent(PrintWriter out)
2184        {
2185            // Display the fixed children array
2186            out.println("\t\t\tdisplayXMLElementArray(_out, children);");
2187        }
2188    
2189        public void writeJavaDisplayDiffPluginContent(
2190            PrintWriter out, int[] diffCount)
2191        {
2192            // Display the fixed children array
2193            out.println("\t\t\t" + prefix(diffCount) + "displayElementArrayDiff(\"children\", "
2194                        + "children, _cother.children, _out, _indent+1);");
2195        }
2196    
2197        /**
2198         * Write the name of the dtd file and java class to standard output.
2199         * This output is used by shell scripts to grab these values.
2200         * The output is only produced in test mode.
2201         */
2202        public void writeOutputs()
2203        {
2204            if (testMode) {
2205                System.out.println(model.dtdName + " " + model.className);
2206            }
2207        }
2208    
2209        /**
2210         * Main function for MetaGenerator. Arguments:
2211         * <ol>
2212         * <li>Name of XML file describing input model.
2213         * <li>Name of output file directory.
2214         * </ol>
2215         */
2216        public static void main(String[] args)
2217        {
2218            int firstArg = 0;
2219            boolean testMode = false;
2220            if (firstArg < args.length && args[firstArg].equals("-debug")) {
2221                System.err.println("MetaGenerator pausing for debugging.  "
2222                                   + "Attach your debugger "
2223                                   + "and press return.");
2224                try {
2225                    System.in.read();
2226                    firstArg++;
2227                } catch (IOException ex) {
2228                    // Do nothing
2229                }
2230            }
2231            if (firstArg < args.length && args[firstArg].equals("-test")) {
2232                System.err.println("Ignoring package name.");
2233                testMode = true;
2234                firstArg++;
2235            }
2236    
2237            if (args.length != 2 + firstArg) {
2238                System.err.println(
2239                    "Usage: java MetaGenerator [-debug] [-test] " +
2240                    "<XML model file> <output directory>");
2241                System.exit(2);
2242            }
2243    
2244            try {
2245                MetaGenerator generator = new MetaGenerator(
2246                    args[0 + firstArg], testMode);
2247                generator.writeFiles(args[1 + firstArg], null);
2248                generator.writeOutputs();
2249            } catch (XOMException ex) {
2250                System.err.println("Generation of model failed:");
2251                System.err.println(ex.toString());
2252                ex.printStackTrace();
2253                System.exit(1);
2254            } catch (IOException ex) {
2255                System.err.println("Generation of model failed:");
2256                System.err.println(ex.toString());
2257                ex.printStackTrace();
2258                System.exit(1);
2259            }
2260        }
2261    
2262        /**
2263         * Display information about this generator for debug purposes.
2264         */
2265        public void debugDisplay()
2266        {
2267            System.out.println("Model:");
2268            System.out.println(model.toString());
2269        }
2270    }
2271    
2272    
2273    // End MetaGenerator.java