top / index / prev / next / target / source
日記形式でつづる いがぴょんコラム ウェブページです。
SAXについて ある程度 具象化していくと生産性と保守性が向上するのではないかと考えています。
プログレスダイアログを表示したかったのですが、最終的には ProgressServiceを利用することにしました。 ProgressService利用サンプル
プラグインプロジェクト
Plug-in with a popup menuで動作確認
public void run(IAction action) {
try {
PlatformUI.getWorkbench().getProgressService().busyCursorWhile(
new IRunnableWithProgress() {
public void run(IProgressMonitor monitor)
throws InvocationTargetException,
InterruptedException {
try {
monitor.beginTask("うそうそタスク", 100);
for (int index = 0; index < 100; index++) {
if (monitor.isCanceled()) {
break;
}
monitor.subTask("処理[" + index + "]");
monitor.worked(1);
Thread.sleep(50);
}
} finally {
monitor.done();
}
}
});
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
私の気のせいかも知れませんが、このProgressServiceを利用するたびに 300KB程度のメモリリークが発生しているような気がします。(Eclipse 3.0.2および Eclipse 3.1.0において)他の方でも同様の現象の発生に成功された方は、是非 Eclipseプロジェクトにバグ報告していただけると幸いです。
SAXの具象化について、blancoIgベースで試作しました。入力データをもとに SAXインタフェースを具象化します。(XMLを手がかりにアクセサソースコードを生成するというスタイルは、Relaxerとやや似たアプローチとなっていると思われます。)これは、整備してblancoSOAP に合流させていきます。
残項目は下記のようになります。
ConcreteSax で 今は localNameで分岐していますが、これをqNameベースで分岐したほうが精度が上がるように考えます。(その結果 分岐が ほぼユニークになると考えます。)
今はコンテンツハンドラのSAX具象化だけですが、コンテンツシリアライザのSAX具象化にも価値があると考えます。
サンプルデータのサイズ次第で、charactersが一発モードあるいはパケットモードと分岐して生成すべきと考えています。 BlancoXml2ConcreteSax.java
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Stack;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.sax.SAXResult;
import javax.xml.transform.stream.StreamSource;
import org.xml.sax.Attributes;
import org.xml.sax.ContentHandler;
import org.xml.sax.Locator;
import org.xml.sax.SAXException;
import blanco.ig.expander.ClassExpander;
import blanco.ig.expander.NameAdjuster;
import blanco.ig.expander.Type;
import blanco.ig.expander.Value;
import blanco.ig.expander.field.PrivateField;
import blanco.ig.expander.method.MethodExpander;
import blanco.ig.generator.GeneratorSetting;
import blanco.ig.generator.ImplementGenerator;
import blanco.ig.service.ServiceClass;
/**
* 解析用XMLを入力して具象化SAXを生成します。<br>
* 入力する解析用XMLファイルは、恣意的なものを準備します。現時点では かなり限定的なものになります。<br>
* 仮定1: 解析用XMLのアトリビュートはすべて指定されて来るものと仮定します。<br>
* 仮定2: 異なる階層において同じ名前のエンティティが存在しないものと仮定します。<br>
* 仮定3: implements ContentHandler を現状では生成後ファイルに追記の必要があります。<br>
* 参考:http://java.sun.com/j2se/1.5.0/ja/docs/ja/api/org/xml/sax/ContentHandler.html<br>
* TODO: charatersについて、集約の上呼びわけを行うべきかどうかを考察。
*
* @author IGA Tosiki
*/
public class BlancoXml2ConcreteSax {
private static final NameAdjuster adjuster = new NameAdjuster();
// 解析しているXMLファイルの各種情報を記憶します。
/**
* エレメントのスタック構造を記憶します。
*/
private final Stack elementStack = new Stack();
/**
* エレメントをハッシュテーブル形式で記憶します。
*/
private final Hashtable currentElementHashtable = new Hashtable();
/**
* 登場したエレメントすべてを記憶します。
*/
private final Hashtable totalElementHashtable = new Hashtable();
/**
*
* @param args
*/
public static void main(String[] args) {
new BlancoXml2ConcreteSax().process(new File("./meta/web.xml"),
"blanco", "myPackage", "MyConcreteSax2");
}
/**
* ソースコード生成処理を実行します。
*
* @param sourceFile
* @param targetDirectory
* @param packageName
* @param className
*/
public void process(final File sourceFile, final String targetDirectory,
final String packageName, final String className) {
final ClassExpander classExpander = getClassExpander(sourceFile,
packageName, className);
// 実際にJavaソースコードを生成します。
GeneratorSetting setting = new GeneratorSetting();
// 出力先ディレクトリを設定します。
setting.setWorkDirectory(targetDirectory);
ImplementGenerator implementGenerator = new ImplementGenerator(
new ServiceClass("DummyService"), setting);
// 作成したクラスを登録します。
implementGenerator.addMain(classExpander);
try {
// 実際にソースコード生成を実行します。
implementGenerator.generate();
// System.out.println("ソースコード生成が完了しました。");
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 実際のクラス展開を実行します。
*
* @param sourceFile
* @param packageName
* @param className
* @return
*/
private ClassExpander getClassExpander(final File sourceFile,
final String packageName, final String className) {
return new ClassExpander(new Type(packageName, className)) {
protected boolean isAbstract() {
return true;
}
protected void expandClassStruct() {
addFileComment(className + "具象化SAXクラス<br>");
getJavaDoc().addLine(className + "具象化SAXクラス<br>");
getJavaDoc().addLine(
"このクラスは解析用XMLファイルを入力として具象化SAXクラスとして生成されました。<br>");
getJavaDoc().addLine("このソースコードはblancoIgにより機械的に自動生成されています。");
getJavaDoc()
.addLine(
"TODO implements ContentHandler を書き加えてください。(blancoIgの不足機能への対応のため。)");
getJavaDoc().addLine("");
getJavaDoc().addLine("@author IGA Tosiki");
// TODO blancoIgの仕様上 implements ContentHandler が足りません。
// フィールド変数を宣言します。
PrivateField field1 = new PrivateField(new Type(
"java.util.Stack"), "elmentStack") {
};
field1.setLiteralValue("new Stack()");
field1.getJavaDoc().addLine("エレメントのスタックです<br>");
field1.getJavaDoc().addLine("XMLの階層構造を記憶します。");
addField(field1);
InputStream inStream = null;
SAXResult result = new SAXResult(new ContentHandler() {
public void setDocumentLocator(Locator arg0) {
}
public void startDocument() throws SAXException {
}
public void endDocument() throws SAXException {
addMethod(new MethodExpander("startElement") {
public void setupSignature() {
setFinal(true);
getJavaDoc()
.addLine(
"オリジナルのstartElementが呼び出されたので、具象メソッドに呼びわけます");
addException(new Type(
"org.xml.sax.SAXException"));
getJavaDoc().addParameter("uri", "名前空間URI");
addArgument(new Value(new Type(
"java.lang.String"), "uri"));
getJavaDoc().addParameter("localName", "ローカル名");
addArgument(new Value(new Type(
"java.lang.String"), "localName"));
getJavaDoc().addParameter("qName", "接頭辞付きの修飾名");
addArgument(new Value(new Type(
"java.lang.String"), "qName"));
getJavaDoc()
.addParameter("atts", "アトリビュートのリスト");
addArgument(new Value(new Type(
"org.xml.sax.Attributes"), "atts"));
}
public void implement() {
getData().addLine(
"_elmentStack.push(localName);");
Enumeration enumeration = totalElementHashtable
.keys();
for (boolean isFirst = true; enumeration
.hasMoreElements(); isFirst = false) {
StringBuffer lineStatement = new StringBuffer();
String key = (String) enumeration
.nextElement();
getData().addLine(
(isFirst ? "" : "else ")
+ "if (localName.equals(\""
+ key + "\")) {");
lineStatement.append("startElement"
+ adjuster.toClassName(key)
+ "(uri, qName");
ArrayList atts = (ArrayList) totalElementHashtable
.get(key);
final int attrLength = atts.size();
for (int index = 0; index < attrLength; index++) {
// System.out.println("attr:"
// + (String) atts.get(index));
if (index == 0) {
lineStatement.append(", ");
} else {
lineStatement.append(", ");
}
lineStatement.append("atts.getValue(\""
+ (String) atts.get(index)
+ "\")");
}
lineStatement.append(");");
getData().addLine(lineStatement.toString());
getData().addLine("}");
}
}
});
addMethod(new MethodExpander("endElement") {
public void setupSignature() {
setFinal(true);
getJavaDoc()
.addLine(
"オリジナルのendElementが呼び出されたので、具象メソッドに呼びわけます");
addException(new Type(
"org.xml.sax.SAXException"));
getJavaDoc().addParameter("uri", "名前空間URI");
addArgument(new Value(new Type(
"java.lang.String"), "uri"));
getJavaDoc().addParameter("localName", "ローカル名");
addArgument(new Value(new Type(
"java.lang.String"), "localName"));
getJavaDoc().addParameter("qName", "接頭辞付きの修飾名");
addArgument(new Value(new Type(
"java.lang.String"), "qName"));
}
public void implement() {
getData()
.addLine(
"final String currentElementOnStack = (String) _elmentStack.peek();");
getData()
.addLine(
"if (currentElementOnStack.equals(localName) == false) {");
getData()
.addLine(
"throw new SAXException(\"XML異常。期待するエレメント[\" + currentElementOnStack + \"]とローカル名[\" + localName + \"]とがずれています.\");");
getData().addLine("}");
Enumeration enumeration = totalElementHashtable
.keys();
for (boolean isFirst = true; enumeration
.hasMoreElements(); isFirst = false) {
StringBuffer lineStatement = new StringBuffer();
String key = (String) enumeration
.nextElement();
getData().addLine(
(isFirst ? "" : "else ")
+ "if (localName.equals(\""
+ key + "\")) {");
lineStatement.append("endElement"
+ adjuster.toClassName(key)
+ "(uri, qName);");
getData().addLine(lineStatement.toString());
getData().addLine("}");
}
getData().addLine("// 最後にポップして階層をひとつ戻します。");
getData().addLine("_elmentStack.pop();");
}
});
addMethod(new MethodExpander("characters") {
public void setupSignature() {
setFinal(true);
getJavaDoc()
.addLine(
"オリジナルのcharactersが呼び出されたので、具象メソッドに呼びわけます");
getJavaDoc()
.addLine(
"char[]で与えられたデータをStringに詰め替えた上で 呼びわけを行います。");
addException(new Type(
"org.xml.sax.SAXException"));
getJavaDoc().addParameter("ch", "XML文書の文字");
addArgument(new Value(new Type("char[]"), "ch"));
getJavaDoc().addParameter("start", "配列内の開始位置");
addArgument(new Value(new Type("int"), "start"));
getJavaDoc().addParameter("length",
"配列から読み取る文字数");
addArgument(new Value(new Type("int"), "length"));
}
public void implement() {
getData()
.addLine(
"final String currentElementOnStack = (String) _elmentStack.peek();");
Enumeration enumeration = totalElementHashtable
.keys();
for (boolean isFirst = true; enumeration
.hasMoreElements(); isFirst = false) {
StringBuffer lineStatement = new StringBuffer();
String key = (String) enumeration
.nextElement();
getData()
.addLine(
(isFirst ? "" : "else ")
+ "if (currentElementOnStack.equals(\""
+ key + "\")) {");
lineStatement
.append("characters"
+ adjuster.toClassName(key)
+ "(new String(ch, start, length));");
getData().addLine(lineStatement.toString());
getData().addLine("}");
}
}
});
addMethod(new MethodExpander("ignorableWhitespace") {
public void setupSignature() {
setFinal(true);
getJavaDoc()
.addLine(
"オリジナルのignorableWhitespaceが呼び出されたので、具象メソッドに呼びわけます");
getJavaDoc()
.addLine(
"char[]で与えられたデータをStringに詰め替えた上で 呼びわけを行います。");
addException(new Type(
"org.xml.sax.SAXException"));
getJavaDoc().addParameter("ch", "XML文書の文字");
addArgument(new Value(new Type("char[]"), "ch"));
getJavaDoc().addParameter("start", "配列内の開始位置");
addArgument(new Value(new Type("int"), "start"));
getJavaDoc().addParameter("length",
"配列から読み取る文字数");
addArgument(new Value(new Type("int"), "length"));
}
public void implement() {
getData()
.addLine(
"final String currentElementOnStack = (String) _elmentStack.peek();");
Enumeration enumeration = totalElementHashtable
.keys();
for (boolean isFirst = true; enumeration
.hasMoreElements(); isFirst = false) {
StringBuffer lineStatement = new StringBuffer();
String key = (String) enumeration
.nextElement();
getData()
.addLine(
(isFirst ? "" : "else ")
+ "if (currentElementOnStack.equals(\""
+ key + "\")) {");
lineStatement
.append("ignorableWhitespace"
+ adjuster.toClassName(key)
+ "(new String(ch, start, length));");
getData().addLine(lineStatement.toString());
getData().addLine("}");
}
}
});
addMethod(new MethodExpander("setDocumentLocator") {
// メソッドのシグニチャ指定
public void setupSignature() {
getJavaDoc().addLine("このメソッドを無視するためのメソッドです。");
addArgument(new Value(new Type(
"org.xml.sax.Locator"), "locator"));
}
public void implement() {
}
});
addMethod(new MethodExpander("startPrefixMapping") {
// メソッドのシグニチャ指定
public void setupSignature() {
addException(new Type(
"org.xml.sax.SAXException"));
addArgument(new Value(new Type(
"java.lang.String"), "prefix"));
addArgument(new Value(new Type(
"java.lang.String"), "uri"));
}
public void implement() {
}
});
addMethod(new MethodExpander("endPrefixMapping") {
// メソッドのシグニチャ指定
public void setupSignature() {
addException(new Type(
"org.xml.sax.SAXException"));
addArgument(new Value(new Type(
"java.lang.String"), "prefix"));
}
public void implement() {
}
});
addMethod(new MethodExpander("processingInstruction") {
// メソッドのシグニチャ指定
public void setupSignature() {
addException(new Type(
"org.xml.sax.SAXException"));
addArgument(new Value(new Type(
"java.lang.String"), "target"));
addArgument(new Value(new Type(
"java.lang.String"), "data"));
}
public void implement() {
}
});
addMethod(new MethodExpander("skippedEntity") {
// メソッドのシグニチャ指定
public void setupSignature() {
addException(new Type(
"org.xml.sax.SAXException"));
addArgument(new Value(new Type(
"java.lang.String"), "name"));
}
public void implement() {
}
});
}
public void startPrefixMapping(String arg0, String arg1)
throws SAXException {
}
public void endPrefixMapping(String arg0)
throws SAXException {
}
/**
* startElementが呼び出された際に、一通りのメソッドの作成を行います。
*/
public void startElement(final String uri,
final String localName, final String qName,
final Attributes atts) throws SAXException {
final ArrayList listAttr = new ArrayList();
elementStack.push(localName);
currentElementHashtable.put("localName", localName);
if (totalElementHashtable.get(localName) != null) {
// すでに登場したエレメントです。
// 処理はスキップします。
return;
}
totalElementHashtable.put(localName, listAttr);
addMethod(new MethodExpander("startElement"
+ adjuster.toClassName(localName)) {
// メソッドのシグニチャ指定
protected boolean isAbstract() {
return true;
}
public void setupSignature() {
getJavaDoc().addLine(
"startElementがローカル名[" + localName
+ "]で呼び出されました。<br>");
getJavaDoc().addLine(
"※ローカル名はメソッド名に含まれるので引数からは除かれています。");
addException(new Type(
"org.xml.sax.SAXException"));
getJavaDoc().addParameter("uri", "名前空間URI");
addArgument(new Value(new Type(
"java.lang.String"), "uri"));
getJavaDoc().addParameter("qName", "接頭辞付きの修飾名");
addArgument(new Value(new Type(
"java.lang.String"), "qName"));
final int attrLength = atts.getLength();
for (int index = 0; index < attrLength; index++) {
// System.out.println(atts.getLocalName(index)
// + "=" + atts.getValue(index));
getJavaDoc()
.addParameter(
"attr"
+ adjuster
.toClassName(atts
.getLocalName(index)),
"アトリビュート["
+ atts
.getLocalName(index)
+ "]の値が渡されます。");
addArgument(new Value(new Type(
"java.lang.String"), "attr"
+ adjuster.toClassName(atts
.getLocalName(index))));
listAttr.add(atts.getLocalName(index));
}
}
public void implement() {
}
});
addMethod(new MethodExpander("endElement"
+ adjuster.toClassName(localName)) {
protected boolean isAbstract() {
return true;
}
// メソッドのシグニチャ指定
public void setupSignature() {
getJavaDoc().addLine(
"endElementがローカル名[" + localName
+ "]で呼び出されました。<br>");
getJavaDoc().addLine(
"※ローカル名はメソッド名に含まれるので引数からは除かれています。");
addException(new Type(
"org.xml.sax.SAXException"));
getJavaDoc().addParameter("uri", "名前空間URI");
addArgument(new Value(new Type(
"java.lang.String"), "uri"));
getJavaDoc().addParameter("qName", "接頭辞付きの修飾名");
addArgument(new Value(new Type(
"java.lang.String"), "qName"));
}
public void implement() {
}
});
addMethod(new MethodExpander("characters"
+ adjuster.toClassName(localName)) {
// メソッドのシグニチャ指定
protected boolean isAbstract() {
return true;
}
public void setupSignature() {
getJavaDoc().addLine(
"charactersがローカル名[" + localName
+ "]で呼び出されました。<br>");
getJavaDoc()
.addLine(
"もとのcharactersメソッドでは char[]ですが、Stringに詰め替えた上でメソッドが呼び出されます。");
addException(new Type(
"org.xml.sax.SAXException"));
getJavaDoc()
.addParameter("data",
"java.lang.String型に詰め替え済みの charactersイベントデータ");
addArgument(new Value(new Type("String"),
"data"));
}
public void implement() {
}
});
addMethod(new MethodExpander("ignorableWhitespace"
+ adjuster.toClassName(localName)) {
// メソッドのシグニチャ指定
protected boolean isAbstract() {
return true;
}
public void setupSignature() {
getJavaDoc()
.addLine(
"ignorableWhitespaceがローカル名["
+ localName
+ "]で呼び出されました。<br>");
getJavaDoc()
.addLine(
"もとのcharactersメソッドでは char[]ですが、Stringに詰め替えた上でメソッドが呼び出されます。");
addException(new Type(
"org.xml.sax.SAXException"));
getJavaDoc()
.addParameter("data",
"java.lang.String型に詰め替え済みの charactersイベントデータ");
addArgument(new Value(new Type("String"),
"data"));
}
public void implement() {
}
});
}
public void endElement(String uri, String localName,
String qName) throws SAXException {
currentElementHashtable.put("localName", elementStack
.pop());
}
public void characters(char[] ch, int start, int length)
throws SAXException {
// System.out.println("characters");
}
public void ignorableWhitespace(char[] arg0, int arg1,
int arg2) throws SAXException {
// System.out.println("ignorableWhitespace");
}
public void processingInstruction(String arg0, String arg1)
throws SAXException {
}
public void skippedEntity(String arg0) throws SAXException {
// System.out.println("skippedEntity");
}
});
try {
inStream = new FileInputStream(sourceFile);
TransformerFactory tf = TransformerFactory.newInstance();
Transformer transformer;
transformer = tf.newTransformer();
transformer.transform(new StreamSource(inStream), result);
} catch (TransformerConfigurationException e) {
e.printStackTrace();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (TransformerException e) {
e.printStackTrace();
} finally {
try {
inStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
};
}
}
※このソースコードは開発途中段階のアイデアレベルのものです。最終的なもの、あるいは保守され続けていくものは blanco Framework内の blancoSOAP にて維持されていく予定です。