Source code: org/apache/axis/wsdl/toJava/JavaWriter.java
1 /*
2 * Copyright 2001-2004 The Apache Software Foundation.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16 package org.apache.axis.wsdl.toJava;
17
18 import org.apache.axis.utils.Messages;
19 import org.apache.axis.wsdl.gen.Generator;
20 import org.w3c.dom.Element;
21 import org.w3c.dom.Node;
22
23 import java.io.File;
24 import java.io.FileWriter;
25 import java.io.IOException;
26 import java.io.PrintWriter;
27 import java.util.StringTokenizer;
28
29 /**
30 * Emitter knows about WSDL writers, one each for PortType, Binding, Service,
31 * Definition, Type. But for some of these WSDL types, Wsdl2java generates
32 * multiple files. Each of these files has a corresponding writer that extends
33 * JavaWriter. So the Java WSDL writers (JavaPortTypeWriter, JavaBindingWriter,
34 * etc.) each calls a file writer (JavaStubWriter, JavaSkelWriter, etc.) for
35 * each file that that WSDL generates.
36 * <p/>
37 * <p>For example, when Emitter calls JavaWriterFactory for a Binding Writer, it
38 * returns a JavaBindingWriter. JavaBindingWriter, in turn, contains a
39 * JavaStubWriter, JavaSkelWriter, and JavaImplWriter since a Binding may cause
40 * a stub, skeleton, and impl template to be generated.
41 * <p/>
42 * <p>Note that the writers that are given to Emitter by JavaWriterFactory DO NOT
43 * extend JavaWriter. They simply implement Writer and delegate the actual
44 * task of writing to extensions of JavaWriter.
45 * <p/>
46 * <p>All of Wsdl2java's Writer implementations follow a common behaviour.
47 * JavaWriter is the abstract base class that dictates this common behaviour.
48 * This behaviour is primarily placed within the generate method. The generate
49 * method calls, in succession (note: the starred methods are the ones you are
50 * probably most interested in):
51 * <dl>
52 * <dt> * getFileName
53 * <dd> This is an abstract method that must be implemented by the subclass.
54 * It returns the fully-qualified file name.
55 * <dt> isFileGenerated(file)
56 * <dd> You should not need to override this method. It checks to see whether
57 * this file is in the List returned by emitter.getGeneratedFileNames.
58 * <dt> registerFile(file)
59 * <dd> You should not need to override this method. It registers this file by
60 * calling emitter.getGeneratedFileInfo().add(...).
61 * <dt> * verboseMessage(file)
62 * <dd> You may override this method if you want to provide more information.
63 * The generate method only calls verboseMessage if verbose is turned on.
64 * <dt> getPrintWriter(file)
65 * <dd> You should not need to override this method. Given the file name, it
66 * creates a PrintWriter for it.
67 * <dt> * writeFileHeader(pw)
68 * <dd> You may want to override this method. The default implementation
69 * generates nothing.
70 * <dt> * writeFileBody(pw)
71 * <dd> This is an abstract method that must be implemented by the subclass.
72 * This is where the body of a file is generated.
73 * <dt> * writeFileFooter(pw)
74 * <dd> You may want to override this method. The default implementation
75 * generates nothing.
76 * <dt> closePrintWriter(pw)
77 * <dd> You should not need to override this method. It simply closes the
78 * PrintWriter.
79 * </dl>
80 */
81 public abstract class JavaWriter implements Generator {
82
83 /** This controls how many characters per line for javadoc comments */
84 protected final static int LINE_LENGTH = 65;
85
86 /** Field emitter */
87 protected Emitter emitter;
88
89 /** Field type */
90 protected String type;
91
92 /**
93 * Constructor.
94 *
95 * @param emitter
96 * @param type
97 */
98 protected JavaWriter(Emitter emitter, String type) {
99 this.emitter = emitter;
100 this.type = type;
101 } // ctor
102
103 /**
104 * Generate a file.
105 *
106 * @throws IOException
107 */
108 public void generate() throws IOException {
109
110 String file = getFileName();
111
112 if (isFileGenerated(file)) {
113 throw new DuplicateFileException(
114 Messages.getMessage("duplicateFile00", file), file);
115 }
116
117 registerFile(file);
118
119 if (emitter.isVerbose()) {
120 String msg = verboseMessage(file);
121
122 if (msg != null) {
123 System.out.println(msg);
124 }
125 }
126
127 PrintWriter pw = getPrintWriter(file);
128
129 writeFileHeader(pw);
130 writeFileBody(pw);
131 writeFileFooter(pw);
132 closePrintWriter(pw);
133 } // generate
134
135 /**
136 * This method must be implemented by a subclass. It
137 * returns the fully-qualified name of the file to be
138 * generated.
139 *
140 * @return
141 */
142 protected abstract String getFileName();
143
144 /**
145 * You should not need to override this method. It checks
146 * to see whether the given file is in the List returned
147 * by emitter.getGeneratedFileNames.
148 *
149 * @param file
150 * @return
151 */
152 protected boolean isFileGenerated(String file) {
153 return emitter.getGeneratedFileNames().contains(file);
154 } // isFileGenerated
155
156 /**
157 * You should not need to override this method.
158 * It registers the given file by calling
159 * emitter.getGeneratedFileInfo().add(...).
160 *
161 * @param file
162 */
163 protected void registerFile(String file) {
164 emitter.getGeneratedFileInfo().add(file, null, type);
165 } // registerFile
166
167 /**
168 * Return the string: "Generating <file>". Override this
169 * method if you want to provide more information.
170 *
171 * @param file
172 * @return
173 */
174 protected String verboseMessage(String file) {
175 return Messages.getMessage("generating", file);
176 } // verboseMessage
177
178 /**
179 * You should not need to override this method.
180 * Given the file name, it creates a PrintWriter for it.
181 *
182 * @param filename
183 * @return
184 * @throws IOException
185 */
186 protected PrintWriter getPrintWriter(String filename) throws IOException {
187
188 File file = new File(filename);
189 File parent = new File(file.getParent());
190
191 parent.mkdirs();
192
193 return new PrintWriter(new FileWriter(file));
194 } // getPrintWriter
195
196 /**
197 * This method is intended to be overridden as necessary
198 * to generate file header information. This default
199 * implementation does nothing.
200 *
201 * @param pw
202 * @throws IOException
203 */
204 protected void writeFileHeader(PrintWriter pw)
205 throws IOException {
206 } // writeFileHeader
207
208 /**
209 * This method must be implemented by a subclass. This
210 * is where the body of a file is generated.
211 *
212 * @param pw
213 * @throws IOException
214 */
215 protected abstract void writeFileBody(PrintWriter pw) throws IOException;
216
217 /**
218 * You may want to override this method. This default
219 * implementation generates nothing.
220 *
221 * @param pw
222 * @throws IOException
223 */
224 protected void writeFileFooter(PrintWriter pw)
225 throws IOException {
226 } // writeFileFooter
227
228 /**
229 * Close the print writer.
230 *
231 * @param pw
232 */
233 protected void closePrintWriter(PrintWriter pw) {
234 pw.close();
235 } // closePrintWriter
236
237 /**
238 * Takes out new lines and wraps at Javadoc tags
239 * @param documentation the raw comments from schema
240 * @param addTab if true adds a tab character when wrapping (methods)
241 */
242 protected String getJavadocDescriptionPart(String documentation, boolean addTab) {
243 if (documentation == null) {
244 return "";
245 }
246
247 String doc = documentation.trim();
248
249 if (documentation.trim().length() == 0) {
250 //nothing to do
251 return doc;
252 }
253
254 // make @ tags start a new line (for javadoc tags mostly)
255 StringTokenizer st = new StringTokenizer(doc, "@");
256 StringBuffer newComments;
257 if (st.hasMoreTokens()) {
258 String token = st.nextToken();
259 boolean startLine = Character.isWhitespace(token.charAt(token.length() - 1))
260 && (token.charAt(token.length() - 1) != '\n');
261 newComments = new StringBuffer(token);
262
263 while (st.hasMoreTokens()) {
264 token = st.nextToken();
265 // don't span links across lines
266 if (startLine) {
267 newComments.append('\n');
268 }
269 newComments.append('@');
270 startLine = Character.isWhitespace(token.charAt(token.length() - 1))
271 & (token.charAt(token.length() - 1) != '\n');
272
273 newComments.append(token);
274 }
275 } else {
276 newComments = new StringBuffer(doc);
277 }
278 newComments.insert(0, addTab ? " * " : " * ");
279
280 // tweak comment ending tags by insterting a
281 // space between the star and the slash, BUG13407
282 int pos = newComments.toString().indexOf("*/");
283 while (pos >= 0) {
284 newComments.insert(pos + 1, ' ');
285 pos = newComments.toString().indexOf("*/");
286 }
287
288 // now pretty it up based on column length
289 int lineStart = 0;
290 int newlinePos = 0;
291 while (lineStart < newComments.length()) {
292 newlinePos = newComments.toString().indexOf("\n", lineStart);
293 if (newlinePos == -1) {
294 newlinePos = newComments.length();
295 }
296 if ((newlinePos - lineStart) > LINE_LENGTH) {
297 // find first whitespace after length
298 lineStart += LINE_LENGTH;
299 while ((lineStart < newComments.length())
300 && !Character.isWhitespace(newComments.charAt(lineStart))) {
301 lineStart++;
302 }
303
304 if (lineStart < newComments.length()) {
305 // don't insert if line wold break at EOF
306 char next = newComments.charAt(lineStart);
307 // insert new line header
308 if ((next == '\r') || (next == '\n')) {
309 //newline exists at the break point, don't put in another one
310 newComments.insert(lineStart + 1, addTab ? " * " : " * ");
311 lineStart += addTab ? 8 : 4;
312 } else {
313 newComments.insert(lineStart, addTab ? "\n * " : "\n * ");
314 lineStart += addTab ? 8 : 4;
315 }
316 }
317
318 // chew up witespace after newline
319 while ((lineStart < newComments.length())
320 && (newComments.charAt(lineStart) == ' ')) { // only chew up simple spaces
321 newComments.delete(lineStart, lineStart + 1);
322 }
323 } else {
324 if (++newlinePos < newComments.length()) {
325 newComments.insert(newlinePos, addTab ? " * " : " * ");
326 }
327 lineStart = newlinePos;
328 lineStart += addTab ? 7 : 3;
329 }
330 }
331
332 return newComments.toString();
333 }
334
335 /**
336 * Output a documentation element as a Java comment.
337 *
338 * @param pw
339 * @param element
340 */
341 protected void writeComment(PrintWriter pw, Element element) {
342 writeComment(pw, element, true);
343 }
344
345 /**
346 * Output a documentation element as a Java comment.
347 *
348 * @param pw
349 * @param element
350 * @param addTab
351 */
352 protected void writeComment(PrintWriter pw, Element element, boolean addTab) {
353
354 if (element == null) {
355 return;
356 }
357
358 Node child = element.getFirstChild();
359
360 if (child == null) {
361 return;
362 }
363
364 String comment = child.getNodeValue();
365
366 if (comment != null) {
367 int start = 0;
368
369 pw.println(); // blank line
370
371 pw.println(addTab ? " /**" : "/**");
372 pw.println(getJavadocDescriptionPart(comment, addTab));
373 pw.println(addTab ? " */" : " */");
374 }
375 } // writeComment
376 } // abstract class JavaWriter