1 package xdoclet.plugins;
2
3 import xdoclet.Plugin;
4 import xdoclet.XDocletException;
5 import xdoclet.util.ClasspathManager;
6 import xdoclet.util.FileUtils;
7 import xdoclet.util.betwixt.BetwixtConfigurer;
8 import xdoclet.sdk.xgg.XGGPojo;
9 import xdoclet.sdk.xgg.betwixt.BeanReaderSerializer;
10
11 import java.io.File;
12 import java.io.FileWriter;
13 import java.io.Writer;
14 import java.io.IOException;
15 import java.io.ObjectInputStream;
16 import java.io.InputStream;
17 import java.io.Reader;
18 import java.io.InputStreamReader;
19 import java.beans.IntrospectionException;
20 import java.util.Collection;
21 import java.net.URL;
22
23 import org.apache.commons.betwixt.io.BeanWriter;
24 import org.apache.commons.betwixt.io.BeanReader;
25 import org.apache.commons.betwixt.XMLIntrospector;
26 import org.apache.commons.logging.LogFactory;
27 import org.xml.sax.SAXException;
28
29 /***
30 * <p>
31 * Plugin backed by <a href="http://jakarta.apache.org/commons/betwixt/">Betwixt</a>.
32 * This plugin is used as a base class for plugins using beans generated by
33 * {@link xdoclet.sdk.xgg.XGGPlugin}.
34 * </p>
35 * <p>
36 * The plugin developer only has to implement one of the <code>populate</code>
37 * methods (which one depending on whether the plugin is intended to be used in
38 * one file or multiple files mode).
39 * </p>
40 * <p>
41 * The implemented populate method should cast the root bean to the class
42 * that is passed to the constructor and populate the bean with sub-beans,
43 * using the metaData that's passes to the same populate method. The metaData
44 * should also be cast to the appropriate type, like {@link xjavadoc.XClass}
45 * or whatever metadata type the plugin expects to be fed with.
46 * </p>
47 *
48 * @bean.class name="betwixt"
49 * displayName="Betwixt Plugin"
50 * shortDescription="A plugin that generates XML with Betwixt"
51 *
52 * @bean.icon color16="betwixt16.gif"
53 *
54 * @author <a href="mailto:aslak.hellesoy at netcom.no">Aslak Hellesøy</a>
55 * @version $Revision: 1.8 $
56 */
57 public abstract class BetwixtPlugin extends Plugin {
58 public static final String SERIALIZED_BEANREADER = "BeanReader.ser";
59
60 private final Class _rootClass;
61
62 // The BeanReader used to read in merge files
63 private BeanReader _beanReader;
64
65 /***
66 * Sets the version.
67 * @param version DTD/XSD version.
68 *
69 * @bean.property
70 * shortDescription="Name of DTD or XSD file."
71 * displayName="Version"
72 * default="true"
73 */
74 public void setVersion(String version) {
75 XGGPojo.setVersion(version);
76 }
77
78 protected BetwixtPlugin( Class rootClass ) {
79 _rootClass = rootClass;
80
81 /*
82 TODO This doesn't work yet.
83 BeanReader isn't properly serialised with all rules,
84 and changes are probably needed in Digester to make it work.
85 */
86 //_beanReader = readBeanReader();
87
88 // TODO this is temporary till we get the above to work
89 File root = FileUtils.getRoot( getClass() );
90 System.out.println("CREATING BEANREADER");
91 _beanReader = BeanReaderSerializer.createBeanReader( root, null);
92 System.out.println("DONE CREATING BEANREADER");
93 }
94
95 /***
96 * Reads a serialised XMLBeanInfoRegistry that will be used
97 * by both BeanReader and BeanWriter. The serialised XMLBeanInfoRegistry
98 * is packaged during build time. The reason why we're doing it this
99 * way is that the construction of an XMLBeanInfoRegistry can be a
100 * lengthy operation.
101 *
102 * @throws IllegalStateException if the registry couldn't be read.
103 */
104 private BeanReader readBeanReader() throws IllegalStateException {
105 BeanReader result = null;
106 try {
107 // We expect to find beans in a subpackage called xml. That's
108 // where XGG should generate the beans.
109 InputStream serialisedRegistry = getClass().getResourceAsStream( "xml/" + SERIALIZED_BEANREADER);
110 ObjectInputStream ois = new ObjectInputStream(serialisedRegistry);
111 return (BeanReader) ois.readObject();
112 } catch( IOException e ) {
113 LogFactory.getLog(BetwixtPlugin.class).warn("NON FATAL: Couldn't read XMLBeanInfoRegistry : " + e.getMessage() );
114 // throw new IllegalStateException( e.getMessage() );
115 } catch( ClassNotFoundException e ) {
116 LogFactory.getLog(BetwixtPlugin.class).warn("NON FATAL: Couldn't read XMLBeanInfoRegistry : " + e.getMessage() );
117 // throw new IllegalStateException( e.getMessage() );
118 } catch( Exception e ) {
119 LogFactory.getLog(BetwixtPlugin.class).warn("NON FATAL: Couldn't read XMLBeanInfoRegistry : " + e.getMessage() );
120 // throw new IllegalStateException( e.getMessage() );
121 }
122 return result;
123 }
124
125 /***
126 * <p>
127 * Parses an XML stream into a bean. This method can be used by subclasses
128 * to merge in additional content. The result should be cast to the
129 * expected Bean class.
130 * </p>
131 * <p>
132 * The stream will be sought for on the classpath, and the mergeFile
133 * should be a relative path.
134 * </p>
135 *
136 * @param mergeFile a relative path to a resource containing XML.
137 * @return a Java Bean, or null if the mergeFile wasn't found.
138 * @throws XDocletException if the file can't be properly parsed.
139 */
140 protected final Object parse( String mergeFile ) throws XDocletException {
141 if( mergeFile == null ) {
142 throw new XDocletException( "mergeFile can't be null" );
143 }
144 URL url = getXDoclet().getClasspathManager().getResource( mergeFile );
145 if( url == null ) {
146 String classpath = getXDoclet().getClasspathManager().getClasspath();
147 String niceClasspath = ClasspathManager.getNiceClasspath( classpath );
148 // TODO: only info here
149 LogFactory.getLog(BetwixtPlugin.class).error( "Couldn't find " + mergeFile + " on classpath:" + niceClasspath );
150 return null;
151 } else {
152 return parse( url );
153 }
154 }
155
156 private final Object parse( URL url ) throws XDocletException {
157 if( url == null ) {
158 throw new XDocletException( "url can't be null" );
159 }
160 try {
161 return parse( new InputStreamReader( url.openStream() ), url.toExternalForm() );
162 } catch( IOException e ) {
163 LogFactory.getLog(BetwixtPlugin.class).error("Couldn't parse " + url.toExternalForm());
164 throw new XDocletException( e );
165 }
166 }
167
168 /***
169 * @todo Send the content through Jelly before sending it to Betwixt.
170 */
171 private final Object parse( Reader in, String location ) throws XDocletException {
172 try {
173 System.out.println("********************************* PARSING MERGE FILE " + location);
174 Object result = _beanReader.parse( in );
175 System.out.println("################################# DONE PARSING MERGE FILE " + location);
176 return result;
177 } catch( IOException e ) {
178 LogFactory.getLog(BetwixtPlugin.class).error("Couldn't parse " + location);
179 throw new XDocletException( e );
180 } catch( SAXException e ) {
181 LogFactory.getLog(BetwixtPlugin.class).error("Couldn't parse " + location);
182 throw new XDocletException( e );
183 }
184 }
185
186 protected final void generate(File file, Object metaData) throws IOException, XDocletException {
187 XGGPojo root = createRoot();
188 populate(root, metaData);
189 generate( file, root );
190 }
191
192 protected final void generate(File file, Collection metaData) throws IOException, XDocletException {
193 XGGPojo root = createRoot();
194 populate(root, metaData);
195 generate( file, root );
196 }
197
198 private final void generate( File file, XGGPojo root ) throws IOException, XDocletException {
199 Writer writer = new FileWriter(file);
200 BeanWriter beanWriter = new BeanWriter(writer);
201 if( _beanReader != null ) {
202 beanWriter.setXMLIntrospector( _beanReader.getXMLIntrospector() );
203 } else {
204 XMLIntrospector introspector = new XMLIntrospector();
205 BetwixtConfigurer.configureIntrospector( introspector, null );
206 }
207
208 try {
209 beanWriter.setIndent(" ");
210 beanWriter.setEndOfLine(System.getProperty("line.separator"));
211 beanWriter.writeXmlDeclaration("<?xml version=\"1.0\" encoding=\"" + getEncoding() + "\"?>");
212 beanWriter.write(root);
213 beanWriter.flush();
214 writer.flush();
215 writer.close();
216 } catch (IOException e) {
217 throw e;
218 } catch (SAXException e) {
219 LogFactory.getLog(BetwixtPlugin.class).error("Failed to write XML", e);
220 throw new XDocletException( e );
221 } catch (IntrospectionException e) {
222 LogFactory.getLog(BetwixtPlugin.class).error("Failed to write XML", e);
223 throw new XDocletException( e );
224 }
225 }
226
227 /***
228 * Populates the XML tree.
229 * @param root Object representing the root element in the XML.
230 * @param metaData data that can be used to populate the XML tree.
231 * @throws XDocletException if population fails.
232 */
233 protected void populate(XGGPojo root, Object metaData) throws XDocletException {
234 throw new UnsupportedOperationException("This method should be implemented in " + getClass().getName() );
235 }
236
237 /***
238 * Populates the XML tree.
239 * @param root Object representing the root element in the XML.
240 * @param metaData data that can be used to populate the XML tree.
241 * @throws XDocletException if population fails.
242 */
243 protected void populate(XGGPojo root, Collection metaData) throws XDocletException {
244 throw new UnsupportedOperationException("This method should be implemented in " + getClass().getName() );
245 }
246
247 private XGGPojo createRoot() {
248 try {
249 return (XGGPojo) _rootClass.newInstance();
250 } catch (Exception e) {
251 LogFactory.getLog(BetwixtPlugin.class).error("Failed to instantiate a new XGGPojo: " + _rootClass.getName(), e );
252 throw new IllegalArgumentException("Can't instantiate with " + _rootClass.getName() );
253 }
254 }
255 }
This page was automatically generated by Maven