/*
* Copyright (C) 2011 eXo Platform SAS.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software 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.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.exoplatform.web.controller.router;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;
import org.exoplatform.web.controller.regexp.GroupType;
import org.exoplatform.web.controller.regexp.RENode;
import org.exoplatform.web.controller.regexp.RERenderer;
import org.exoplatform.web.controller.regexp.REVisitor;
/**
* @author <a href="mailto:julien.viet@exoplatform.com">Julien Viet</a>
*/
public class ValueResolverFactory extends REVisitor<RuntimeException> {
static class Alternative {
/** . */
private StringBuilder resolvingExpression = new StringBuilder();
/** . */
private String prefix;
/** . */
private String suffix;
/** . */
private StringBuilder buffer = new StringBuilder();
/** . */
private StringBuilder valueMatcher = new StringBuilder();
public StringBuilder getResolvingExpression() {
return resolvingExpression;
}
public String getPrefix() {
return prefix;
}
public String getSuffix() {
return suffix;
}
public StringBuilder getValueMatcher() {
return valueMatcher;
}
@Override
public String toString() {
return getClass().getSimpleName() + "[" + resolvingExpression + "]";
}
}
/** . */
private List<Alternative> alternatives = new ArrayList<Alternative>();
/** . */
private Alternative current = null;
public List<Alternative> foo(RENode root) {
alternatives.clear();
root.accept(this);
return alternatives;
}
@Override
protected void visit(RENode.Disjunction disjunction) throws RuntimeException {
if (current != null) {
RENode.Alternative alternative = disjunction.getAlternative();
if (alternative != null) {
alternative.accept(this);
}
} else {
RENode.Alternative alternative = disjunction.getAlternative();
if (alternative != null) {
current = new Alternative();
alternative.accept(this);
current.suffix = current.buffer.toString();
current.buffer.setLength(0);
alternatives.add(current);
current = null;
}
//
RENode.Disjunction next = disjunction.getNext();
if (next != null) {
next.accept(this);
}
}
}
@Override
protected void visit(RENode.Group expr) throws RuntimeException {
if (expr.getType() == GroupType.CAPTURING_GROUP) {
try {
RERenderer renderer = new RERenderer(current.resolvingExpression);
expr.accept(renderer);
} catch (IOException e) {
// Should not happen
throw new AssertionError(e);
}
try {
RERenderer renderer = new RERenderer(current.valueMatcher);
expr.accept(renderer);
} catch (IOException e) {
// Should not happen
throw new AssertionError(e);
}
current.prefix = current.buffer.toString();
current.buffer.setLength(0);
} else {
super.visit(expr);
}
}
@Override
protected void visit(RENode.Alternative alternative) throws RuntimeException {
alternative.getExpr().accept(this);
RENode.Alternative next = alternative.getNext();
if (next != null) {
next.accept(this);
}
}
@Override
protected void visit(RENode.Char expr) throws RuntimeException {
for (int i = expr.getMin(); i > 0; i--) {
current.resolvingExpression.append(expr.getValue());
current.buffer.append(expr.getValue());
}
}
@Override
protected void visit(RENode.Any expr) throws RuntimeException {
for (int i = expr.getMin(); i > 0; i--) {
// Any can be 'a'
current.resolvingExpression.append('a');
current.buffer.append('a');
}
}
/** . */
private Solver solver;
@Override
protected void visit(RENode.CharacterClass expr) throws RuntimeException {
expr.getExpr().accept(this);
for (int i = expr.getMin(); i > 0; i--) {
if (solver.hasNext()) {
char c = solver.next();
current.resolvingExpression.append(c);
current.buffer.append(c);
solver.reset();
} else {
throw new UnsupportedOperationException("wtf?");
}
}
}
@Override
protected void visit(RENode.CharacterClassExpr.Or expr) throws RuntimeException {
expr.getLeft().accept(this);
Solver left = solver;
expr.getRight().accept(this);
Solver right = solver;
solver = new Solver.Or(left, right);
}
@Override
protected void visit(RENode.CharacterClassExpr.Range expr) throws RuntimeException {
RENode.CharacterClassExpr.Char from = expr.getFrom();
RENode.CharacterClassExpr.Char to = expr.getTo();
solver = new Solver.Range(from.getValue(), to.getValue());
}
@Override
protected void visit(RENode.CharacterClassExpr.Char expr) throws RuntimeException {
solver = new Solver.Char(expr.getValue());
}
@Override
protected void visit(RENode.CharacterClassExpr.And expr) throws RuntimeException {
expr.getLeft().accept(this);
Solver left = solver;
expr.getRight().accept(this);
Solver right = solver;
solver = new Solver.And(left, right);
}
@Override
protected void visit(RENode.CharacterClassExpr.Not expr) throws RuntimeException {
RENode.CharacterClassExpr negated = expr.getNegated();
if (negated == null) {
// Do nothing ?
} else {
negate(negated);
}
}
private void negate(RENode.CharacterClassExpr negated) throws RuntimeException {
if (negated instanceof RENode.CharacterClassExpr.Not) {
RENode.CharacterClassExpr nested = ((RENode.CharacterClassExpr.Not) negated).getNegated();
if (nested != null) {
nested.accept(this);
}
} else if (negated instanceof RENode.CharacterClassExpr.Or) {
RENode.CharacterClassExpr.Or or = (RENode.CharacterClassExpr.Or) negated;
negate(or.getLeft());
Solver left = solver;
negate(or.getRight());
Solver right = solver;
solver = new Solver.And(left, right);
} else if (negated instanceof RENode.CharacterClassExpr.And) {
RENode.CharacterClassExpr.And or = (RENode.CharacterClassExpr.And) negated;
negate(or.getLeft());
Solver left = solver;
negate(or.getRight());
Solver right = solver;
solver = new Solver.Or(left, right);
} else {
char from;
char to;
if (negated instanceof RENode.CharacterClassExpr.Char) {
from = to = ((RENode.CharacterClassExpr.Char) negated).getValue();
} else if (negated instanceof RENode.CharacterClassExpr.Range) {
RENode.CharacterClassExpr.Range range = (RENode.CharacterClassExpr.Range) negated;
from = range.getFrom().getValue();
to = range.getTo().getValue();
} else {
throw new UnsupportedOperationException();
}
Solver.Range left = null;
Character c = prevValid(--from);
if (c != null) {
left = new Solver.Range(' ', c);
}
Solver.Range right = null;
c = nextValid(++to);
if (c != null) {
right = new Solver.Range(c, Character.MAX_VALUE);
}
if (left == null) {
if (right != null) {
solver = right;
}
} else {
if (right == null) {
solver = left;
} else {
solver = new Solver.Or(left, right);
}
}
}
}
private abstract static class Solver implements Iterator<Character> {
public void remove() {
throw new UnsupportedOperationException();
}
protected abstract void reset();
private static class And extends Solver {
/** . */
private final Solver left;
/** . */
private final Solver right;
/** . */
private Character leftChar;
/** . */
private Character next;
private And(Solver left, Solver right) {
this.left = left;
this.right = right;
this.next = null;
this.leftChar = null;
}
public boolean hasNext() {
while (next == null) {
if (leftChar == null) {
if (left.hasNext()) {
leftChar = left.next();
} else {
break;
}
}
if (right.hasNext()) {
Character c = right.next();
if (c == leftChar) {
next = c;
}
} else {
right.reset();
leftChar = null;
}
}
return next != null;
}
public Character next() {
if (!hasNext()) {
throw new NoSuchElementException();
}
Character tmp = next;
next = null;
return tmp;
}
@Override
protected void reset() {
left.reset();
right.reset();
}
}
private static class Or extends Solver {
/** . */
private final Solver left;
/** . */
private final Solver right;
private Or(Solver left, Solver right) {
this.left = left;
this.right = right;
}
public boolean hasNext() {
return left.hasNext() || right.hasNext();
}
public Character next() {
if (left.hasNext()) {
return left.next();
} else if (right.hasNext()) {
return right.next();
}
throw new NoSuchElementException();
}
@Override
protected void reset() {
left.reset();
right.reset();
}
}
private static class Range extends Solver {
/** . */
private char from;
/** . */
private char current;
/** . */
private char to;
private Range(char from, char to) {
this.from = from;
this.current = from;
this.to = to;
}
public boolean hasNext() {
return current < to;
}
public Character next() {
if (current >= to) {
throw new NoSuchElementException();
}
return current++;
}
@Override
protected void reset() {
current = from;
}
}
private static class Char extends Solver {
/** . */
private final char value;
/** . */
private boolean done;
private Char(char value) {
this.value = value;
this.done = false;
}
public boolean hasNext() {
return !done;
}
public Character next() {
if (done) {
throw new NoSuchElementException();
}
done = true;
return value;
}
@Override
protected void reset() {
done = false;
}
}
}
private static Character nextValid(char from) {
while (true) {
if (!Character.isISOControl(from)) {
return from;
} else {
if (from == Character.MAX_VALUE) {
return null;
} else {
from++;
}
}
}
}
private static Character prevValid(char from) {
while (true) {
if (!Character.isISOControl(from)) {
return from;
} else {
if (from == Character.MIN_VALUE) {
return null;
} else {
from--;
}
}
}
}
}