/zpath/trunk/src/ak/zpath/PathCreator.java |
---|
0,0 → 1,367 |
package ak.zpath; |
import java.util.List; |
import java.util.ArrayList; |
import java.text.ParseException; |
public class PathCreator |
{ |
private PathElement element; |
private List elements; |
private PathParser pathParser = new PathParser(); |
public PathCreator() |
{ |
} |
public List createPath(String path) |
throws ParseException |
{ |
List tokens = pathParser.parse(path); |
Token firstToken; |
int pos = -1; |
if(tokens.size() == 0) |
throw new PathCreateException("Empty path", null); |
firstToken = (Token)tokens.get(0); |
elements = new ArrayList(); |
if(firstToken.getType() == Token.TOKEN_SLASH) { |
// the path starts with slash |
PathElement slashElement = new PathElement(); |
slashElement.setName("/"); |
elements.add(slashElement); |
pos = 0; |
} |
element = null; |
stateElementStart(tokens, pos); |
if(element != null) elements.add(element); // add the last element |
return elements; |
} |
private int stateElementStart(List tokens, int pos) |
throws PathCreateException |
{ |
Token token = (Token)tokens.get(++pos); |
if(element != null) elements.add(element); |
element = new PathElement(); |
switch(token.getType()) { |
case Token.TOKEN_NAME: |
element.setName(token.getValue()); |
pos = stateElementName(tokens, pos); |
break; |
default: |
throw new PathCreateException("Element name expected", token); |
} |
return pos; |
} |
private int stateElementName(List tokens, int pos) |
throws PathCreateException |
{ |
if(++pos >= tokens.size()) return pos; |
Token token = (Token)tokens.get(pos); |
switch(token.getType()) { |
case Token.TOKEN_SLASH: |
pos = stateElementStart(tokens, pos); |
break; |
case Token.TOKEN_OPEN_BRACKET: |
pos = stateElementCondition(tokens, pos); |
break; |
case Token.TOKEN_SHARP: |
pos = stateOperandText(tokens, pos); |
break; |
case Token.TOKEN_AT: |
pos = stateOperandAttribute(tokens, pos); |
break; |
default: |
throw new PathCreateException( |
"Element condition or operand expected", token); |
} |
return pos; |
} |
private int stateOperandText(List tokens, int pos) |
throws PathCreateException |
{ |
Token token = (Token)tokens.get(++pos); |
element.setFirstOperand(PathElement.OPERAND_TEXT, null); |
switch(token.getType()) { |
case Token.TOKEN_ASSIGN: |
element.setOperation(PathElement.OPERATION_ASSIGN); |
pos = stateSecondOperand(tokens, pos); |
break; |
case Token.TOKEN_APPEND: |
element.setOperation(PathElement.OPERATION_APPEND); |
pos = stateSecondOperand(tokens, pos); |
break; |
default: |
throw new PathCreateException( |
"Assign or append operation expected", token); |
} |
return pos; |
} |
private int stateOperandAttribute(List tokens, int pos) |
throws PathCreateException |
{ |
Token token = (Token)tokens.get(++pos); |
switch(token.getType()) { |
case Token.TOKEN_NAME: { |
element.setFirstOperand(PathElement.OPERAND_ATTRIBUTE, token.getValue()); |
token = (Token)tokens.get(++pos); |
switch(token.getType()) { |
case Token.TOKEN_ASSIGN: |
element.setOperation(PathElement.OPERATION_ASSIGN); |
pos = stateSecondOperand(tokens, pos); |
break; |
case Token.TOKEN_APPEND: |
element.setOperation(PathElement.OPERATION_APPEND); |
pos = stateSecondOperand(tokens, pos); |
break; |
default: |
throw new PathCreateException( |
"Assign or append operation expected", token); |
} |
break; |
} |
default: |
throw new PathCreateException("Attribute name expected", token); |
} |
return pos; |
} |
private int stateSecondOperand(List tokens, int pos) |
throws PathCreateException |
{ |
Token token = (Token)tokens.get(++pos); |
switch(token.getType()) { |
case Token.TOKEN_VARIABLE: |
element.setSecondOperand(PathElement.OPERAND_VARIABLE, token.getValue()); |
if(pos < tokens.size() - 1) |
throw new PathCreateException("Path end expected", token); |
break; |
case Token.TOKEN_STRING: |
element.setSecondOperand(PathElement.OPERAND_STRING, token.getValue()); |
if(pos < tokens.size() - 1) |
throw new PathCreateException("Path end expected", token); |
break; |
default: |
throw new PathCreateException("Second operand expected", token); |
} |
element.validateOperands(); // must be end of the path |
return pos; |
} |
private int stateElementCondition(List tokens, int pos) |
throws PathCreateException |
{ |
Token token = (Token)tokens.get(++pos); |
switch(token.getType()) { |
case Token.TOKEN_NEW: |
pos = stateNew(tokens, pos); |
break; |
case Token.TOKEN_SHARP: |
case Token.TOKEN_AT: |
pos = stateLogicalCondition(tokens, pos); |
break; |
default: |
throw new PathCreateException("Element condition expected", token); |
} |
return pos; |
} |
private int stateLogicalCondition(List tokens, int pos) |
throws PathCreateException |
{ |
Token token = (Token)tokens.get(pos); // do not go to the next token |
switch(token.getType()) { |
case Token.TOKEN_SHARP: |
pos = stateConditionText(tokens, pos); |
break; |
case Token.TOKEN_AT: |
pos = stateConditionAttribute(tokens, pos); |
break; |
default: |
throw new PathCreateException("Element condition expected", token); |
} |
return pos; |
} |
private int stateConditionText(List tokens, int pos) |
throws PathCreateException |
{ |
Token token = (Token)tokens.get(++pos); |
element.addCondition(); |
element.setFirstCondition(PathElement.OPERAND_TEXT, null); |
switch(token.getType()) { |
case Token.TOKEN_EQUAL: |
element.setConditionOperation(PathElement.OPERATION_EQUAL); |
pos = stateConditionSecond(tokens, pos); |
break; |
default: |
throw new PathCreateException("Compare operator expected", token); |
} |
return pos; |
} |
private int stateConditionAttribute(List tokens, int pos) |
throws PathCreateException |
{ |
Token token = (Token)tokens.get(++pos); |
element.addCondition(); |
switch(token.getType()) { |
case Token.TOKEN_NAME: { |
element.setFirstCondition( |
PathElement.OPERAND_ATTRIBUTE, token.getValue()); |
token = (Token)tokens.get(++pos); |
switch(token.getType()) { |
case Token.TOKEN_EQUAL: |
element.setConditionOperation(PathElement.OPERATION_EQUAL); |
pos = stateConditionSecond(tokens, pos); |
break; |
default: |
throw new PathCreateException("Compare operation expected", token); |
} |
break; |
} |
default: |
throw new PathCreateException("Attibute name expected", token); |
} |
return pos; |
} |
private int stateConditionSecond(List tokens, int pos) |
throws PathCreateException |
{ |
Token token = (Token)tokens.get(++pos); |
switch(token.getType()) { |
case Token.TOKEN_VARIABLE: |
element.setSecondCondition( |
PathElement.OPERAND_VARIABLE, token.getValue()); |
break; |
case Token.TOKEN_STRING: |
element.setSecondCondition( |
PathElement.OPERAND_STRING, token.getValue()); |
break; |
default: |
throw new PathCreateException( |
"Second condition operand expected", token); |
} |
element.validateCondition(); // must be end of condition |
token = (Token)tokens.get(++pos); |
switch(token.getType()) { |
case Token.TOKEN_CLOSE_BRACKET: |
pos = stateConditionEnd(tokens, pos); |
break; |
case Token.TOKEN_AND: |
pos++; // go to next token for stateLogicalCondition |
pos = stateLogicalCondition(tokens, pos); |
break; |
default: |
throw new PathCreateException( |
"End of conditions or next condition expected", token); |
} |
return pos; |
} |
private int stateNew(List tokens, int pos) |
throws PathCreateException |
{ |
Token token = (Token)tokens.get(++pos); |
element.setNew(); |
switch(token.getType()) { |
case Token.TOKEN_CLOSE_BRACKET: |
pos = stateConditionEnd(tokens, pos); |
break; |
default: |
throw new PathCreateException("End of conditions expected", token); |
} |
return pos; |
} |
private int stateConditionEnd(List tokens, int pos) |
throws PathCreateException |
{ |
if(++pos >= tokens.size()) return pos; |
Token token = (Token)tokens.get(pos); |
switch(token.getType()) { |
case Token.TOKEN_SLASH: |
stateElementStart(tokens, pos); // start new path element |
break; |
case Token.TOKEN_SHARP: |
pos = stateOperandText(tokens, pos); |
break; |
case Token.TOKEN_AT: |
pos = stateOperandAttribute(tokens, pos); |
break; |
default: |
throw new PathCreateException( |
"End of element description expected", token); |
} |
return pos; |
} |
} |
/zpath/trunk/src/ak/zpath/DocumentTree.java |
---|
0,0 → 1,550 |
package ak.zpath; |
import org.w3c.dom.*; |
import javax.xml.parsers.*; |
import javax.xml.transform.*; |
import javax.xml.transform.dom.*; |
import javax.xml.transform.stream.*; |
import java.io.*; |
import java.util.*; |
import java.text.ParseException; |
public class DocumentTree |
{ |
/** |
* if true then null values of parameters will be replaced with empty strings |
*/ |
private boolean nullDefaultEmptyString = true; |
/** |
* if true then execution will be terminated if desired node for assigment |
* has already a value |
*/ |
private boolean failOnValueSet = true; |
private PathCreator pathCreator = new PathCreator(); |
private Document document; |
private Element lastElement = null; |
public DocumentTree(Document document) |
{ |
this.document = document; |
} |
public void updateDocument(String path, Object[] values) |
throws ParseException |
{ |
updateElement(path, values); |
} |
public Element updateElement(String path, Object[] values) |
throws ParseException |
{ |
return updateElement(null, path, values); |
} |
public Element updateElement(Element start, String path, Object[] values) |
throws ParseException |
{ |
return updateElement(start, path, createParamsMap(values)); |
} |
public Element updateElement(String path, Map params) |
throws ParseException |
{ |
return updateElement(null, path, params); |
} |
public Element updateElement(Element start, String path, Map params) |
throws ParseException |
{ |
List elements = pathCreator.createPath(path); |
Element target = searchElementInternal(start, elements, params, true); |
updateTarget( |
(PathElement)elements.get(elements.size() - 1), params, target); |
return target; |
} |
public Element searchElement(String path, Object[] values) |
throws ParseException |
{ |
return searchElement(null, path, values); |
} |
public Element searchElement(Element start, String path, Object[] values) |
throws ParseException |
{ |
return searchElement(start, path, createParamsMap(values)); |
} |
public Element searchElement(String path, Map params) |
throws ParseException |
{ |
return searchElement(null, path, params); |
} |
public Element searchElement(Element start, String path, Map params) |
throws ParseException |
{ |
return searchElementInternal(start, |
pathCreator.createPath(path), params, false); |
} |
protected Element searchElementInternal(Element start, List path, Map params, |
boolean update) |
throws ParseException |
{ |
Element root = document.getDocumentElement(); |
PathElement element; |
Element target = null; |
int startPos = 0; |
if(path.size() == 0) |
throw new PathCreateException("No elements found in path"); |
element = (PathElement)path.get(0); |
if("/".equals(element.getName())) { // absolute path |
element = (PathElement)path.get(1); |
if(path.size() > 0 && "**".equals(element.getName())) { |
// special case: '/**' |
startPos = 2; |
} |
else { |
if(root == null) { |
if(!update) return null; |
root = document.createElement(element.getName()); |
document.appendChild(root); |
lastElement = root; |
} |
else if(!root.getNodeName().equals(element.getName())) { |
if(!update) |
return null; |
else |
throw new PathCreateException( |
"Document has already root element with name '" |
+ root.getNodeName() + "'"); |
} |
target = root; |
startPos = 2; |
} |
} |
if("**".equals(element.getName())) { // ref to last created element |
if(lastElement == null) |
throw new PathCreateException( |
"Cannot go to '**' because no elements was created yet"); |
target = lastElement; |
startPos++; |
} |
else if(startPos == 0) { // it was no slash element at the begin |
if(start == null) |
throw new PathCreateException( |
"No start element specified for relative path"); |
target = start; |
startPos = 0; |
} |
target = doStep(path, startPos, params, target, update); |
return target; |
} |
protected void updateTarget(PathElement element, Map params, Element target) |
throws ParseException |
{ |
Node node = null; |
String value; |
// get first value |
switch(element.getFirstOperandType()) { |
case PathElement.OPERAND_NONE: |
return; // nothing to do |
case PathElement.OPERAND_TEXT: |
NodeList list = target.getChildNodes(); |
boolean done = false; |
for(int i = 0; i < list.getLength(); i++) { |
Node subnode = list.item(i); |
if(subnode.getNodeType() == Node.TEXT_NODE) { |
node = subnode; |
done = true; |
break; |
} |
} |
if(!done) { |
node = document.createTextNode(null); |
target.appendChild(node); |
} |
break; |
case PathElement.OPERAND_ATTRIBUTE: |
node = target.getAttributeNode(element.getFirstOperandValue()); |
if(node == null) { |
node = document.createAttribute(element.getFirstOperandValue()); |
target.setAttributeNode((Attr)node); |
} |
break; |
default: |
throw new RuntimeException("Unknown first operand type: " |
+ element.getFirstOperandType()); |
} |
// get second value |
switch(element.getSecondOperandType()) { |
case PathElement.OPERAND_VARIABLE: |
value = (String)params.get(element.getSecondOperandValue()); |
break; |
case PathElement.OPERAND_STRING: |
value = element.getSecondOperandValue(); |
break; |
default: |
throw new RuntimeException("Unknown second operand type: " |
+ element.getSecondOperandType()); |
} |
// set value |
String oldValue = node.getNodeValue(); |
switch(element.getOperation()) { |
case PathElement.OPERATION_ASSIGN: |
// chech if value is already set |
if(failOnValueSet && oldValue != null && !oldValue.equals("")) |
throw new RuntimeException("Node " + node.getNodeName() |
+ " has already value '" + oldValue + "'"); |
node.setNodeValue(value); |
break; |
case PathElement.OPERATION_APPEND: |
if(oldValue == null) oldValue = ""; |
node.setNodeValue(oldValue + value); |
break; |
default: |
throw new RuntimeException("Unknown operation: " |
+ element.getOperation()); |
} |
} |
protected Map createParamsMap(Object[] values) |
throws ParseException |
{ |
Map map = new HashMap(); |
if(values != null) { |
if(values.length % 2 != 0) |
throw new PathCreateException( |
"Number of values must match the number of names"); |
for(int i = 0; i < values.length; i += 2) { |
if(!(values[i] instanceof String)) |
throw new PathCreateException("Name of value must be string"); |
if(values[i+1] == null) |
; |
else if(!(values[i+1] instanceof String)) |
throw new PathCreateException( |
"Value must be string in current version"); |
// save the value |
Object value = values[i+1]; |
if(nullDefaultEmptyString) { |
if(value == null) value = ""; |
} |
else |
throw new PathCreateException( |
"Cannot save null value of '" + values[i] + "' to XML"); |
map.put(values[i], value); |
} |
} |
return map; |
} |
protected Element doStep(List path, int pos, Map params, Element node, |
boolean update) |
throws ParseException |
{ |
if(pos >= path.size()) return node; |
NodeList list = node.getChildNodes(); |
int count = list.getLength(); |
PathElement element = (PathElement)path.get(pos); |
// special element name - parent element |
if("..".equals(element.getName())) { |
if(node.getParentNode() == null) |
throw new PathCreateException( |
"Node " + node.getNodeName() + " has no parent"); |
if(!(node.getParentNode() instanceof Element)) |
throw new PathCreateException( |
"Parent of node " + node.getNodeName() + " is not an element"); |
return doStep(path, ++pos, params, (Element)node.getParentNode(), update); |
} |
// ordinal element without the 'new' keyword |
if(!element.getNew()) { |
for(int i = count - 1; i >= 0; i--) { // go backward |
Node subnode = list.item(i); |
if(subnode.getNodeType() != Node.ELEMENT_NODE) |
continue; // go through elements only |
Element e = (Element)subnode; |
if(testElement(element, params, e)) { |
return doStep(path, ++pos, params, e, update); |
} |
} |
} |
// nothing found |
if(update) { // create a new one |
if(element.getName() == null) |
throw new PathCreateException("Cannot create element with name '*'"); |
Element newElement = document.createElement(element.getName()); |
setupElement(element, params, newElement); |
node.appendChild(newElement); |
lastElement = newElement; |
return doStep(path, ++pos, params, newElement, update); |
} |
else { // not allowed to change document |
return null; |
} |
} |
protected void setupElement(PathElement element, Map params, Element node) |
{ |
for(Iterator i = element.getConditions().iterator(); i.hasNext(); ) { |
PathCondition condition = (PathCondition)i.next(); |
Node subnode = null; |
String value; |
// get first value |
switch(condition.getFirstOperandType()) { |
case PathElement.OPERAND_TEXT: |
subnode = document.createTextNode(""); |
node.appendChild(subnode); |
break; |
case PathElement.OPERAND_ATTRIBUTE: |
subnode = document.createAttribute(condition.getFirstOperandValue()); |
node.setAttributeNode((Attr)subnode); |
break; |
default: |
throw new RuntimeException("Unknown first operand type: " |
+ condition.getFirstOperandType()); |
} |
// get second value |
switch(condition.getSecondOperandType()) { |
case PathElement.OPERAND_VARIABLE: |
value = (String)params.get(condition.getSecondOperandValue()); |
break; |
case PathElement.OPERAND_STRING: |
value = condition.getSecondOperandValue(); |
break; |
default: |
throw new RuntimeException("Unknown second operand type: " |
+ condition.getSecondOperandType()); |
} |
// set |
switch(condition.getOperation()) { |
case PathElement.OPERATION_EQUAL: |
subnode.setNodeValue(value); |
break; |
default: |
throw new RuntimeException("Unknown operation: " |
+ condition.getOperation()); |
} |
} |
} |
protected boolean testElement(PathElement element, Map params, Element node) |
{ |
// element name is specified and does not match |
if(!element.getName().equals("*") |
&& !node.getNodeName().equals(element.getName())) |
{ |
return false; |
} |
for(Iterator i = element.getConditions().iterator(); i.hasNext(); ) { |
PathCondition condition = (PathCondition)i.next(); |
String firstValue = null; |
String secondValue; |
// get first value |
switch(condition.getFirstOperandType()) { |
case PathElement.OPERAND_TEXT: |
NodeList list = node.getChildNodes(); |
for(int j = 0; j < list.getLength(); j++) { |
Node subnode = list.item(j); |
if(subnode.getNodeType() == Node.TEXT_NODE) { |
firstValue = subnode.getNodeValue(); |
break; |
} |
} |
break; |
case PathElement.OPERAND_ATTRIBUTE: |
firstValue = node.getAttribute(condition.getFirstOperandValue()); |
break; |
default: |
throw new RuntimeException("Unknown first operand type: " |
+ condition.getFirstOperandType()); |
} |
// get second value |
switch(condition.getSecondOperandType()) { |
case PathElement.OPERAND_VARIABLE: |
secondValue = (String)params.get(condition.getSecondOperandValue()); |
break; |
case PathElement.OPERAND_STRING: |
secondValue = condition.getSecondOperandValue(); |
break; |
default: |
throw new RuntimeException("Unknown second operand type: " |
+ condition.getSecondOperandType()); |
} |
// compare |
switch(condition.getOperation()) { |
case PathElement.OPERATION_EQUAL: |
if(!firstValue.equals(secondValue)) return false; |
break; |
default: |
throw new RuntimeException("Unknown operation: " |
+ condition.getOperation()); |
} |
} |
return true; |
} |
public static void main(String[] args) |
throws Exception |
{ |
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); |
DocumentBuilder builder = factory.newDocumentBuilder(); |
Document document = builder.newDocument(); |
DocumentTree tree = new DocumentTree(document); |
Transformer transformer |
= TransformerFactory.newInstance().newTransformer(); |
// test update ------------------------------------------------------------- |
tree.updateDocument( |
"/Vertrag/bbb@name:='BBB'", |
null ); |
tree.updateDocument( |
"/Vertrag/bbb@id:='ZZZ'", |
null ); |
Element ccc = tree.updateElement( |
"/Vertrag/bbb[@name='BBB' and @id=${attr1}]/ccc@test := $value", |
new Object[] { |
"value", null, |
"attr1", "ZZZ" |
} ); |
tree.updateElement( |
"/Vertrag/bbb[@name='BBB' and @id=${attr1}]/ccc@test2 := $value", |
new Object[] { |
"value", "66666", |
"attr1", "ZZZ" |
} ); |
tree.updateElement(ccc, |
"subccc# := 'test relative path'", |
(Object[])null); |
tree.updateElement(ccc, |
"../../subnode@attr := 'test relative path 2'", |
(Object[])null); |
tree.updateDocument( |
"/Vertrag/bbb/ddd# .= '123'", |
null); |
tree.updateDocument( |
"/Vertrag/bbb/ddd# .= '456'", |
null); |
tree.updateDocument( |
"/Vertrag/bbb/ccc[new]@test3 := $value", |
new Object[] { |
"value", "ABCD", |
"attr1", "ZZZ" |
} ); |
tree.updateDocument( |
"/Vertrag/bbb/*# .= '!!!'", |
null); |
tree.updateDocument( |
"/**@nnn := 'test last element'", |
null); |
tree.updateDocument( |
"/**/../..@lll := 'test last element'", |
null); |
tree.updateDocument( |
"/Vertrag/Sparten/Sparte[@bezeichnung=${a}]" |
+ "/SpartenspezifischerTeil/Wagnis[@nummer=${c}]/Objekt[@nummer=${d}]", |
new Object[] { |
"a", "Gebaeude", |
"c", "0001", |
"d", "0002" |
} ); |
tree.updateDocument( |
"/Vertrag/Sparten/Sparte[@bezeichnung=${a}]" |
+ "/SpartenspezifischerTeil/Wagnis[@nummer=${c}]/Objekt[@nummer=${d}]", |
new Object[] { |
"a", "Gebaeude", |
"c", "0001", |
"d", "0003" |
} ); |
// test search ------------------------------------------------------------- |
tree.searchElement("/Vertrag/Sparten", (Object[])null); |
tree.searchElement("/Vertrag2/Sparten", (Object[])null); |
tree.searchElement("/Vertrag/nnnn/mmmm/mmm", (Object[])null); |
// write result ------------------------------------------------------------ |
transformer.transform(new DOMSource(document), new StreamResult( |
new FileOutputStream("result.xml"))); |
(new XmlSaver(new FileOutputStream("result2.xml"))).save(document); |
} |
} |
/zpath/trunk/src/ak/zpath/PathCreateException.java |
---|
0,0 → 1,17 |
package ak.zpath; |
import java.text.ParseException; |
public class PathCreateException |
extends ParseException |
{ |
public PathCreateException(String message) |
{ |
super(message, -1); |
} |
public PathCreateException(String message, Token token) |
{ |
super(message, token == null ? -1 : token.getPosition()); |
} |
} |
/zpath/trunk/src/ak/zpath/Token.java |
---|
0,0 → 1,66 |
package ak.zpath; |
public class Token |
{ |
public static final int TOKEN_SLASH = 1; |
public static final int TOKEN_OPEN_BRACKET = 2; |
public static final int TOKEN_CLOSE_BRACKET = 3; |
public static final int TOKEN_AT = 4; |
public static final int TOKEN_SHARP = 5; |
public static final int TOKEN_STRING = 6; |
public static final int TOKEN_EQUAL = 7; |
public static final int TOKEN_VARIABLE = 8; |
public static final int TOKEN_ASSIGN = 9; |
public static final int TOKEN_AND = 10; |
public static final int TOKEN_OR = 11; |
public static final int TOKEN_NEW = 12; |
public static final int TOKEN_NAME = 13; |
public static final int TOKEN_APPEND = 14; |
private int type; |
private int pos; |
private String value; |
public Token(int type, int pos, String value) |
{ |
this.type = type; |
this.pos = pos; |
this.value = value; |
} |
public int getType() |
{ |
return type; |
} |
public int getPosition() |
{ |
return pos; |
} |
public String getValue() |
{ |
return value; |
} |
public String toString() |
{ |
switch(type) { |
case Token.TOKEN_SLASH: return "slash"; |
case Token.TOKEN_OPEN_BRACKET: return "open bracket"; |
case Token.TOKEN_CLOSE_BRACKET: return "close bracket"; |
case Token.TOKEN_AT: return "at"; |
case Token.TOKEN_SHARP: return "sharp"; |
case Token.TOKEN_STRING: return "string [" + value + "]"; |
case Token.TOKEN_EQUAL: return "equal"; |
case Token.TOKEN_VARIABLE: return "variable [" + value + "]"; |
case Token.TOKEN_ASSIGN: return "assign"; |
case Token.TOKEN_APPEND: return "append"; |
case Token.TOKEN_AND: return "and"; |
case Token.TOKEN_OR: return "or"; |
case Token.TOKEN_NEW: return "new"; |
case Token.TOKEN_NAME: return "name [" + value + "]"; |
default: throw new RuntimeException("Unknown token type: " + type); |
} |
} |
} |
/zpath/trunk/src/ak/zpath/XmlSaver.java |
---|
0,0 → 1,129 |
package ak.zpath; |
import org.w3c.dom.*; |
import java.io.*; |
public class XmlSaver |
{ |
public static final String ENCODING = "UTF-8"; |
public static final String INDENT = "\t"; |
private OutputStream output; |
private PrintStream printer; |
public XmlSaver() |
{ |
} |
public XmlSaver(OutputStream output) |
{ |
setOutput(output); |
} |
public OutputStream getOutput() |
{ |
return output; |
} |
public void setOutput(OutputStream output) |
{ |
this.output = output; |
} |
public void save(Document document) |
throws IOException |
{ |
printer = new PrintStream(output, false, ENCODING); |
printer.println( |
"<?xml version=\"1.0\" encoding=\"" + ENCODING + "\"?>\n"); |
if(document != null) |
saveElement(document.getDocumentElement(), ""); |
printer.flush(); |
} |
protected void saveElement(Element element, String indent) |
throws IOException |
{ |
if(element == null) return; |
String newIndent = indent + INDENT; |
NodeList children = element.getChildNodes(); |
NamedNodeMap attrs = element.getAttributes(); |
boolean isEmpty = true; |
boolean hasText = false; |
// print tag name |
printer.print(indent); |
printer.print("<"); |
printer.print(element.getTagName()); |
// print attributes |
for(int i = 0; i < attrs.getLength(); i++) { |
Attr attr = (Attr)attrs.item(i); |
printer.print(" "); |
printer.print(attr.getName()); |
printer.print("=\""); |
printer.print(attr.getValue()); |
printer.print("\""); |
} |
// chech if has nested elements |
for(int i = 0; i < children.getLength(); i++) { |
Node subnode = children.item(i); |
if(subnode instanceof Comment) |
isEmpty = false; |
else if(subnode instanceof Element) |
isEmpty = false; |
else if(subnode instanceof Text) |
hasText = true; |
} |
if(isEmpty) { // empty element |
if(!hasText) { |
printer.println("/>"); |
} |
else { |
printer.print(">"); |
// print nested text |
for(int i = 0; i < children.getLength(); i++) { |
Node subnode = children.item(i); |
if(subnode instanceof Text) |
printer.print(subnode.getNodeValue()); |
} |
printer.print("</"); |
printer.print(element.getTagName()); |
printer.println(">"); |
} |
} |
else { // has nested elements |
printer.println(">"); |
// print nested elements, comment, text |
for(int i = 0; i < children.getLength(); i++) { |
Node subnode = children.item(i); |
if(subnode instanceof Comment) { |
printer.print("<!--"); |
printer.print(subnode.getNodeValue()); |
printer.println("-->"); |
} |
else if(subnode instanceof Element) { |
saveElement((Element)subnode, newIndent); |
} |
else if(subnode instanceof Text) { |
printer.print(subnode.getNodeValue()); |
} |
} |
printer.print(indent); |
printer.print("</"); |
printer.print(element.getTagName()); |
printer.println(">"); |
} |
} |
} |
/zpath/trunk/src/ak/zpath/PathCondition.java |
---|
0,0 → 1,69 |
package ak.zpath; |
public class PathCondition |
{ |
private int firstOperandType; |
private String firstOperandValue; |
private int secondOperandType; |
private String secondOperandValue; |
private int operationType; |
public int getFirstOperandType() |
{ |
return firstOperandType; |
} |
public void setFirstOperandType(int operandType) |
{ |
firstOperandType = operandType; |
} |
public String getFirstOperandValue() |
{ |
return firstOperandValue; |
} |
public void setFirstOperandValue(String value) |
{ |
firstOperandValue = value; |
} |
public int getOperation() |
{ |
return operationType; |
} |
public void setOperation(int operationType) |
{ |
this.operationType = operationType; |
} |
public int getSecondOperandType() |
{ |
return secondOperandType; |
} |
public void setSecondOperandType(int operandType) |
{ |
secondOperandType = operandType; |
} |
public String getSecondOperandValue() |
{ |
return secondOperandValue; |
} |
public void setSecondOperandValue(String value) |
{ |
secondOperandValue = value; |
} |
public void validateCondition() |
{ |
} |
public String toString() |
{ |
return "Condition"; |
} |
} |
/zpath/trunk/src/ak/zpath/PathElement.java |
---|
0,0 → 1,130 |
package ak.zpath; |
import java.util.List; |
import java.util.ArrayList; |
public class PathElement |
{ |
public static final int OPERAND_NONE = 0; |
public static final int OPERAND_TEXT = 1; |
public static final int OPERAND_ATTRIBUTE = 2; |
public static final int OPERAND_VARIABLE = 3; |
public static final int OPERAND_STRING = 4; |
public static final int OPERATION_ASSIGN = 5; |
public static final int OPERATION_EQUAL = 6; |
public static final int OPERATION_APPEND = 7; |
private String name; |
private boolean isNew = false; |
private List conditions = new ArrayList(); |
private PathCondition condition; |
private int firstOperandType = OPERAND_NONE; |
private String firstOperandValue; |
private int secondOperandType; |
private String secondOperandValue; |
private int operationType; |
public String getName() |
{ |
return name; |
} |
public void setName(String name) |
{ |
this.name = name; |
} |
public int getFirstOperandType() |
{ |
return firstOperandType; |
} |
public String getFirstOperandValue() |
{ |
return firstOperandValue; |
} |
public void setFirstOperand(int operandType, String value) |
{ |
firstOperandType = operandType; |
firstOperandValue = value; |
} |
public int getOperation() |
{ |
return operationType; |
} |
public void setOperation(int operationType) |
{ |
this.operationType = operationType; |
} |
public int getSecondOperandType() |
{ |
return secondOperandType; |
} |
public String getSecondOperandValue() |
{ |
return secondOperandValue; |
} |
public void setSecondOperand(int operandType, String value) |
{ |
secondOperandType = operandType; |
secondOperandValue = value; |
} |
public void validateOperands() |
{ |
} |
public List getConditions() |
{ |
return conditions; |
} |
public void addCondition() |
{ |
condition = new PathCondition(); |
conditions.add(condition); |
} |
public void setFirstCondition(int operandType, String value) |
{ |
condition.setFirstOperandType(operandType); |
condition.setFirstOperandValue(value); |
} |
public void setConditionOperation(int operationType) |
{ |
condition.setOperation(operationType); |
} |
public void setSecondCondition(int operandType, String value) |
{ |
condition.setSecondOperandType(operandType); |
condition.setSecondOperandValue(value); |
} |
public void validateCondition() |
{ |
condition.validateCondition(); |
} |
public boolean getNew() |
{ |
return isNew; |
} |
public void setNew() |
{ |
isNew = true; |
} |
public String toString() |
{ |
return "Element [" + name + "]"; |
} |
} |
/zpath/trunk/src/ak/zpath/PathParseException.java |
---|
0,0 → 1,12 |
package ak.zpath; |
import java.text.ParseException; |
public class PathParseException |
extends ParseException |
{ |
public PathParseException(String message, int pos) |
{ |
super(message, pos); |
} |
} |
/zpath/trunk/src/ak/zpath/PathParser.java |
---|
0,0 → 1,224 |
package ak.zpath; |
import java.util.List; |
import java.util.ArrayList; |
public class PathParser |
{ |
public List parse(String path) |
throws PathParseException |
{ |
char c; |
int startPos; |
int pos = 0; |
int endPos = path.length(); |
String token; |
List tokens = new ArrayList(); |
while(pos < endPos) { |
c = path.charAt(pos); |
while(Character.isWhitespace(c) && pos < endPos - 1) { |
c = path.charAt(++pos); |
} |
if(pos >= endPos) break; |
switch(c) { |
case '/': |
addToken(tokens, pos, Token.TOKEN_SLASH, null); |
pos++; |
break; |
case '[': |
addToken(tokens, pos, Token.TOKEN_OPEN_BRACKET, null); |
pos++; |
break; |
case ']': |
addToken(tokens, pos, Token.TOKEN_CLOSE_BRACKET, null); |
pos++; |
break; |
case '@': |
addToken(tokens, pos, Token.TOKEN_AT, null); |
pos++; |
break; |
case '#': |
addToken(tokens, pos, Token.TOKEN_SHARP, null); |
pos++; |
break; |
case '"': |
case '\'': |
startPos = pos+1; |
pos = findStringEnd(path, c, startPos); |
addToken(tokens, startPos-1, Token.TOKEN_STRING, |
path.substring(startPos, pos)); |
pos++; |
break; |
case '=': |
addToken(tokens, pos, Token.TOKEN_EQUAL, null); |
pos++; |
break; |
case '$': |
if(++pos >= endPos) |
throw new PathParseException("variable name expected", pos); |
c = path.charAt(pos); |
if(c == '{') { |
startPos = ++pos; |
pos = findVariableEnd(path, c, startPos); |
addToken(tokens, startPos-2, |
Token.TOKEN_VARIABLE, path.substring(startPos, pos)); |
} |
else if(Character.isLetter(c)) { |
startPos = pos; |
pos = findVariableEnd(path, '\u0000', pos); |
addToken(tokens, startPos-1, |
Token.TOKEN_VARIABLE, path.substring(startPos, pos)); |
} |
else |
throw new PathParseException("variable name expected", pos); |
pos++; |
break; |
case ':': |
if(++pos >= endPos) |
throw new PathParseException("'=' expected", pos); |
c = path.charAt(pos); |
if(c == '=') |
addToken(tokens, pos-1, Token.TOKEN_ASSIGN, null); |
else |
throw new PathParseException("'=' expected", pos); |
pos++; |
break; |
case '.': |
if(++pos >= endPos) |
throw new PathParseException("'=' or '.' expected", pos); |
c = path.charAt(pos); |
if(c == '=') |
addToken(tokens, pos-1, Token.TOKEN_APPEND, null); |
else if(c == '.') |
addToken(tokens, pos-1, Token.TOKEN_NAME, ".."); |
else |
throw new PathParseException("'=' expected", pos); |
pos++; |
break; |
case '*': |
if(pos < endPos-1 && path.charAt(pos+1) == '*') { |
addToken(tokens, pos, Token.TOKEN_NAME, "**"); |
pos += 2; |
} |
else { |
addToken(tokens, pos, Token.TOKEN_NAME, "*"); |
pos++; |
} |
break; |
default: |
if(Character.isLetter(c) || c == '_') { |
startPos = pos; |
pos = findTokenEnd(path, pos); |
token = path.substring(startPos, pos); |
if("and".equals(token)) |
addToken(tokens, startPos, Token.TOKEN_AND, null); |
else if("or".equals(token)) |
addToken(tokens, startPos, Token.TOKEN_OR, null); |
else if("new".equals(token)) |
addToken(tokens, startPos, Token.TOKEN_NEW, null); |
else |
addToken(tokens, startPos, Token.TOKEN_NAME, token); |
} |
else |
throw new PathParseException( |
"unexpected character '" + c + "'", pos); |
} |
} |
return tokens; |
} |
private int findStringEnd(String path, char c, int pos) |
throws PathParseException |
{ |
int endPos = path.length(); |
if(pos >= endPos - 1) |
throw new PathParseException( |
"unclosed string, '" + c + "' expected", pos); |
while(path.charAt(++pos) != c) { |
if(pos >= endPos - 1) |
throw new PathParseException( |
"unclosed string, '" + c + "' expected", pos); |
} |
return pos; |
} |
private int findVariableEnd(String path, char c, int pos) |
throws PathParseException |
{ |
int endPos = path.length(); |
if(pos >= endPos) |
throw new PathParseException("unexpected end of variable name", pos); |
char c2 = path.charAt(pos); |
if(c == '{' && c2 == '}') |
throw new PathParseException("empty variable name", pos); |
else if(!Character.isLetter(c2) && c2 != '_') |
throw new PathParseException("variable name expected", pos); |
while(true) { |
if(pos >= endPos - 1) |
throw new PathParseException("unexpected end of variable name", pos); |
c2 = path.charAt(++pos); |
if(c == '{') { |
if(c2 == '}') break; |
if(!Character.isLetter(c2) && !Character.isDigit(c2) && c2 != '_') |
throw new PathParseException("unexpected character '" + c2 |
+ "' in variable name is occured instead of '}'", pos); |
} |
else { |
if(!Character.isLetter(c2) && !Character.isDigit(c2) && c2 != '_') |
break; |
if(pos >= endPos - 1) { |
pos++; |
break; |
} |
} |
} |
return pos; |
} |
private int findTokenEnd(String path, int pos) |
throws PathParseException |
{ |
int endPos = path.length(); |
char c; |
while(true) { |
pos++; |
if(pos >= endPos) break; |
c = path.charAt(pos); |
if(!Character.isLetter(c) && !Character.isDigit(c) && c != '_') break; |
} |
return pos; |
} |
private void addToken(List tokens, int pos, int type, String value) |
{ |
tokens.add(new Token(type, pos, value)); |
} |
} |