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 }