001    /* ========================================================================
002     * JCommon : a free general purpose class library for the Java(tm) platform
003     * ========================================================================
004     *
005     * (C) Copyright 2000-2005, by Object Refinery Limited and Contributors.
006     * 
007     * Project Info:  http://www.jfree.org/jcommon/index.html
008     *
009     * This library is free software; you can redistribute it and/or modify it 
010     * under the terms of the GNU Lesser General Public License as published by 
011     * the Free Software Foundation; either version 2.1 of the License, or 
012     * (at your option) any later version.
013     *
014     * This library is distributed in the hope that it will be useful, but 
015     * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 
016     * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public 
017     * License for more details.
018     *
019     * You should have received a copy of the GNU Lesser General Public
020     * License along with this library; if not, write to the Free Software
021     * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, 
022     * USA.  
023     *
024     * [Java is a trademark or registered trademark of Sun Microsystems, Inc. 
025     * in the United States and other countries.]
026     * 
027     * ----------------
028     * ModelWriter.java
029     * ----------------
030     * (C)opyright 2003, 2004, by Thomas Morgner and Contributors.
031     *
032     * Original Author:  Thomas Morgner;
033     * Contributor(s):   David Gilbert (for Object Refinery Limited);
034     *
035     * $Id: ModelWriter.java,v 1.3 2005/10/18 13:32:20 mungady Exp $
036     *
037     * Changes
038     * -------------------------
039     * 21.06.2003 : Initial version
040     *
041     */
042    
043    package org.jfree.xml.generator;
044    
045    import java.io.IOException;
046    import java.io.Writer;
047    
048    import org.jfree.xml.generator.model.ClassDescription;
049    import org.jfree.xml.generator.model.Comments;
050    import org.jfree.xml.generator.model.DescriptionModel;
051    import org.jfree.xml.generator.model.IgnoredPropertyInfo;
052    import org.jfree.xml.generator.model.ManualMappingInfo;
053    import org.jfree.xml.generator.model.MultiplexMappingInfo;
054    import org.jfree.xml.generator.model.PropertyInfo;
055    import org.jfree.xml.generator.model.PropertyType;
056    import org.jfree.xml.generator.model.TypeInfo;
057    import org.jfree.xml.util.ClassModelTags;
058    import org.jfree.xml.writer.AttributeList;
059    import org.jfree.xml.writer.SafeTagList;
060    import org.jfree.xml.writer.XMLWriterSupport;
061    
062    /**
063     * A model writer.
064     */
065    public class ModelWriter {
066    
067        /** The tags that can be split. */
068        private static SafeTagList safeTags;
069    
070        /**
071         * Returns the safe tag list.
072         * 
073         * @return The safe tag list.
074         */
075        public static SafeTagList getSafeTags() {
076            if (safeTags == null) {
077                safeTags = new SafeTagList();
078                safeTags.add(ClassModelTags.OBJECTS_TAG);
079                safeTags.add(ClassModelTags.OBJECT_TAG);
080                safeTags.add(ClassModelTags.CONSTRUCTOR_TAG);
081                safeTags.add(ClassModelTags.ELEMENT_PROPERTY_TAG);
082                safeTags.add(ClassModelTags.LOOKUP_PROPERTY_TAG);
083                safeTags.add(ClassModelTags.ATTRIBUTE_PROPERTY_TAG);
084                safeTags.add(ClassModelTags.PARAMETER_TAG);
085                safeTags.add(ClassModelTags.INCLUDE_TAG);
086                safeTags.add(ClassModelTags.IGNORED_PROPERTY_TAG);
087                safeTags.add(ClassModelTags.MANUAL_TAG);
088                safeTags.add(ClassModelTags.MAPPING_TAG);
089                safeTags.add(ClassModelTags.TYPE_TAG);
090            }
091            return safeTags;
092        }
093    
094        /** A support class for writing XML tags. */
095        private XMLWriterSupport writerSupport;
096        
097        /** A model containing class descriptions. */
098        private DescriptionModel model;
099    
100        /**
101         * Creates a new model writer instance.
102         */
103        public ModelWriter() {
104            this.writerSupport = new XMLWriterSupport(getSafeTags(), 0);
105        }
106    
107        /**
108         * Returns the model.
109         * 
110         * @return The model.
111         */
112        public DescriptionModel getModel() {
113            return this.model;
114        }
115    
116        /**
117         * Sets the model to be written.
118         * 
119         * @param model  the model.
120         */
121        public void setModel(final DescriptionModel model) {
122            this.model = model;
123        }
124    
125        /**
126         * Writes an XML header.
127         * 
128         * @param writer  the writer.
129         * 
130         * @throws IOException if there is an I/O problem.
131         */
132        public static void writeXMLHeader(final Writer writer) throws IOException {
133            writer.write("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
134            writer.write(XMLWriterSupport.getLineSeparator());
135        }
136    
137        /**
138         * Writes a set of comments.
139         * 
140         * @param writer  the writer.
141         * @param comments  a set of comments.
142         * 
143         * @throws IOException if there is an I/O problem.
144         */
145        protected void writeStandardComment(final Writer writer, final Comments comments) throws IOException {
146            if ((comments == null) || (comments.getOpenTagComment() == null)) {
147                writer.write(
148                    "<!-- CVSTag: $Id: ModelWriter.java,v 1.3 2005/10/18 13:32:20 mungady Exp $ " 
149                    + comments + " -->"
150                );
151                writer.write(XMLWriterSupport.getLineSeparator());
152            }
153            else {
154                writeComment(writer, comments.getOpenTagComment());
155            }
156        }
157    
158        /**
159         * Writes a sequence of comments.
160         * 
161         * @param writer  the writer.
162         * @param comments  the comments (<code>null</code> ignored).
163         * 
164         * @throws IOException if there is an I/O problem.
165         */
166        protected void writeComment(final Writer writer, final String[] comments) throws IOException {
167            if (comments == null) {
168                return;
169            }
170            for (int i = 0; i < comments.length; i++) {
171                this.writerSupport.indent(writer, XMLWriterSupport.INDENT_ONLY);
172                writer.write("<!--");
173                writer.write(comments[i]);
174                writer.write("-->");
175                writer.write(XMLWriterSupport.getLineSeparator());
176            }
177        }
178    
179        /**
180         * Writes the open comments from a set of comments.
181         * 
182         * @param writer  the writer.
183         * @param comments  the set of comments.
184         * 
185         * @throws IOException if there is an I/O problem.
186         */
187        protected void writeOpenComment(final Writer writer, final Comments comments) throws IOException {
188            if (comments == null) {
189                return;
190            }
191            if (comments.getOpenTagComment() == null) {
192                return;
193            }
194            writeComment(writer, comments.getOpenTagComment());
195        }
196    
197        /**
198         * Writes the close comments from a set of comments.
199         * 
200         * @param writer  the writer.
201         * @param comments  the set of comments.
202         * 
203         * @throws IOException if there is an I/O problem.
204         */
205        protected void writeCloseComment(final Writer writer, final Comments comments) throws IOException {
206            if (comments == null) {
207                return;
208            }
209            if (comments.getCloseTagComment() == null) {
210                return;
211            }
212            writeComment(writer, comments.getCloseTagComment());
213        }
214    
215        /**
216         * Writes a closed (short) tag with eventually nested comments.
217         *
218         * @param writer  the writer.
219         * @param tagName  the tag name.
220         * @param attributes  the attributes.
221         * @param comments  the comments.
222         * 
223         * @throws IOException if there is an I/O problem.
224         */
225        protected void writeTag(final Writer writer, 
226                                final String tagName,
227                                final AttributeList attributes,
228                                final Comments comments) throws IOException {
229            if (comments == null) {
230                this.writerSupport.writeTag(writer, tagName, attributes, XMLWriterSupport.CLOSE);
231            }
232            else {
233                writeOpenComment(writer, comments);
234                if (comments.getCloseTagComment() != null) {
235                    this.writerSupport.writeTag(writer, tagName, attributes, XMLWriterSupport.OPEN);
236                    writeCloseComment(writer, comments);
237                    this.writerSupport.writeCloseTag(writer, tagName);
238                }
239                else {
240                    this.writerSupport.writeTag(writer, tagName, attributes, XMLWriterSupport.CLOSE);
241                }
242            }
243        }
244    
245        /**
246         * Writes a closed (short) tag with eventually nested comments.
247         *
248         * @param writer  the writer.
249         * @param tagName  the tag name.
250         * @param attribute  the attribute name.
251         * @param value  the attribute value.
252         * @param comments  the comments.
253         * 
254         * @throws IOException if there is an I/O problem.
255         */
256        protected void writeTag(final Writer writer, 
257                                final String tagName,
258                                final String attribute, 
259                                final String value,
260                                final Comments comments) throws IOException {
261            if (comments == null) {
262                this.writerSupport.writeTag(writer, tagName, attribute, value , XMLWriterSupport.CLOSE);
263            }
264            else {
265                writeOpenComment(writer, comments);
266                if (comments.getCloseTagComment() != null) {
267                    this.writerSupport.writeTag(
268                        writer, tagName, attribute, value, XMLWriterSupport.OPEN
269                    );
270                    writeCloseComment(writer, comments);
271                    this.writerSupport.writeCloseTag(writer, tagName);
272                }
273                else {
274                    this.writerSupport.writeTag(
275                        writer, tagName, attribute, value, XMLWriterSupport.CLOSE
276                    );
277                }
278            }
279        }
280    
281        /**
282         * Writes a model to the specified writer.
283         * 
284         * @param writer  the writer.
285         * 
286         * @throws IOException if there is an I/O problem.
287         */
288        public void write(final Writer writer) throws IOException {
289            
290            writeStandardComment(writer, this.model.getModelComments());
291            this.writerSupport.writeTag(writer, ClassModelTags.OBJECTS_TAG);
292            final String[] sources = this.model.getSources();
293            for (int i = 0; i < sources.length; i++) {
294                final Comments comments = this.model.getIncludeComment(sources[i]);
295                writeTag(
296                    writer, 
297                    ClassModelTags.INCLUDE_TAG, ClassModelTags.SOURCE_ATTR, sources[i], comments
298                );
299            }
300    
301            for (int i = 0; i < this.model.size(); i++) {
302                final ClassDescription cd = this.model.get(i);
303                writeClassDescription(writer, cd);
304            }
305    
306            final ManualMappingInfo[] mappings = getModel().getMappingModel().getManualMapping();
307            for (int i = 0; i < mappings.length; i++) {
308                final ManualMappingInfo mi = mappings[i];
309                writeManualMapping(writer, mi);
310            }
311    
312            final MultiplexMappingInfo[] mmappings = getModel().getMappingModel().getMultiplexMapping();
313            for (int i = 0; i < mmappings.length; i++) {
314                final MultiplexMappingInfo mi = mmappings[i];
315                writeMultiplexMapping(writer, mi);
316            }
317    
318            writeCloseComment(writer, this.model.getModelComments());
319            this.writerSupport.writeCloseTag(writer, ClassModelTags.OBJECTS_TAG);
320            
321        }
322    
323        /**
324         * Writes a manual mapping to the XML output.
325         * 
326         * @param writer  the writer.
327         * @param mi  the mapping info.
328         * 
329         * @throws IOException if there is an I/O problem.
330         */
331        protected void writeManualMapping(final Writer writer, final ManualMappingInfo mi) throws IOException {
332            final AttributeList al = new AttributeList();
333            al.setAttribute(ClassModelTags.CLASS_ATTR, mi.getBaseClass().getName());
334            al.setAttribute(ClassModelTags.READ_HANDLER_ATTR, mi.getReadHandler().getName());
335            al.setAttribute(ClassModelTags.WRITE_HANDLER_ATTR, mi.getWriteHandler().getName());
336            writeTag(writer, ClassModelTags.MANUAL_TAG, al, mi.getComments());
337        }
338    
339        /**
340         * Writes a multiplex mapping to the XML output.
341         * 
342         * @param writer  the writer.
343         * @param mi  the mapping info.
344         * 
345         * @throws IOException if there is an I/O problem.
346         */
347        protected void writeMultiplexMapping(final Writer writer, final MultiplexMappingInfo mi)
348            throws IOException {
349            
350            final TypeInfo[] tis = mi.getChildClasses();
351    
352            final AttributeList al = new AttributeList();
353            al.setAttribute(ClassModelTags.BASE_CLASS_ATTR, mi.getBaseClass().getName());
354            al.setAttribute(ClassModelTags.TYPE_ATTR, mi.getTypeAttribute());
355            getWriterSupport().writeTag(writer, ClassModelTags.MAPPING_TAG, al, XMLWriterSupport.OPEN);
356    
357            for (int j = 0; j < tis.length; j++) {
358                final AttributeList tiAttr = new AttributeList();
359                tiAttr.setAttribute(ClassModelTags.NAME_ATTR, tis[j].getName());
360                tiAttr.setAttribute(ClassModelTags.CLASS_ATTR, tis[j].getType().getName());
361                writeTag(writer, ClassModelTags.TYPE_TAG, tiAttr, tis[j].getComments());
362            }
363    
364            getWriterSupport().writeCloseTag(writer, ClassModelTags.MAPPING_TAG);
365        }
366    
367        /**
368         * Writes a class description.
369         * 
370         * @param writer  the writer.
371         * @param cd  the class description.
372         * 
373         * @throws IOException if there is an I/O problem.
374         */
375        protected void writeClassDescription(final Writer writer, final ClassDescription cd) throws IOException {
376    
377            if (cd.isUndefined()) {
378                return;
379            }
380    
381            final AttributeList al = new AttributeList();
382            al.setAttribute(ClassModelTags.CLASS_ATTR, cd.getName());
383            if (cd.getRegisterKey() != null) {
384                al.setAttribute(ClassModelTags.REGISTER_NAMES_ATTR, cd.getRegisterKey());
385            }
386            if (cd.isPreserve()) {
387                al.setAttribute(ClassModelTags.IGNORE_ATTR, "true");
388            }
389            this.writerSupport.writeTag(writer, ClassModelTags.OBJECT_TAG, al, XMLWriterSupport.OPEN);
390    
391            final TypeInfo[] constructorInfo = cd.getConstructorDescription();
392            if (constructorInfo != null && constructorInfo.length != 0) {
393                this.writerSupport.writeTag(writer, ClassModelTags.CONSTRUCTOR_TAG);
394                for (int i = 0; i < constructorInfo.length; i++) {
395                    final AttributeList constructorList = new AttributeList();
396                    constructorList.setAttribute(
397                        ClassModelTags.CLASS_ATTR, constructorInfo[i].getType().getName()
398                    );
399                    constructorList.setAttribute(
400                        ClassModelTags.PROPERTY_ATTR, constructorInfo[i].getName()
401                    );
402                    writeTag(writer, ClassModelTags.PARAMETER_TAG, constructorList, 
403                             constructorInfo[i].getComments());
404                }
405                this.writerSupport.writeCloseTag(writer, ClassModelTags.CONSTRUCTOR_TAG);
406            }
407    
408            final PropertyInfo[] properties = cd.getProperties();
409            for (int i = 0; i < properties.length; i++) {
410                writePropertyInfo(writer, properties[i]);
411            }
412    
413            this.writerSupport.writeCloseTag(writer, ClassModelTags.OBJECT_TAG);
414        }
415    
416        /**
417         * Writes a property info element.
418         * 
419         * @param writer  the writer.
420         * @param ipi  the property info.
421         * 
422         * @throws IOException if there is an I/O problem.
423         */
424        private void writePropertyInfo(final Writer writer, final PropertyInfo ipi) throws IOException {
425            final AttributeList props = new AttributeList();
426            props.setAttribute(ClassModelTags.NAME_ATTR, ipi.getName());
427    
428            if (ipi instanceof IgnoredPropertyInfo) {
429                writeTag(writer, ClassModelTags.IGNORED_PROPERTY_TAG, props, ipi.getComments());
430                return;
431            }
432    
433            if (ipi.getPropertyType().equals(PropertyType.ATTRIBUTE)) {
434                props.setAttribute(ClassModelTags.ATTRIBUTE_ATTR, ipi.getXmlName());
435                props.setAttribute(ClassModelTags.ATTRIBUTE_HANDLER_ATTR, ipi.getXmlHandler());
436                writeTag(writer, ClassModelTags.ATTRIBUTE_PROPERTY_TAG, props, ipi.getComments());
437            }
438            else if (ipi.getPropertyType().equals(PropertyType.ELEMENT)) {
439                if (ipi.getComments() == null || ipi.getComments().getOpenTagComment() == null)
440                {
441                    this.writerSupport.indent(writer, XMLWriterSupport.INDENT_ONLY);
442                    writer.write("<!-- property type is " + ipi.getType() + " -->");
443                    writer.write(System.getProperty("line.separator", "\n"));
444                }
445                props.setAttribute(ClassModelTags.ELEMENT_ATTR, ipi.getXmlName());
446                writeTag(writer, ClassModelTags.ELEMENT_PROPERTY_TAG, props, ipi.getComments());
447            }
448            else {
449                props.setAttribute(ClassModelTags.LOOKUP_ATTR, ipi.getXmlName());
450                writeTag(writer, ClassModelTags.LOOKUP_PROPERTY_TAG, props, ipi.getComments());
451            }
452        }
453    
454        /**
455         * Returns the writer support object.
456         * 
457         * @return The writer support object.
458         */
459        public XMLWriterSupport getWriterSupport() {
460            return this.writerSupport;
461        }
462    }