/*!
* This program is free software; you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License, version 2.1 as published by the Free Software
* Foundation.
*
* You should have received a copy of the GNU Lesser General Public License along with this
* program; if not, you can obtain a copy at http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html
* or from the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU Lesser General Public License for more details.
*
* Copyright (c) 2002-2013 Pentaho Corporation.. All rights reserved.
*/
package org.pentaho.reporting.engine.classic.core.testsupport.selector;
import java.io.IOException;
import java.io.StreamTokenizer;
import java.io.StringReader;
import java.util.ArrayList;
import org.pentaho.reporting.engine.classic.core.AttributeNames;
import org.pentaho.reporting.engine.classic.core.layout.model.RenderBox;
import org.pentaho.reporting.engine.classic.core.layout.model.RenderNode;
import org.pentaho.reporting.engine.classic.core.layout.process.IterateSimpleStructureProcessStep;
import org.pentaho.reporting.engine.classic.core.metadata.ElementType;
public class MatchFactory
{
private static class NodeIterator extends IterateSimpleStructureProcessStep
{
private boolean singleResult;
private ArrayList<RenderNode> nodes;
private NodeMatcher matcher;
private NodeIterator()
{
nodes = new ArrayList<RenderNode>();
}
public RenderNode get(final RenderNode node, final NodeMatcher matcher)
{
if (node == null)
{
throw new NullPointerException();
}
if (matcher == null)
{
throw new NullPointerException();
}
this.matcher = matcher;
this.nodes.clear();
this.singleResult = true;
startProcessing(node);
if (this.nodes.isEmpty())
{
return null;
}
return this.nodes.get(0);
}
public RenderNode[] getAll(final RenderNode node, final NodeMatcher matcher)
{
if (node == null)
{
throw new NullPointerException();
}
if (matcher == null)
{
throw new NullPointerException();
}
this.matcher = matcher;
this.nodes.clear();
this.singleResult = false;
startProcessing(node);
return this.nodes.toArray(new RenderNode[this.nodes.size()]);
}
protected boolean startBox(final RenderBox box)
{
if (singleResult && nodes.isEmpty() == false)
{
return false;
}
if (matcher.matches(box))
{
nodes.add(box);
if (singleResult)
{
return false;
}
}
return true;
}
protected void processOtherNode(final RenderNode node)
{
if (singleResult && nodes.isEmpty() == false)
{
return;
}
if (matcher.matches(node))
{
nodes.add(node);
}
}
}
public enum Type
{
Start, Descendant, Child, Id, Class, Element
}
private static ElementMatcher createMatcher(StreamTokenizer tokenizer)
{
if (tokenizer.ttype == '*')
{
return new AnyNodeMatcher();
}
return new ElementMatcher(tokenizer.sval);
}
public static NodeMatcher parse(final String s) throws IOException
{
final StreamTokenizer tokenizer = new StreamTokenizer(new StringReader(s));
tokenizer.wordChars('0', '9');
tokenizer.ordinaryChar('.');
tokenizer.ordinaryChar(',');
tokenizer.ordinaryChars(0, ' ');
ElementMatcher elementMatcher = null;
NodeMatcher n = null;
Type selectorType = Type.Start;
int token;
while ((token = tokenizer.nextToken()) != StreamTokenizer.TT_EOF)
{
if (token == StreamTokenizer.TT_WORD || token == '*')
{
NodeMatcher matcher = null;
switch (selectorType)
{
case Start:
elementMatcher = createMatcher(tokenizer);
matcher = elementMatcher;
break;
case Child:
n = new ChildMatcher(n);
elementMatcher = createMatcher(tokenizer);
matcher = elementMatcher;
break;
case Descendant:
n = new DescendantMatcher(n);
elementMatcher = createMatcher(tokenizer);
matcher = elementMatcher;
break;
case Id:
if (elementMatcher == null)
{
if (n != null)
{
n = new DescendantMatcher(n);
}
elementMatcher = createMatcher(tokenizer);
matcher = elementMatcher;
}
elementMatcher.add(new AttributeMatcher(AttributeNames.Xml.NAMESPACE, AttributeNames.Xml.ID, tokenizer.sval));
break;
case Class:
if (elementMatcher == null)
{
if (n != null)
{
n = new DescendantMatcher(n);
}
elementMatcher = createMatcher(tokenizer);
matcher = elementMatcher;
}
elementMatcher.add(new AttributeMatcher(AttributeNames.Core.NAMESPACE, AttributeNames.Core.STYLE_CLASS, tokenizer.sval));
break;
default:
throw new IOException();
}
selectorType = Type.Element;
if (matcher != null)
{
if (n != null)
{
n = new AndMatcher(matcher, n);
}
else
{
n = matcher;
}
}
}
else
{
if (token == '>')
{
selectorType = Type.Child;
}
if (token == '.')
{
selectorType = Type.Class;
}
if (token == '#')
{
selectorType = Type.Id;
}
if (Character.isWhitespace(token))
{
if (selectorType == Type.Class ||
selectorType == Type.Id)
{
throw new IllegalStateException();
}
if (selectorType != Type.Child)
{
selectorType = Type.Descendant;
}
}
}
}
return n;
}
public static RenderNode match(final RenderNode base, final NodeMatcher parse)
{
if (parse == null)
{
throw new NullPointerException();
}
return new NodeIterator().get(base, parse);
}
public static RenderNode[] matchAll(final RenderNode base, final NodeMatcher parse)
{
if (parse == null)
{
throw new NullPointerException();
}
return new NodeIterator().getAll(base, parse);
}
public static RenderNode match(final RenderNode base, final String selector) throws IOException
{
final NodeMatcher parse = parse(selector);
return match(base, parse);
}
public static RenderNode[] matchAll(final RenderNode base, final String selector) throws IOException
{
final NodeMatcher parse = parse(selector);
return matchAll(base, parse);
}
public static RenderNode findElementByName (final RenderNode section, final String name)
{
final RenderNode[] retval =
findElementsByAttribute(section, AttributeNames.Core.NAMESPACE, AttributeNames.Core.NAME, name);
if (retval.length == 0)
{
return null;
}
return retval[0];
}
public static RenderNode[] findElementsByName (final RenderNode section, final String name)
{
return findElementsByAttribute(section, AttributeNames.Core.NAMESPACE, AttributeNames.Core.NAME, name);
}
public static RenderNode[] findElementsByAttribute (final RenderNode section, final String ns, final String name, final Object value)
{
return matchAll(section, new AttributeMatcher(ns, name, value));
}
public static RenderNode[] findElementsByElementType(final RenderNode section, final ElementType type)
{
return matchAll(section, new ElementTypeMatcher(type.getMetaData().getName()));
}
public static RenderNode[] findElementsByNodeType(final RenderNode section, final int type)
{
return matchAll(section, new RenderNodeTypeMatcher(type));
}
/*
public static void main(final String[] args) throws IOException
{
System.out.println(new MatchFactory().parse("Test Element > test .c#i"));
final AndMatcher a1 = new AndMatcher(new ElementMatcher("p"),
new DescendantMatcher(new AndMatcher(new ElementMatcher("div"),
new DescendantMatcher(new ElementMatcher("body")))));
System.out.println(a1);
}
*/
}