/* * 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--; } } } } }