blob: 21539cdb36f38547335fb0dbc4c13c227b882c1a [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2007 IBM Corporation and Others
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Hisashi MIYASHITA - initial API and implementation
*******************************************************************************/
package org.eclipse.actf.ai.query.impl;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.eclipse.actf.model.dom.dombycom.IFlashNode;
import org.w3c.dom.Attr;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
public class FlashQueryImpl {
private static final String FLASH_QUERY_NS = "http://www.ibm.com/xmlns/prod/aiBrowser/fennec/flash-query";
private final String base;
private final Target[] targets;
private final boolean isFrameRangeSpecified;
private final int frameRangeMin;
private final int frameRangeMax;
private final boolean isDepthSpecified;
private final int depth;
private FlashQueryImpl(String base, List<Target> targetList,
boolean isDepthSpecified, int depth,
boolean isFrameRangeSpecified,
int frameRangeMin, int frameRangeMax) {
this.base = base;
this.targets = targetList.toArray(new Target[targetList.size()]);
this.isDepthSpecified = isDepthSpecified;
this.depth = depth;
this.isFrameRangeSpecified = isFrameRangeSpecified;
this.frameRangeMin = frameRangeMin;
this.frameRangeMax = frameRangeMax;
}
static private abstract class Target {
protected boolean isRelative;
public abstract List<Node> query(IFlashNode fn, List<Node> r);
}
static private class SimpleTarget extends Target {
private final String path;
@Override
public List<Node> query(IFlashNode fn, List<Node> l) {
IFlashNode r;
if (isRelative) {
if (path.length() > 0) {
r = fn.getNodeFromPath(fn.getTarget() + "." + path);
} else {
r = fn;
}
} else {
r = fn.getNodeFromPath(path);
}
if (r != null) l.add(r);
return l;
}
SimpleTarget(String path) {
if (path.charAt(0) == '.') {
isRelative = true;
this.path = path.substring(1);
} else {
isRelative = false;
this.path = path;
}
}
}
static private class WildCardTarget extends Target {
private List<Object> pathSegments;
static private Pattern regExpQuote(String pat, char wildCardChar) {
StringBuffer r = new StringBuffer();
for (int i = 0; i < pat.length(); i++) {
char c = pat.charAt(i);
if (c == wildCardChar) {
r.append(".*");
} else if (("\\?*+.[]{}()$^".indexOf(c) >= 0)) {
r.append('\\');
r.append(c);
} else {
r.append(c);
}
}
return Pattern.compile(r.toString());
}
private boolean match(Pattern p, IFlashNode fn) {
String target = fn.getTarget();
int idx = target.lastIndexOf(".");
if (idx > 0) {
target = target.substring(idx + 1);
}
Matcher m = p.matcher(target);
return m.matches();
}
private ArrayList<Node> queryWC(IFlashNode fn, Pattern p, ArrayList<Node> l) {
IFlashNode[] fns = fn.getInnerNodes();
for (int i = 0; i < fns.length; i++) {
if (match(p, fns[i])) {
l.add(fns[i]);
}
}
return l;
}
@Override
public List<Node> query(IFlashNode fn, List<Node> l) {
ArrayList<Node> cnl = new ArrayList<Node>();
Iterator<Object> it = pathSegments.iterator();
if (isRelative) {
cnl.add(fn);
} else {
if (!it.hasNext()) return l;
Object ps = it.next();
if (!(ps instanceof String)) return l;
IFlashNode fn2 = fn.getNodeFromPath((String) ps);
if (fn2 == null) return l;
cnl.add(fn2);
}
while (it.hasNext()) {
ArrayList<Node> cnl2 = new ArrayList<Node>();
Object ps = it.next();
if (ps instanceof String) {
for (int i = 0; i < cnl.size(); i++) {
IFlashNode fn2 = (IFlashNode) cnl.get(i);
fn2 = fn2.getNodeFromPath(fn2.getTarget() + "." + ps);
if (fn2 != null) cnl2.add(fn2);
}
} else {
for (int i = 0; i < cnl.size(); i++) {
IFlashNode fn2 = (IFlashNode) cnl.get(i);
cnl2 = queryWC(fn2, (Pattern) ps, cnl2);
}
}
cnl = cnl2;
if (cnl.size() == 0) return l;
}
l.addAll(cnl);
return l;
}
WildCardTarget(String target, String base) {
pathSegments = new ArrayList<Object>();
char ct = target.charAt(0);
switch (ct) {
case '/':
isRelative = false;
target = target.substring(1);
break;
case '.':
isRelative = true;
target = target.substring(1);
break;
default:
isRelative = false;
target = base + "." + target;
}
StringBuffer buf = new StringBuffer();
String[] segs = target.split("\\.");
for (int i = 0; i < segs.length; i++) {
if (segs[i].indexOf('*') >= 0) {
if (buf.length() > 0) {
pathSegments.add(buf.toString());
buf.setLength(0);
}
pathSegments.add(regExpQuote(segs[i], '*'));
} else {
if (buf.length() > 0) {
buf.append(".");
}
buf.append(segs[i]);
}
}
if (buf.length() > 0) {
pathSegments.add(buf.toString());
}
}
}
public boolean hasTarget() {
return targets.length > 0;
}
public List<Node> query(Node base) {
if (!(base instanceof IFlashNode)) return null;
IFlashNode fn = (IFlashNode) base;
List<Node> r = new ArrayList<Node>();
for (int i = 0; i < targets.length; i++) {
r = targets[i].query(fn, r);
}
if (isDepthSpecified) {
List<Node> r2 = new ArrayList<Node>();
Iterator<Node> it = r.iterator();
while (it.hasNext()) {
IFlashNode fn2 = (IFlashNode) it.next();
IFlashNode fnd = fn2.getNodeAtDepth(depth);
if (fnd != null) r2.add(fnd);
}
r = r2;
}
if (isFrameRangeSpecified) {
Iterator<Node> it = r.iterator();
while (it.hasNext()) {
IFlashNode fn2 = (IFlashNode) it.next();
int f = fn2.getCurrentFrame();
if (!((frameRangeMin <= f) && (f < frameRangeMax))) it.remove();
}
}
return r;
}
private static String computeBase(String path, String pbase) {
char ct = path.charAt(0);
switch (ct) {
case '/':
return path.substring(1);
case '.':
return path;
default:
if ((pbase.length() == 0) || (pbase.equals("."))) return path;
return pbase + "." + path;
}
}
static FlashQueryImpl parse(Element e, FlashQueryImpl parentQuery) {
String base = e.getAttributeNS(FLASH_QUERY_NS, "base");
String pbase = "";
if (parentQuery != null) {
pbase = parentQuery.base;
}
if (base.length() == 0) {
base = pbase;
} else {
base = computeBase(base, pbase);
}
String targetStr = e.getAttributeNS(FLASH_QUERY_NS, "targets");
ArrayList<Target> targetList = new ArrayList<Target>();
if (targetStr.length() > 0) {
String[] targets = targetStr.split("[ \t\r\n]");
for (int i = 0; i < targets.length; i++) {
if (targets[i].indexOf('*') >= 0) {
targetList.add(new WildCardTarget(targets[i], base));
} else {
targetList.add(new SimpleTarget(computeBase(targets[i], base)));
}
}
}
String depthStr = e.getAttributeNS(FLASH_QUERY_NS, "depth");
boolean isDepthSpecified = false;
int depth = 0;
if (depthStr.length() > 0) {
try {
depth = Integer.parseInt(depthStr.trim());
isDepthSpecified = true;
} catch (NumberFormatException ex) {
}
}
boolean isFrameRangeSpecified = false;
int frameRangeMin = 0;
int frameRangeMax = 0;
String frameRangeStr = e.getAttributeNS(FLASH_QUERY_NS, "frameRange");
if (frameRangeStr.length() > 0) {
int pos = frameRangeStr.indexOf('-');
try {
if (pos < 0) {
int m = Integer.parseInt(frameRangeStr.trim());
frameRangeMin = m;
frameRangeMax = m + 1;
} else {
frameRangeMin = Integer.parseInt(frameRangeStr.substring(0, pos).trim());
frameRangeMax = Integer.parseInt(frameRangeStr.substring(pos + 1).trim());
}
isFrameRangeSpecified = true;
} catch (NumberFormatException ex) {
}
}
FlashQueryImpl q = new FlashQueryImpl(base, targetList,
isDepthSpecified, depth,
isFrameRangeSpecified,
frameRangeMin,
frameRangeMax);
return q;
}
static Attr serializeQuery(Node domNode, Node usrNode) {
if (!(domNode instanceof IFlashNode))
return null;
Document doc = usrNode.getOwnerDocument();
String target = ((IFlashNode)domNode).getTarget();
if (target != null && target.length() > 0) {
Attr attr = doc.createAttributeNS(FLASH_QUERY_NS, "flq:targets");
attr.setNodeValue(target);
return attr;
}
return null;
}
}