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
043package org.jfree.xml.generator;
044
045import java.io.BufferedWriter;
046import java.io.File;
047import java.io.FileOutputStream;
048import java.io.IOException;
049import java.io.OutputStreamWriter;
050import java.util.ArrayList;
051import java.util.Arrays;
052import java.util.Iterator;
053
054import org.jfree.io.IOUtils;
055import org.jfree.util.HashNMap;
056import org.jfree.util.Log;
057import org.jfree.xml.generator.model.ClassDescription;
058import org.jfree.xml.generator.model.DescriptionModel;
059import org.jfree.xml.generator.model.ManualMappingInfo;
060import org.jfree.xml.generator.model.MappingModel;
061import org.jfree.xml.generator.model.MultiplexMappingInfo;
062import org.jfree.xml.util.ClassModelTags;
063
064/**
065 * A model writer that writes to multiple files.
066 */
067public 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}