org.apache.axis.wsdl.toJava.JavaWriter Maven / Gradle / Ivy
/*
* Copyright 2001-2004 The Apache Software Foundation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.axis.wsdl.toJava;
import org.apache.axis.utils.Messages;
import org.apache.axis.wsdl.gen.Generator;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import java.io.*;
import java.util.StringTokenizer;
/**
* Emitter knows about WSDL writers, one each for PortType, Binding, Service,
* Definition, Type. But for some of these WSDL types, Wsdl2java generates
* multiple files. Each of these files has a corresponding writer that extends
* JavaWriter. So the Java WSDL writers (JavaPortTypeWriter, JavaBindingWriter,
* etc.) each calls a file writer (JavaStubWriter, JavaSkelWriter, etc.) for
* each file that that WSDL generates.
*
* For example, when Emitter calls JavaWriterFactory for a Binding Writer, it
* returns a JavaBindingWriter. JavaBindingWriter, in turn, contains a
* JavaStubWriter, JavaSkelWriter, and JavaImplWriter since a Binding may cause
* a stub, skeleton, and impl template to be generated.
*
* Note that the writers that are given to Emitter by JavaWriterFactory DO NOT
* extend JavaWriter. They simply implement Writer and delegate the actual
* task of writing to extensions of JavaWriter.
*
* All of Wsdl2java's Writer implementations follow a common behaviour.
* JavaWriter is the abstract base class that dictates this common behaviour.
* This behaviour is primarily placed within the generate method. The generate
* method calls, in succession (note: the starred methods are the ones you are
* probably most interested in):
*
* - * getFileName
*
- This is an abstract method that must be implemented by the subclass.
* It returns the fully-qualified file name.
*
- isFileGenerated(file)
*
- You should not need to override this method. It checks to see whether
* this file is in the List returned by emitter.getGeneratedFileNames.
*
- registerFile(file)
*
- You should not need to override this method. It registers this file by
* calling emitter.getGeneratedFileInfo().add(...).
*
- * verboseMessage(file)
*
- You may override this method if you want to provide more information.
* The generate method only calls verboseMessage if verbose is turned on.
*
- getPrintWriter(file)
*
- You should not need to override this method. Given the file name, it
* creates a PrintWriter for it.
*
- * writeFileHeader(pw)
*
- You may want to override this method. The default implementation
* generates nothing.
*
- * writeFileBody(pw)
*
- This is an abstract method that must be implemented by the subclass.
* This is where the body of a file is generated.
*
- * writeFileFooter(pw)
*
- You may want to override this method. The default implementation
* generates nothing.
*
- closePrintWriter(pw)
*
- You should not need to override this method. It simply closes the
* PrintWriter.
*
*/
public abstract class JavaWriter implements Generator {
/** This controls how many characters per line for javadoc comments */
protected final static int LINE_LENGTH = 65;
/** Field emitter */
protected Emitter emitter;
/** Field type */
protected String type;
/**
* Constructor.
*
* @param emitter
* @param type
*/
protected JavaWriter(Emitter emitter, String type) {
this.emitter = emitter;
this.type = type;
} // ctor
/**
* Generate a file.
*
* @throws IOException
*/
public void generate() throws IOException {
String file = getFileName();
if (isFileGenerated(file)) {
throw new DuplicateFileException(
Messages.getMessage("duplicateFile00", file), file);
}
registerFile(file);
if (emitter.isVerbose()) {
String msg = verboseMessage(file);
if (msg != null) {
System.out.println(msg);
}
}
PrintWriter pw = getPrintWriter(file);
writeFileHeader(pw);
writeFileBody(pw);
writeFileFooter(pw);
closePrintWriter(pw);
} // generate
/**
* This method must be implemented by a subclass. It
* returns the fully-qualified name of the file to be
* generated.
*
* @return
*/
protected abstract String getFileName();
/**
* You should not need to override this method. It checks
* to see whether the given file is in the List returned
* by emitter.getGeneratedFileNames.
*
* @param file
* @return
*/
protected boolean isFileGenerated(String file) {
return emitter.getGeneratedFileNames().contains(file);
} // isFileGenerated
/**
* You should not need to override this method.
* It registers the given file by calling
* emitter.getGeneratedFileInfo().add(...).
*
* @param file
*/
protected void registerFile(String file) {
emitter.getGeneratedFileInfo().add(file, null, type);
} // registerFile
/**
* Return the string: "Generating ". Override this
* method if you want to provide more information.
*
* @param file
* @return
*/
protected String verboseMessage(String file) {
return Messages.getMessage("generating", file);
} // verboseMessage
/**
* You should not need to override this method.
* Given the file name, it creates a PrintWriter for it.
*
* @param filename
* @return
* @throws IOException
*/
protected PrintWriter getPrintWriter(String filename) throws IOException {
File file = new File(filename);
File parent = new File(file.getParent());
parent.mkdirs();
FileOutputStream out = new FileOutputStream(file);
OutputStreamWriter writer = new OutputStreamWriter(out, "UTF-8");
return new PrintWriter(writer);
} // getPrintWriter
/**
* This method is intended to be overridden as necessary
* to generate file header information. This default
* implementation does nothing.
*
* @param pw
* @throws IOException
*/
protected void writeFileHeader(PrintWriter pw)
throws IOException {
} // writeFileHeader
/**
* This method must be implemented by a subclass. This
* is where the body of a file is generated.
*
* @param pw
* @throws IOException
*/
protected abstract void writeFileBody(PrintWriter pw) throws IOException;
/**
* You may want to override this method. This default
* implementation generates nothing.
*
* @param pw
* @throws IOException
*/
protected void writeFileFooter(PrintWriter pw)
throws IOException {
} // writeFileFooter
/**
* Close the print writer.
*
* @param pw
*/
protected void closePrintWriter(PrintWriter pw) {
pw.close();
} // closePrintWriter
/**
* Takes out new lines and wraps at Javadoc tags
* @param documentation the raw comments from schema
* @param addTab if true adds a tab character when wrapping (methods)
*/
protected String getJavadocDescriptionPart(String documentation, boolean addTab) {
if (documentation == null) {
return "";
}
String doc = documentation.trim();
if (documentation.trim().length() == 0) {
//nothing to do
return doc;
}
// make @ tags start a new line (for javadoc tags mostly)
StringTokenizer st = new StringTokenizer(doc, "@");
StringBuffer newComments;
if (st.hasMoreTokens()) {
String token = st.nextToken();
boolean startLine = Character.isWhitespace(token.charAt(token.length() - 1))
&& (token.charAt(token.length() - 1) != '\n');
newComments = new StringBuffer(token);
while (st.hasMoreTokens()) {
token = st.nextToken();
// don't span links across lines
if (startLine) {
newComments.append('\n');
}
newComments.append('@');
startLine = Character.isWhitespace(token.charAt(token.length() - 1))
& (token.charAt(token.length() - 1) != '\n');
newComments.append(token);
}
} else {
newComments = new StringBuffer(doc);
}
newComments.insert(0, addTab ? " * " : " * ");
// tweak comment ending tags by insterting a
// space between the star and the slash, BUG13407
int pos = newComments.toString().indexOf("*/");
while (pos >= 0) {
newComments.insert(pos + 1, ' ');
pos = newComments.toString().indexOf("*/");
}
// now pretty it up based on column length
int lineStart = 0;
int newlinePos = 0;
while (lineStart < newComments.length()) {
newlinePos = newComments.toString().indexOf("\n", lineStart);
if (newlinePos == -1) {
newlinePos = newComments.length();
}
if ((newlinePos - lineStart) > LINE_LENGTH) {
// find first whitespace after length
lineStart += LINE_LENGTH;
while ((lineStart < newComments.length())
&& !Character.isWhitespace(newComments.charAt(lineStart))) {
lineStart++;
}
if (lineStart < newComments.length()) {
// don't insert if line wold break at EOF
char next = newComments.charAt(lineStart);
// insert new line header
if ((next == '\r') || (next == '\n')) {
//newline exists at the break point, don't put in another one
newComments.insert(lineStart + 1, addTab ? " * " : " * ");
lineStart += addTab ? 8 : 4;
} else {
newComments.insert(lineStart, addTab ? "\n * " : "\n * ");
lineStart += addTab ? 8 : 4;
}
}
// chew up witespace after newline
while ((lineStart < newComments.length())
&& (newComments.charAt(lineStart) == ' ')) { // only chew up simple spaces
newComments.delete(lineStart, lineStart + 1);
}
} else {
if (++newlinePos < newComments.length()) {
newComments.insert(newlinePos, addTab ? " * " : " * ");
}
lineStart = newlinePos;
lineStart += addTab ? 7 : 3;
}
}
return newComments.toString();
}
/**
* Output a documentation element as a Java comment.
*
* @param pw
* @param element
*/
protected void writeComment(PrintWriter pw, Element element) {
writeComment(pw, element, true);
}
/**
* Output a documentation element as a Java comment.
*
* @param pw
* @param element
* @param addTab
*/
protected void writeComment(PrintWriter pw, Element element, boolean addTab) {
if (element == null) {
return;
}
Node child = element.getFirstChild();
if (child == null) {
return;
}
String comment = child.getNodeValue();
if (comment != null) {
int start = 0;
pw.println(); // blank line
pw.println(addTab ? " /**" : "/**");
pw.println(getJavadocDescriptionPart(comment, addTab));
pw.println(addTab ? " */" : " */");
}
} // writeComment
} // abstract class JavaWriter