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
027package org.eigenbase.xom;
028import java.io.*;
029import 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 **/
037public 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