source: branches/dev-5819/Compiler/GenericCodeGen/src/jastadd/GenericXMLGenerator.jrag @ 13651

Last change on this file since 13651 was 13651, checked in by randersson, 2 months ago

#5819 Merged trunk into branch

File size: 15.4 KB
Line 
1import java.io.PrintStream;
2
3/*
4    Copyright (C) 2013 Modelon AB
5
6    This program is free software: you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation, version 3 of the License.
9
10    This program is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13    GNU General Public License for more details.
14
15    You should have received a copy of the GNU General Public License
16    along with this program.  If not, see <http://www.gnu.org/licenses/>.
17*/
18
19import org.jmodelica.util.XMLUtil;
20
21/**
22 * A generator class containing basic functionality for generating XML files.
23 *
24 * Allows generation without a template file (by passing null as the template).
25 */
26public abstract class GenericXMLGenerator extends GenericGenerator {
27
28    private String indentStep;
29    private int indentation;
30   
31    public interface Creator {
32        public GenericXMLGenerator create(Printer expPrinter, char escapeCharacter, FClass fclass);
33    }
34   
35    /**
36     * Returns the string denoting the beginning of the copyright blurb.
37     */
38    protected String startOfBlurb() { return "<!--"; }
39   
40    /**
41     * Returns the string denoting the end of the copyright blurb.
42     */
43    protected String endOfBlurb() { return "-->"; }
44   
45    /**
46     * Returns the indentation step.
47     */
48    protected String indentStep() { return "\t"; }
49   
50    /**
51     * Constructor.
52     *
53     * @param expPrinter Printer object used to generate code for expressions.
54     * @param escapeCharacter Escape characters used to decode tags.
55     * @param fclass An FClass object used as a basis for the code generation.
56     */
57    public GenericXMLGenerator(Printer expPrinter, char escapeCharacter, FClass fclass) {
58        super(expPrinter,escapeCharacter, fclass);
59       
60        indentStep = indentStep();
61        indentation = 0;
62    }
63
64    /**
65     * See generate(String templateFile, String outputFile).
66     *
67     * In this implementation, if templateReader is null, then no template is used.
68     * Instead, the tag returned by {@link #rootTag()} is generated.
69     *
70     * @param templateReader  a Reader object from which the template file can be used
71     * @param genPrinter      a CodeStream object to which the generated code is written
72     * @param includeDir      directory to look for included files in. If null, includes are not supported
73     */
74    public void generate(Reader templateReader, CodeStream genPrinter, File includeDir) {
75        if (templateReader == null) {
76                generateXMLDeclaration(genPrinter);
77            rootTag().generate(genPrinter);
78               
79        } else {
80            super.generate(templateReader, genPrinter, includeDir);
81        }
82    }
83   
84    /**
85     * Generates the XML Declaration.
86     */
87    protected void generateXMLDeclaration(CodeStream printer) {
88        printer.print("<?xml");
89        generateAttribute("version", getXMLVersion(), printer);
90        generateAttribute("encoding", getXMLEncoding(), printer);
91        printer.print("?>\n");
92    }
93   
94    /**
95     * Get the version for the XML Declaration, defalut implementation returns "1.0".
96     */
97    protected String getXMLVersion() {
98        return "1.0";
99    }
100   
101    /**
102     * Get the encoding for the XML Declaration, defalut implementation returns "UTF-8".
103     */
104    protected String getXMLEncoding() {
105        return "UTF-8";
106    }
107   
108    /**
109     * Get the root tag, used to generate the file if no template file is given.
110     *
111     * Default implementation returns the tag with the name returned by {@link #root()}.
112     */
113    protected AbstractTag rootTag() {
114        return getTag(root());
115    }
116   
117    /**
118     * The name of the root tag to use if no template file is given.
119     */
120    protected abstract String root();
121   
122    /**
123     * Get an array of tags from an array of tag names.
124     */
125    protected AbstractTag[] findTags(String[] names) {
126        AbstractTag[] tags = new AbstractTag[names.length];
127        int i = 0;
128        for (String name : names) 
129            tags[i++] = getTag(name);
130        return tags;
131    }
132   
133    /**
134     * Generate the start of an XML tag.
135     *
136     * @param name     the name of the tag
137     * @param printer  the print stream to use
138     */
139    protected void generateTagStart(String name, CodeStream printer) {
140        indent(printer);
141        printer.print('<');
142        printer.print(name);
143    }
144   
145    /**
146     * Generate the end of an XML opening tag.
147     *
148     * @param printer  the print stream to use
149     */
150    protected void generateOpenEnd(CodeStream printer) {
151        printer.print(">\n");
152        indentation++;
153    }
154   
155    /**
156     * Generates an open XML start tag. This is the same as first calling
157     * generateTagStart() followed by generateOpenEnd().
158     */
159    protected void generateOpenTag(String name, CodeStream printer) {
160        generateTagStart(name, printer);
161        generateOpenEnd(printer);
162    }
163   
164    /**
165     * Generates an one line tag with text contents inside.
166     */
167    protected void generateOneLineTag(String name, Object contents, CodeStream printer) {
168        generateTagStart(name, printer);
169        printer.print(">", XMLUtil.escape(contents.toString()), "</", name, ">\n");
170    }
171   
172    /**
173     * Generate the end of an XML closed tag.
174     *
175     * Closed tags can not contain other tags, and ends with "/>".
176     *
177     * @param printer  the print stream to use
178     */
179    protected void generateClosedEnd(CodeStream printer) {
180        printer.print(" />\n");
181    }
182   
183    /**
184     * Generate an XML closing tag.
185     *
186     * @param name     the name of the tag
187     * @param printer  the print stream to use
188     */
189    protected void generateClosing(String name, CodeStream printer) {
190        indentation--;
191        indent(printer);
192        printer.print("</", name, ">\n");
193    }
194   
195    /**
196     * Generate an XML attribute.
197     *
198     * @param name     the name of the attribute
199     * @param value    the value of the attribute
200     * @param printer  the print stream to use
201     */
202    protected void generateAttribute(String name, Object value, CodeStream printer) {
203        printer.print(" ", name, "=\"", XMLUtil.escape(value.toString()), "\"");
204    }
205   
206    /**
207     * Generate an XML attribute.
208     *
209     * @param name     the name of the attribute
210     * @param variable variable to fetch the value of the attribute from
211     * @param printer  the print stream to use
212     */
213    protected void generateAttributeIfSet(String name, FVariable variable, CodeStream printer) {
214        if (variable.attributeSet(name)) {
215            FExp attributeExp = variable.attributeExp(name);
216            if (attributeExp.variability().constantVariability() ||
217                    variable.isInput() || variable.isDifferentiatedVariable()) {
218                try {
219                    String value = attributeExp.ceval().xmlValue();
220                    generateAttribute(name, value, printer);
221                } catch (ConstantEvaluationException e) {
222                    throw new InternalCompilerError("Exception when generating xml attribute '" +
223                        name + "' for variable '" + variable.name() + "'.", e);
224                }
225            }
226        }
227    }
228   
229    /**
230     * Generate an XML attribute representing a list, the entires will be
231     * space separated.
232     *
233     * @param name     the name of the attribute
234     * @param values   the values of the attribute
235     * @param printer  the print stream to use
236     */
237    protected void generateListAttribute(String name, Iterable<? extends Object> values, CodeStream printer) {
238        printer.print(" ", name, "=\"");
239        boolean first = true;
240        for (Object value : values) {
241            if (!first) {
242                printer.print(" ");
243            }
244            first = false;
245            printer.print(XMLUtil.escape(value.toString()));
246        }
247        printer.print("\"");
248    }
249
250    /**
251     * Generates an XML comment with the provided contents. Special characters
252     * are escaped in the provided content before printing.
253     *
254     * @param contents Contents of the comment
255     * @param printer the print stream to use
256     */
257    protected void generateComment(String contents, CodeStream printer) {
258        indent(printer);
259        printer.print("<!-- " + XMLUtil.escape(contents) + " -->\n");
260    }
261
262    /**
263     * Print the current indentation to printer.
264     */
265    private void indent(CodeStream printer) {
266        for (int i = 0; i < indentation; i++)
267            printer.print(indentStep);
268    }
269   
270   
271    /**
272     * Base class for tags used to generate XML files.
273     *
274     * For an XML tag that has attributes described by their own tag classes,
275     * override {@link #attributes()} to return their names.
276     * For an XML tag that contains child tags described by their own tag classes,
277     * override {@link #children()} to return their names.
278     * For an XML attribute, override {@link #value()} to return its value.
279     *
280     * If no specific behavior is needed (looping, etc.), then that is enough.
281     * Otherwise, override {@link #generate(java.io.PrintStream)}.
282     */
283    protected abstract class XMLTag extends DAETag {
284       
285        private AbstractTag[] childTags;
286        private AbstractTag[] attributeTags;
287       
288        /**
289         * The name to use for the xml tag or attribute.
290         */
291        protected String xml;
292       
293        /**
294         * Construct a tag.
295         *
296         * @param name        tag name
297         * @param xml         name to use for the xml tag or attribute
298         * @param myGenerator the tag's generator
299         * @param fclass      an FClass to generate code for
300         */
301        public XMLTag(String name, String xml, AbstractGenerator myGenerator, FClass fclass) {
302            super(name, myGenerator, fclass);
303            this.xml = xml;
304        }
305       
306        /**
307         * Get the tags used to generate the children of this node.
308         *
309         * Default implementation returns all tags named in {@link #children()}.
310         */
311        public AbstractTag[] childTags() {
312            if (childTags == null) 
313                childTags = findTags(children());
314            return childTags;
315        }
316       
317        /**
318         * Get the tags used to generate the attributes of this node.
319         *
320         * Default implementation returns all tags named in {@link #attributes()}.
321         */
322        public AbstractTag[] attributeTags() {
323            if (attributeTags == null) 
324                attributeTags = findTags(attributes());
325            return attributeTags;
326        }
327       
328        /**
329         * Does this tag have any child tags that are active?
330         */
331        public boolean hasActiveChildren() {
332            return anyTagActive(childTags());
333        }
334       
335        /**
336         * Does this tag have any attribute tags that are active?
337         */
338        public boolean hasActiveAttributes() {
339            return anyTagActive(attributeTags());
340        }
341       
342        /**
343         * Checks if any of the given tags are active.
344         */
345        public boolean anyTagActive(AbstractTag[] tags) {
346            for (AbstractTag tag : tags) {
347                if (tag.isActive()) {
348                    return true;
349                }
350            }
351            return false;
352        }
353       
354        /**
355         * Get the names of the tags used to generate the children of this node.
356         *
357         * Subclasses should override if they can have any children.
358         */
359        public String[] children() {
360            return new String[] {};
361        }
362       
363        /**
364         * Get the names of the tags used to generate the attributes of this node.
365         *
366         * Subclasses should override if they can have any attributes.
367         */
368        public String[] attributes() {
369            return new String[] {};
370        }
371       
372        /**
373         * The value of the attribute described by this tag.
374         *
375         * Subclasses that represent an XML attribute should override and return non-null.
376         */
377        public Object value() {
378            return null;
379        }
380       
381        /**
382         * Generate the children of this tag.
383         */
384        public void generateChildren(CodeStream printer) {
385            for (AbstractTag child : childTags()) 
386                child.generateTag(printer);
387        }
388       
389        /**
390         * Generate the attributes of this tag.
391         */
392        public void generateAttributes(CodeStream printer) {
393            for (AbstractTag attribute : attributeTags()) 
394                attribute.generateTag(printer);
395        }
396       
397        /**
398         * Generate the opening tag of an XML node, with attributes as defined by
399         * {@link #attributeTags()}.
400         *
401         * @param name     the name of the XML tag
402         * @param printer  the print stream to use
403         */
404        public void generateOpening(String name, CodeStream printer) {
405            generateTagStart(name, printer);
406            generateAttributes(printer);
407            generateOpenEnd(printer);
408        }
409       
410        /**
411         * Generate an XML node, with children and/or attributes as defined by
412         * {@link #childTags()} and {@link #attributeTags()}.
413         *
414         * @param name     the name of the XML tag
415         * @param printer  the print stream to use
416         */
417        public void generateNode(String name, CodeStream printer) {
418            generateOpening(name, printer);
419            generateChildren(printer);
420            generateClosing(name, printer);
421        }
422       
423        /**
424         * Generate a closed XML tag, with attributes as defined by {@link #attributeTags()}.
425         *
426         * @param name     the name of the XML tag
427         * @param printer  the print stream to use
428         */
429        public void generateClosed(String name, CodeStream printer) {
430            generateTagStart(name, printer);
431            generateAttributes(printer);
432            generateClosedEnd(printer);
433        }
434
435        /**
436         * Method for generating code corresponding to the tag.
437         *
438         * Default implementation does:
439         * <ul>
440         * <li>If {@link #value()} returns non-null, generate an XML attribute with that value.
441         * <li>Otherwise if {@link #childTags()} returns an empty array, generate an XML node as
442         * per {@link #generateNode(String, CodeStream)}.
443         * <li>Otherwise generate a closed XML tag, as per
444         * {@link #generateClosed(String, CodeStream)}.
445         * </ul>
446         *
447         * @param printer A CodeStream object for output of the generated code.
448         */
449        public void generate(CodeStream printer) {
450                Object val = value();
451            if (val != null)
452                generateAttribute(xml, val, printer);
453            else if (hasActiveChildren())
454                generateNode(xml, printer);
455            else
456                generateClosed(xml, printer);
457        }
458       
459    }
460}
Note: See TracBrowser for help on using the repository browser.