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

Last change on this file since 13800 was 13800, checked in by randersson, 6 weeks ago

#5819 Merged trunk into branch

File size: 15.6 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                    CValue cvalue = attributeExp.ceval();
220                    if (name.equals("nominal")) {
221                        cvalue = cvalue.absoluteValue();
222                    }
223                    String value = cvalue.xmlValue();
224                    generateAttribute(name, value, printer);
225                } catch (ConstantEvaluationException e) {
226                    throw new InternalCompilerError("Exception when generating xml attribute '" +
227                        name + "' for variable '" + variable.name() + "'.", e);
228                }
229            }
230        }
231    }
232   
233    /**
234     * Generate an XML attribute representing a list, the entires will be
235     * space separated.
236     *
237     * @param name     the name of the attribute
238     * @param values   the values of the attribute
239     * @param printer  the print stream to use
240     */
241    protected void generateListAttribute(String name, Iterable<? extends Object> values, CodeStream printer) {
242        printer.print(" ", name, "=\"");
243        boolean first = true;
244        for (Object value : values) {
245            if (!first) {
246                printer.print(" ");
247            }
248            first = false;
249            printer.print(XMLUtil.escape(value.toString()));
250        }
251        printer.print("\"");
252    }
253
254    /**
255     * Generates an XML comment with the provided contents. Special characters
256     * are escaped in the provided content before printing.
257     *
258     * @param contents Contents of the comment
259     * @param printer the print stream to use
260     */
261    protected void generateComment(String contents, CodeStream printer) {
262        indent(printer);
263        printer.print("<!-- " + XMLUtil.escape(contents) + " -->\n");
264    }
265
266    /**
267     * Print the current indentation to printer.
268     */
269    private void indent(CodeStream printer) {
270        for (int i = 0; i < indentation; i++)
271            printer.print(indentStep);
272    }
273   
274   
275    /**
276     * Base class for tags used to generate XML files.
277     *
278     * For an XML tag that has attributes described by their own tag classes,
279     * override {@link #attributes()} to return their names.
280     * For an XML tag that contains child tags described by their own tag classes,
281     * override {@link #children()} to return their names.
282     * For an XML attribute, override {@link #value()} to return its value.
283     *
284     * If no specific behavior is needed (looping, etc.), then that is enough.
285     * Otherwise, override {@link #generate(java.io.PrintStream)}.
286     */
287    protected abstract class XMLTag extends DAETag {
288       
289        private AbstractTag[] childTags;
290        private AbstractTag[] attributeTags;
291       
292        /**
293         * The name to use for the xml tag or attribute.
294         */
295        protected String xml;
296       
297        /**
298         * Construct a tag.
299         *
300         * @param name        tag name
301         * @param xml         name to use for the xml tag or attribute
302         * @param myGenerator the tag's generator
303         * @param fclass      an FClass to generate code for
304         */
305        public XMLTag(String name, String xml, AbstractGenerator myGenerator, FClass fclass) {
306            super(name, myGenerator, fclass);
307            this.xml = xml;
308        }
309       
310        /**
311         * Get the tags used to generate the children of this node.
312         *
313         * Default implementation returns all tags named in {@link #children()}.
314         */
315        public AbstractTag[] childTags() {
316            if (childTags == null) 
317                childTags = findTags(children());
318            return childTags;
319        }
320       
321        /**
322         * Get the tags used to generate the attributes of this node.
323         *
324         * Default implementation returns all tags named in {@link #attributes()}.
325         */
326        public AbstractTag[] attributeTags() {
327            if (attributeTags == null) 
328                attributeTags = findTags(attributes());
329            return attributeTags;
330        }
331       
332        /**
333         * Does this tag have any child tags that are active?
334         */
335        public boolean hasActiveChildren() {
336            return anyTagActive(childTags());
337        }
338       
339        /**
340         * Does this tag have any attribute tags that are active?
341         */
342        public boolean hasActiveAttributes() {
343            return anyTagActive(attributeTags());
344        }
345       
346        /**
347         * Checks if any of the given tags are active.
348         */
349        public boolean anyTagActive(AbstractTag[] tags) {
350            for (AbstractTag tag : tags) {
351                if (tag.isActive()) {
352                    return true;
353                }
354            }
355            return false;
356        }
357       
358        /**
359         * Get the names of the tags used to generate the children of this node.
360         *
361         * Subclasses should override if they can have any children.
362         */
363        public String[] children() {
364            return new String[] {};
365        }
366       
367        /**
368         * Get the names of the tags used to generate the attributes of this node.
369         *
370         * Subclasses should override if they can have any attributes.
371         */
372        public String[] attributes() {
373            return new String[] {};
374        }
375       
376        /**
377         * The value of the attribute described by this tag.
378         *
379         * Subclasses that represent an XML attribute should override and return non-null.
380         */
381        public Object value() {
382            return null;
383        }
384       
385        /**
386         * Generate the children of this tag.
387         */
388        public void generateChildren(CodeStream printer) {
389            for (AbstractTag child : childTags()) 
390                child.generateTag(printer);
391        }
392       
393        /**
394         * Generate the attributes of this tag.
395         */
396        public void generateAttributes(CodeStream printer) {
397            for (AbstractTag attribute : attributeTags()) 
398                attribute.generateTag(printer);
399        }
400       
401        /**
402         * Generate the opening tag of an XML node, with attributes as defined by
403         * {@link #attributeTags()}.
404         *
405         * @param name     the name of the XML tag
406         * @param printer  the print stream to use
407         */
408        public void generateOpening(String name, CodeStream printer) {
409            generateTagStart(name, printer);
410            generateAttributes(printer);
411            generateOpenEnd(printer);
412        }
413       
414        /**
415         * Generate an XML node, with children and/or attributes as defined by
416         * {@link #childTags()} and {@link #attributeTags()}.
417         *
418         * @param name     the name of the XML tag
419         * @param printer  the print stream to use
420         */
421        public void generateNode(String name, CodeStream printer) {
422            generateOpening(name, printer);
423            generateChildren(printer);
424            generateClosing(name, printer);
425        }
426       
427        /**
428         * Generate a closed XML tag, with attributes as defined by {@link #attributeTags()}.
429         *
430         * @param name     the name of the XML tag
431         * @param printer  the print stream to use
432         */
433        public void generateClosed(String name, CodeStream printer) {
434            generateTagStart(name, printer);
435            generateAttributes(printer);
436            generateClosedEnd(printer);
437        }
438
439        /**
440         * Method for generating code corresponding to the tag.
441         *
442         * Default implementation does:
443         * <ul>
444         * <li>If {@link #value()} returns non-null, generate an XML attribute with that value.
445         * <li>Otherwise if {@link #childTags()} returns an empty array, generate an XML node as
446         * per {@link #generateNode(String, CodeStream)}.
447         * <li>Otherwise generate a closed XML tag, as per
448         * {@link #generateClosed(String, CodeStream)}.
449         * </ul>
450         *
451         * @param printer A CodeStream object for output of the generated code.
452         */
453        public void generate(CodeStream printer) {
454                Object val = value();
455            if (val != null)
456                generateAttribute(xml, val, printer);
457            else if (hasActiveChildren())
458                generateNode(xml, printer);
459            else
460                generateClosed(xml, printer);
461        }
462       
463    }
464}
Note: See TracBrowser for help on using the repository browser.