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     * SplittingModelWriter.java
029     * -------------------------
030     * (C)opyright 2003, by Thomas Morgner and Contributors.
031     *
032     * Original Author:  Thomas Morgner;
033     * Contributor(s):   David Gilbert (for Object Refinery Limited);
034     *
035     * $Id: SplittingModelWriter.java,v 1.2 2005/10/18 13:32:20 mungady Exp $
036     *
037     * Changes
038     * -------------------------
039     * 12.11.2003 : Initial version
040     *
041     */
042    
043    package org.jfree.xml.generator;
044    
045    import java.io.BufferedWriter;
046    import java.io.File;
047    import java.io.FileOutputStream;
048    import java.io.IOException;
049    import java.io.OutputStreamWriter;
050    import java.util.ArrayList;
051    import java.util.Arrays;
052    import java.util.Iterator;
053    
054    import org.jfree.io.IOUtils;
055    import org.jfree.util.HashNMap;
056    import org.jfree.util.Log;
057    import org.jfree.xml.generator.model.ClassDescription;
058    import org.jfree.xml.generator.model.DescriptionModel;
059    import org.jfree.xml.generator.model.ManualMappingInfo;
060    import org.jfree.xml.generator.model.MappingModel;
061    import org.jfree.xml.generator.model.MultiplexMappingInfo;
062    import org.jfree.xml.util.ClassModelTags;
063    
064    /**
065     * A model writer that writes to multiple files.
066     */
067    public class SplittingModelWriter extends ModelWriter {
068    
069        /** ??. */
070        private HashNMap classDescriptionByPackage;
071        
072        /** The sources. */
073        private ArrayList sources;
074        
075        /** The target file. */
076        private File targetFile;
077        
078        /** The file extension. */
079        private String extension;
080        
081        /** The plain file name. */
082        private String plainFileName;
083        
084        /** ??. */
085        private HashNMap manualMappingByPackage;
086        
087        /** ??. */
088        private HashNMap multiplexMappingByPackage;
089    
090        /**
091         * Creates a new instance.
092         */
093        public SplittingModelWriter() {
094            super();
095        }
096    
097        /**
098         * Writes the model to the specified target.
099         * 
100         * @param target  the target file name.
101         * 
102         * @throws IOException if there is an I/O problem.
103         */
104        public synchronized void write(final String target) throws IOException {
105         
106            final DescriptionModel model = getModel();
107            this.sources = new ArrayList(Arrays.asList(model.getSources()));
108            this.targetFile = new File(target);
109            this.plainFileName = IOUtils.getInstance().stripFileExtension(this.targetFile.getName());
110            this.extension = IOUtils.getInstance().getFileExtension(target);
111    
112            // split into classDescriptionByPackage ...
113            this.classDescriptionByPackage = new HashNMap();
114            for (int i = 0; i < model.size(); i++) {
115                final ClassDescription cd = model.get(i);
116                if (cd.getSource() == null) {
117                    final String packageName = getPackage(cd.getObjectClass());
118                    final String includeFileName = this.plainFileName + "-" + packageName 
119                        + this.extension;
120                    this.classDescriptionByPackage.add(includeFileName, cd);
121                }
122                else {
123                    this.classDescriptionByPackage.add(cd.getSource(), cd);
124                }
125            }
126    
127            final MappingModel mappingModel = model.getMappingModel();
128    
129            // split manual mappings into packages ...
130            final ManualMappingInfo[] manualMappings = mappingModel.getManualMapping();
131            this.manualMappingByPackage = new HashNMap();
132            for (int i = 0; i < manualMappings.length; i++) {
133                final ManualMappingInfo mapping = manualMappings[i];
134                if (mapping.getSource() == null) {
135                    this.manualMappingByPackage.add("", mapping);
136                }
137                else {
138                    this.manualMappingByPackage.add(mapping.getSource(), mapping);
139                }
140            }
141    
142            // split manual mappings into packages ...
143            final MultiplexMappingInfo[] multiplexMappings = mappingModel.getMultiplexMapping();
144            this.multiplexMappingByPackage = new HashNMap();
145            for (int i = 0; i < multiplexMappings.length; i++) {
146                final MultiplexMappingInfo mapping = multiplexMappings[i];
147                if (mapping.getSource() == null) {
148                    this.multiplexMappingByPackage.add("", mapping);
149                }
150                else {
151                    this.multiplexMappingByPackage.add(mapping.getSource(), mapping);
152                }
153            }
154    
155    
156            final Object[] keys = this.classDescriptionByPackage.keySet().toArray();
157            for (int i = 0; i < keys.length; i++) {
158    
159                final String includeFileName = (String) keys[i];
160                // write if not contained in the master file ...
161                if (!includeFileName.equals("")) {
162                    writePackageFile(includeFileName);
163                }
164            }
165    
166            writeMasterFile();
167    
168            this.manualMappingByPackage = null;
169            this.multiplexMappingByPackage = null;
170            this.classDescriptionByPackage = null;
171            this.sources = null;
172        }
173    
174        /**
175         * Writes a file for a package.
176         * 
177         * @param includeFileName  the name of the file.
178         * 
179         * @throws IOException if there is an I/O problem.
180         */
181        private void writePackageFile(final String includeFileName) throws IOException {
182            
183            final Iterator values = this.classDescriptionByPackage.getAll(includeFileName);
184            final Iterator manualMappings = this.manualMappingByPackage.getAll(includeFileName);
185            final Iterator multiplexMappings = this.multiplexMappingByPackage.getAll(includeFileName);
186            if (!values.hasNext() && !manualMappings.hasNext() && !multiplexMappings.hasNext()) {
187                return;
188            }
189    
190            Log.debug ("Writing included file: " + includeFileName);
191            // the current file need no longer be included manually ...
192            this.sources.remove(includeFileName);
193    
194            final BufferedWriter writer = new BufferedWriter(
195                new OutputStreamWriter(
196                    new FileOutputStream(
197                        new File(this.targetFile.getParentFile(), includeFileName)
198                    ), 
199                    "UTF-8"
200                )
201            );
202    
203            writeXMLHeader(writer);
204            writeStandardComment(writer, getModel().getModelComments());
205            getWriterSupport().writeTag(writer, ClassModelTags.OBJECTS_TAG);
206    
207            while (values.hasNext()) {
208                final ClassDescription cd = (ClassDescription) values.next();
209                writeClassDescription(writer, cd);
210            }
211    
212    
213            while (manualMappings.hasNext()) {
214                final ManualMappingInfo mi = (ManualMappingInfo) manualMappings.next();
215                writeManualMapping(writer, mi);
216            }
217    
218            while (multiplexMappings.hasNext()) {
219                final MultiplexMappingInfo mi = (MultiplexMappingInfo) multiplexMappings.next();
220                writeMultiplexMapping(writer, mi);
221            }
222    
223            writeCloseComment(writer, getModel().getModelComments());
224            getWriterSupport().writeCloseTag(writer, ClassModelTags.OBJECTS_TAG);
225            writer.close();
226        }
227    
228        /**
229         * Returns the name of the package for the given class. This is a
230         * workaround for the classloader behaviour of JDK1.2.2 where no
231         * package objects are created.
232         *
233         * @param c the class for which we search the package.
234         * 
235         * @return the name of the package, never null.
236         */
237        public static String getPackage(final Class c) {
238            final String className = c.getName();
239            final int idx = className.lastIndexOf('.');
240            if (idx <= 0) {
241                // the default package
242                return "";
243            }
244            else {
245                return className.substring(0, idx);
246            }
247        }
248    
249        /**
250         * Writes the master file.
251         * 
252         * @throws IOException if there is an I/O problem.
253         */
254        private void writeMasterFile() throws IOException {
255    
256            Log.debug ("Writing master file: " + this.targetFile);
257    
258            final BufferedWriter writer = new BufferedWriter(
259                new OutputStreamWriter(new FileOutputStream(this.targetFile), "UTF-8")
260            );
261    
262            writeXMLHeader(writer);
263            writeStandardComment(writer, getModel().getModelComments());
264            getWriterSupport().writeTag(writer, ClassModelTags.OBJECTS_TAG);
265    
266            for (int i = 0; i < this.sources.size(); i++) {
267                final String includeFileName = (String) this.sources.get(i);
268                if (!includeFileName.equals("")) {
269                    writeTag(writer, ClassModelTags.INCLUDE_TAG, ClassModelTags.SOURCE_ATTR,
270                        includeFileName, getModel().getIncludeComment(includeFileName));
271                }
272            }
273    
274            final Object[] keys = this.classDescriptionByPackage.keySet().toArray();
275            Arrays.sort(keys);
276            for (int i = 0; i < keys.length; i++) {
277                final String includeFileName = (String) keys[i];
278                if (!includeFileName.equals("")) {
279                    writeTag(writer, ClassModelTags.INCLUDE_TAG, ClassModelTags.SOURCE_ATTR,
280                        includeFileName, getModel().getIncludeComment(includeFileName));
281                }
282            }
283    
284            final Iterator values = this.classDescriptionByPackage.getAll("");
285            while (values.hasNext()) {
286                final ClassDescription cd = (ClassDescription) values.next();
287                writeClassDescription(writer, cd);
288            }
289    
290            final Iterator manualMappings = this.manualMappingByPackage.getAll("");
291            while (manualMappings.hasNext()) {
292                final ManualMappingInfo mi = (ManualMappingInfo) manualMappings.next();
293                writeManualMapping(writer, mi);
294            }
295    
296            final Iterator multiplexMappings = this.multiplexMappingByPackage.getAll("");
297            while (multiplexMappings.hasNext()) {
298                final MultiplexMappingInfo mi = (MultiplexMappingInfo) multiplexMappings.next();
299                writeMultiplexMapping(writer, mi);
300            }
301    
302            writeCloseComment(writer, getModel().getModelComments());
303            getWriterSupport().writeCloseTag(writer, ClassModelTags.OBJECTS_TAG);
304            writer.close();
305        }
306        
307    }