S Project~ソフトウェア実験室~

メモ Java

JSP以外でEL式(Expression Language)を使用する
JSP2.1をサポートするサーバと、ApacheプロジェクトのCommons ELを使います。

まずは変数の変換です。
下記のようなクラスを作成します。
ここでは単純にMapを使って、EL式「${変数}」と実際の変数の対応を定義しています。
resolveVariableで変数名に対応する変数を返せばいいので、お好みに変更してください。
import javax.servlet.jsp.el.VariableResolver;

@SuppressWarnings("deprecation")
public class MyVariableResolver implements VariableResolver {

	private final Map<String, Object> map;
	
	public MyVariableResolver(Map<String, Object> map) {
		this.map = map;
	}

	@Override
	public Object resolveVariable(String pName) {
		return map.get(pName);
	}
}
					
次はメソッドのマッピングです。
resolveFunctionメソッドでEL式「startWiths」とMyFunctionMapper.myStartWithsメソッドを対応付けています。
「${f:startWiths(string,"abc")」のように、ELから関数が呼ばれた場合はresolveFunctionメソッドが呼ばれます。
この場合uriは「f」、functionsには「startWiths」が入ります。
後はその内容を元に、対応するメソッドを返せばOKです。
メソッドはリフレクションを使って取得します。なお、メソッドはstaticメソッドでなければなりません。
import javax.servlet.jsp.el.FunctionMapper;

@SuppressWarnings("deprecation")
public class MyFunctionMapper implements FunctionMapper {

	@Override
	public Method resolveFunction(String uri, String function) {
		if (uri.equals("f")) {
			return MyFunctionMapper.class.getMethod("myStartWiths", String.class, String.class);
		}
		return null;
	}

	public static String myStartWiths(String string, String prefix) {
		return string.startWiths(prefix);
	}
}
					
変数とメソッドのマッピングが終わったら、それを呼び出す関数を作ります。
下記のような関数を作成して、ELから参照する変数をMapに格納して呼び出せばELの結果が返ります。
ELの内容が「${変数}」であれば、その変数の型のオブジェクトが、「abc${変数}」のように、前後に文字列等がある場合はtoString()で変換した値に前後の文字列が結合され、文字列として返されます。
import javax.servlet.jsp.el.ELException;

import org.apache.commons.el.Expression;
import org.apache.commons.el.ExpressionString;
import org.apache.commons.el.Logger;
import org.apache.commons.el.parser.ELParser;
import org.apache.commons.el.parser.ParseException;

public static Object resolve(Map<String, Object> map, String text) {
	ELParser parser = new ELParser(new StringReader(text));
	try {
		Object result = parser.ExpressionString();
		if (result instanceof String) {
			return result;
		} else if (result instanceof Expression) {
			Expression exp = (Expression)result;
			MyVariableResolver resolver = new MyVariableResolver(map);
			MyFunctionMapper fresolver = new MyFunctionMapper();
			Logger log = new Logger(System.err);
			return exp.evaluate(resolver , fresolver, log);
		} else {
			ExpressionString exp = (ExpressionString)result;
			MyVariableResolver resolver = new MyVariableResolver(map);
			MyFunctionMapper fresolver = new MyFunctionMapper();
			Logger log = new Logger(System.err);
			return exp.evaluate(resolver , fresolver, log);
		}
	} catch (ParseException e) {
		// エラー処理
		return text;
	} catch (ELException e) {
		// エラー処理
		return text;
	}
}
					
SAXでXML Schemaを使った検証を行う。
下記のコードで検証できます。
Schemaに一致しない場合はSAXExceptionが投げられるのではなく、errorハンドラが呼ばれるのでそちらでトラップして例外投げるなり何なりしてください。
InputStream schemaIn = new FileInputStream("xml_schema.xsd");

SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
SAXParserFactory saxfactory = SAXParserFactory.newInstance();

StreamSource [] schema = new StreamSource[] {
  new SteramSource(schemaIn1),
  new SteramSource(schemaIn2)
};

saxfactory.setSchema(schemaFactory.newSchema(schema));
saxfactory.setNamespaceAware(true);
saxfactory.setValidating(false);	// DTDの検証はしないので、false

InputSource xmlIn = new InputSource(reader);
SAXParser parser = saxfactory.newSAXParser();
parser.parse(xmlIn, new SAXHandlerX());

////////////////////////////////////////////////////
public class SAXHandlerX extends DefaultHandler {
  public void error(SAXParseException e) throws SAXException {
    // TODO ここでトラップ
    // 例
    throw new SAXException();
  }
}
errorハンドラで以下のメッセージがハンドリングされる場合は、DTDの検証が有効になっています。
saxfactory.setValidating(false);
を設定すれば、解消されます。
・Document is invalid: no grammar found.
・Document root element "xxx", must match DOCTYPE root "null".