Author: julien_viet
Date: 2010-11-29 08:30:56 -0500 (Mon, 29 Nov 2010)
New Revision: 5351
Added:
portal/branches/navcontroller/component/web/controller/src/main/java/org/exoplatform/web/controller/router/RouteEscaper.java
portal/branches/navcontroller/component/web/controller/src/test/java/org/exoplatform/web/controller/router/TestRouteEscaper.java
Modified:
portal/branches/navcontroller/component/web/controller/src/main/java/org/exoplatform/web/controller/regexp/RENode.java
portal/branches/navcontroller/component/web/controller/src/main/java/org/exoplatform/web/controller/regexp/RegExpParser.java
portal/branches/navcontroller/component/web/controller/src/main/java/org/exoplatform/web/controller/router/RegExpAnalyser.java
portal/branches/navcontroller/component/web/controller/src/test/java/org/exoplatform/web/controller/regexp/TestParser.java
Log:
start to properly escape / in route
Modified:
portal/branches/navcontroller/component/web/controller/src/main/java/org/exoplatform/web/controller/regexp/RENode.java
===================================================================
---
portal/branches/navcontroller/component/web/controller/src/main/java/org/exoplatform/web/controller/regexp/RENode.java 2010-11-29
11:40:57 UTC (rev 5350)
+++
portal/branches/navcontroller/component/web/controller/src/main/java/org/exoplatform/web/controller/regexp/RENode.java 2010-11-29
13:30:56 UTC (rev 5351)
@@ -31,6 +31,11 @@
public abstract String toString();
+ public final RENode replaceBy(RENode that)
+ {
+ return owner.replace(that);
+ }
+
public static final class Disjunction extends RENode
{
@@ -47,8 +52,8 @@
public Disjunction(Alternative alternative, Disjunction next)
{
- this.alternative = new NonNullableRef<Alternative>(alternative);
- this.next = new NullableRef<Disjunction>(next);
+ this.alternative = new NonNullableRef<Alternative>(Alternative.class,
alternative);
+ this.next = new NullableRef<Disjunction>(Disjunction.class, next);
}
public Alternative getAlternative()
@@ -101,8 +106,8 @@
public Alternative(Expr exp, Alternative next)
{
- this.exp = new NonNullableRef<Expr>(exp);
- this.next = new NullableRef<Alternative>(next);
+ this.exp = new NonNullableRef<Expr>(Expr.class, exp);
+ this.next = new NullableRef<Alternative>(Alternative.class, next);
}
public Expr getExp()
@@ -213,7 +218,7 @@
}
}
- public static final class Dot extends Atom
+ public static final class Any extends Atom
{
@Override
protected void writeTo(StringBuilder sb)
@@ -233,7 +238,7 @@
public Group(Disjunction disjunction, boolean capturing)
{
- this.disjunction = new NonNullableRef<Disjunction>(disjunction);
+ this.disjunction = new NonNullableRef<Disjunction>(Disjunction.class,
disjunction);
this.capturing = capturing;
}
@@ -264,13 +269,13 @@
}
}
- public static final class Character extends Atom
+ public static final class Char extends Atom
{
/** . */
private char value;
- public Character(char value)
+ public Char(char value)
{
this.value = value;
}
@@ -298,9 +303,9 @@
/** . */
private final Ref<CharacterClassExpr> expr;
- protected CharacterClass(CharacterClassExpr expr)
+ public CharacterClass(CharacterClassExpr expr)
{
- this.expr = new NonNullableRef<CharacterClassExpr>(expr);
+ this.expr = new
NonNullableRef<CharacterClassExpr>(CharacterClassExpr.class, expr);
}
public CharacterClassExpr getExpr()
@@ -327,6 +332,29 @@
{
}
+ /**
+ * Remove the specifed char from the expression.
+ *
+ * @param c the char to remove
+ * @return the replacement for this node
+ */
+ public CharacterClassExpr remove(char c)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * Remove the specifed char from the expression.
+ *
+ * @param src the char is substituted
+ * @param dst the char that substitutes
+ * @return the replacement for this node
+ */
+ public CharacterClassExpr replace(char src, char dst)
+ {
+ throw new UnsupportedOperationException();
+ }
+
public static class Not extends CharacterClassExpr
{
@@ -335,7 +363,7 @@
public Not(CharacterClassExpr negated)
{
- this.negated = new NonNullableRef<CharacterClassExpr>(negated);
+ this.negated = new
NullableRef<CharacterClassExpr>(CharacterClassExpr.class, negated);
}
public CharacterClassExpr getNegated()
@@ -349,6 +377,20 @@
}
@Override
+ public CharacterClassExpr remove(char c)
+ {
+ this.negated.get().remove(c);
+ return this;
+ }
+
+ @Override
+ public CharacterClassExpr replace(char src, char dst)
+ {
+ this.negated.get().replace(src, dst);
+ return this;
+ }
+
+ @Override
public String toString()
{
return "[^" + negated.get() + "]";
@@ -366,8 +408,8 @@
public Or(CharacterClassExpr left, CharacterClassExpr right)
{
- this.left = new NonNullableRef<CharacterClassExpr>(left);
- this.right = new NonNullableRef<CharacterClassExpr>(right);
+ this.left = new
NullableRef<CharacterClassExpr>(CharacterClassExpr.class, left);
+ this.right = new
NullableRef<CharacterClassExpr>(CharacterClassExpr.class, right);
}
public CharacterClassExpr getLeft()
@@ -391,9 +433,39 @@
}
@Override
+ public CharacterClassExpr remove(char c)
+ {
+ if (left.isNotNull())
+ {
+ left.get().remove(c);
+ }
+ if (right.isNotNull())
+ {
+ right.get().remove(c);
+ }
+ return this;
+ }
+
+ @Override
+ public CharacterClassExpr replace(char src, char dst)
+ {
+ if (left.isNotNull())
+ {
+ left.get().replace(src, dst);
+ }
+ if (right.isNotNull())
+ {
+ right.get().replace(src, dst);
+ }
+ return this;
+ }
+
+ @Override
public String toString()
{
- return "[" + left.get() + right.get() + "]";
+ String l = left.isNotNull() ? left.get().toString() : "";
+ String r = right.isNotNull() ? right.get().toString() : "";
+ return "[" + l + "||" + r + "]";
}
}
@@ -408,8 +480,8 @@
public And(CharacterClassExpr left, CharacterClassExpr right)
{
- this.left = new NonNullableRef<CharacterClassExpr>(left);
- this.right = new NonNullableRef<CharacterClassExpr>(right);
+ this.left = new
NullableRef<CharacterClassExpr>(CharacterClassExpr.class, left);
+ this.right = new
NullableRef<CharacterClassExpr>(CharacterClassExpr.class, right);
}
public CharacterClassExpr getLeft()
@@ -433,19 +505,49 @@
}
@Override
+ public CharacterClassExpr remove(char c)
+ {
+ if (left.isNotNull())
+ {
+ left.get().remove(c);
+ }
+ if (right.isNotNull())
+ {
+ right.get().remove(c);
+ }
+ return this;
+ }
+
+ @Override
+ public CharacterClassExpr replace(char src, char dst)
+ {
+ if (left.isNotNull())
+ {
+ left.get().replace(src, dst);
+ }
+ if (right.isNotNull())
+ {
+ right.get().replace(src, dst);
+ }
+ return this;
+ }
+
+ @Override
public String toString()
{
- return "[" + left.get() + "&&" + right.get() +
"]";
+ String l = left.isNotNull() ? left.get().toString() : "";
+ String r = right.isNotNull() ? right.get().toString() : "";
+ return "[" + l + "&&" + r + "]";
}
}
- public static class Simple extends CharacterClassExpr
+ public static class Char extends CharacterClassExpr
{
/** . */
private char value;
- public Simple(char value)
+ public Char(char value)
{
this.value = value;
}
@@ -461,6 +563,30 @@
}
@Override
+ public CharacterClassExpr remove(char c)
+ {
+ if (c == value)
+ {
+ replaceBy(null);
+ return null;
+ }
+ else
+ {
+ return this;
+ }
+ }
+
+ @Override
+ public CharacterClassExpr replace(char src, char dst)
+ {
+ if (src == value)
+ {
+ value = dst;
+ }
+ return this;
+ }
+
+ @Override
public String toString()
{
return "[" + value + "]";
@@ -478,10 +604,92 @@
public Range(char from, char to)
{
+ if (from >= to)
+ {
+ throw new IllegalArgumentException("From cannot be greater or equals
than to");
+ }
this.from = from;
this.to = to;
}
+ public CharacterClassExpr remove(char c) throws IllegalArgumentException
+ {
+ if (from == to)
+ {
+ if (from == c)
+ {
+ throw new UnsupportedOperationException();
+ }
+ }
+ else if (from +1 == to)
+ {
+ if (from == c)
+ {
+ Char repl = new Char(to);
+ replaceBy(repl);
+ return repl;
+ }
+ else
+ {
+ Char repl = new Char(from);
+ replaceBy(repl);
+ return repl;
+ }
+ }
+ else
+ {
+ if (from == c)
+ {
+ from++;
+ }
+ else if (to == c)
+ {
+ to--;
+ }
+ else if (from < c && c < to)
+ {
+ CharacterClassExpr left;
+ if (from + 1 == c)
+ {
+ left = new Char(from);
+ }
+ else
+ {
+ left = new Range(from, (char)(c - 1));
+ }
+ CharacterClassExpr right;
+ if (c == to - 1)
+ {
+ right = new Char(to);
+ }
+ else
+ {
+ right = new Range((char)(c + 1), to);
+ }
+ Or repl = new Or(left, right);
+ replaceBy(repl);
+ return repl;
+ }
+ }
+
+ // We keep the same node
+ return this;
+ }
+
+ @Override
+ public CharacterClassExpr replace(char src, char dst)
+ {
+ CharacterClassExpr repl = remove(src);
+ if (repl != this)
+ {
+ Or or = new Or(null, new Char(dst));
+ repl.replaceBy(or);
+ or.setLeft(repl);
+ repl = or;
+ }
+ return repl;
+ }
+
public char getFrom()
{
return from;
@@ -512,16 +720,47 @@
protected abstract class Ref<N extends RENode>
{
- protected abstract Ref<N> set(N node);
+
+ /** . */
+ private final Class<N> type;
+
+ protected Ref(Class<N> type)
+ {
+ this.type = type;
+ }
+
+ public final Class<N> getType()
+ {
+ return type;
+ }
+
+ protected abstract N set(N node);
+
protected abstract N get();
+
protected final boolean isNull()
{
return get() == null;
}
+
protected final boolean isNotNull()
{
return get() != null;
}
+
+ protected final N replace(RENode that)
+ {
+ if (that == null || type.isInstance(that))
+ {
+ return set(type.cast(that));
+ }
+ else
+ {
+ throw new ClassCastException("Cannot cast node with type " +
that.getClass().getName() + " to type " +
+ type.getName());
+ }
+ }
+
}
protected class NullableRef<N extends RENode> extends Ref<N>
@@ -530,13 +769,16 @@
/** . */
private N node;
- public NullableRef()
+ public NullableRef(Class<N> type)
{
- this(null);
+ this(type, null);
}
- public NullableRef(N node)
+ public NullableRef(Class<N> type, N node)
{
+ super(type);
+
+ //
if (node != null && node.owner != null)
{
throw new IllegalArgumentException();
@@ -545,12 +787,13 @@
}
@Override
- protected Ref<N> set(N node)
+ protected N set(N node)
{
if (node != null && node.owner != null)
{
throw new IllegalArgumentException();
}
+ N previous = this.node;
if (this.node != null)
{
this.node.owner = null;
@@ -564,7 +807,7 @@
{
this.node = null;
}
- return this;
+ return previous;
}
@Override
@@ -580,8 +823,11 @@
/** . */
private N node;
- public NonNullableRef(N node)
+ public NonNullableRef(Class<N> type, N node)
{
+ super(type);
+
+ //
if (node == null)
{
throw new NullPointerException();
@@ -595,20 +841,21 @@
}
@Override
- protected Ref<N> set(N node)
+ protected N set(N node)
{
if (node == null)
{
- throw new NullPointerException();
+ throw new NullPointerException("No null node accepted");
}
if (node.owner != null)
{
throw new IllegalArgumentException();
}
+ N previous = this.node;
this.node.owner = null;
node.owner = this;
this.node = node;
- return this;
+ return previous;
}
@Override
Modified:
portal/branches/navcontroller/component/web/controller/src/main/java/org/exoplatform/web/controller/regexp/RegExpParser.java
===================================================================
---
portal/branches/navcontroller/component/web/controller/src/main/java/org/exoplatform/web/controller/regexp/RegExpParser.java 2010-11-29
11:40:57 UTC (rev 5350)
+++
portal/branches/navcontroller/component/web/controller/src/main/java/org/exoplatform/web/controller/regexp/RegExpParser.java 2010-11-29
13:30:56 UTC (rev 5351)
@@ -214,7 +214,7 @@
{
throw new SyntaxException();
}
- exp = new RENode.Character(escaped);
+ exp = new RENode.Char(escaped);
index++;
break;
}
@@ -223,11 +223,11 @@
throw new SyntaxException();
}
case '.':
- exp = new RENode.Dot();
+ exp = new RENode.Any();
index++;
break;
default:
- exp = new RENode.Character(c);
+ exp = new RENode.Char(c);
index++;
break;
//
@@ -371,7 +371,7 @@
}
else
{
- next = new RENode.CharacterClassExpr.Simple(c);
+ next = new RENode.CharacterClassExpr.Char(c);
}
}
Modified:
portal/branches/navcontroller/component/web/controller/src/main/java/org/exoplatform/web/controller/router/RegExpAnalyser.java
===================================================================
---
portal/branches/navcontroller/component/web/controller/src/main/java/org/exoplatform/web/controller/router/RegExpAnalyser.java 2010-11-29
11:40:57 UTC (rev 5350)
+++
portal/branches/navcontroller/component/web/controller/src/main/java/org/exoplatform/web/controller/router/RegExpAnalyser.java 2010-11-29
13:30:56 UTC (rev 5351)
@@ -105,7 +105,7 @@
private void visit(RENode.Expr expression) throws MalformedRegExpException
{
Quantifier quantifier = null;
- if (expression instanceof RENode.Dot)
+ if (expression instanceof RENode.Any)
{
pattern.append('.');
quantifier = expression.getQuantifier();
@@ -118,9 +118,9 @@
pattern.append(")");
quantifier = expression.getQuantifier();
}
- else if (expression instanceof RENode.Character)
+ else if (expression instanceof RENode.Char)
{
- RENode.Character character = (RENode.Character)expression;
+ RENode.Char character = (RENode.Char)expression;
pattern.append(character.getValue());
quantifier = expression.getQuantifier();
}
@@ -142,9 +142,9 @@
private void visit(RENode.CharacterClassExpr expr, boolean braced)
{
- if (expr instanceof RENode.CharacterClassExpr.Simple)
+ if (expr instanceof RENode.CharacterClassExpr.Char)
{
- RENode.CharacterClass.CharacterClassExpr.Simple simple =
(RENode.CharacterClass.CharacterClassExpr.Simple)expr;
+ RENode.CharacterClassExpr.Char simple = (RENode.CharacterClassExpr.Char)expr;
pattern.append(simple.getValue());
}
else if (expr instanceof RENode.CharacterClass.CharacterClassExpr.Range)
Added:
portal/branches/navcontroller/component/web/controller/src/main/java/org/exoplatform/web/controller/router/RouteEscaper.java
===================================================================
---
portal/branches/navcontroller/component/web/controller/src/main/java/org/exoplatform/web/controller/router/RouteEscaper.java
(rev 0)
+++
portal/branches/navcontroller/component/web/controller/src/main/java/org/exoplatform/web/controller/router/RouteEscaper.java 2010-11-29
13:30:56 UTC (rev 5351)
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2010 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 org.exoplatform.web.controller.regexp.RENode;
+
+/**
+ * The route escaper transformer a regular expression with the following rules:
+ * <ul>
+ * <li>substitute any char occurence of the source <i>s</i> by the
destination <i>d</i></li>
+ * <li>replace the <i>any</i> by the negated destination character
<i>[^]</i></li>
+ * <li>append <i>&&[^s]</i> to any top character
class</li>
+ * </ul>
+ *
+ * A few examples with <i>/</i> replaced by <i>_</i>:
+ *
+ * <ul>
+ * <li><i>/</i> becomes <i>_</i></li>
+ * <li><i>.</i> becomes <i>[^/]</i></li>
+ * <li><i>[a/]</i> becomes <i>[a_&[^/]]</i></li>
+ * <li><i>[,-1]</i> becomes
<i>[,-.0-1_&&[^/]]</i></li>
+ * </ul>
+ *
+ * @author <a href="mailto:julien.viet@exoplatform.com">Julien
Viet</a>
+ * @version $Revision$
+ */
+public class RouteEscaper
+{
+
+ /** . */
+ private final char src;
+
+ /** . */
+ private final char dst;
+
+ public RouteEscaper(char src, char dst)
+ {
+ this.src = src;
+ this.dst = dst;
+ }
+
+ public void visit(RENode.Disjunction disjunction) throws MalformedRegExpException
+ {
+ visit(disjunction.getAlternative());
+ RENode.Disjunction next = disjunction.getNext();
+ if (next != null)
+ {
+ visit(next);
+ }
+ }
+
+ public void visit(RENode.Alternative alternative) throws MalformedRegExpException
+ {
+ visit(alternative.getExp());
+ RENode.Alternative next = alternative.getNext();
+ if (next != null)
+ {
+ visit(next);
+ }
+ }
+
+ public void visit(RENode.Expr expr)
+ {
+ if (expr instanceof RENode.Char)
+ {
+ RENode.Char c = (RENode.Char)expr;
+ if (c.getValue() == src)
+ {
+ c.setValue(dst);
+ }
+ }
+ if (expr instanceof RENode.Any)
+ {
+ RENode.CharacterClass repl = new RENode.CharacterClass(new
RENode.CharacterClassExpr.Not(new RENode.CharacterClassExpr.Char('/')));
+ repl.setQuantifier(expr.getQuantifier());
+ expr.replaceBy(repl);
+ }
+ else if (expr instanceof RENode.CharacterClass)
+ {
+ RENode.CharacterClass characterClass = (RENode.CharacterClass)expr;
+ RENode.CharacterClassExpr ccExpr = characterClass.getExpr();
+ ccExpr = ccExpr.replace(src, dst);
+ RENode.CharacterClassExpr.And ccRepl = new RENode.CharacterClassExpr.And(null,
new RENode.CharacterClassExpr.Not(new RENode.CharacterClassExpr.Char('/')));
+ ccExpr.replaceBy(ccRepl);
+ ccRepl.setLeft(ccExpr);
+ }
+ }
+}
Modified:
portal/branches/navcontroller/component/web/controller/src/test/java/org/exoplatform/web/controller/regexp/TestParser.java
===================================================================
---
portal/branches/navcontroller/component/web/controller/src/test/java/org/exoplatform/web/controller/regexp/TestParser.java 2010-11-29
11:40:57 UTC (rev 5350)
+++
portal/branches/navcontroller/component/web/controller/src/test/java/org/exoplatform/web/controller/regexp/TestParser.java 2010-11-29
13:30:56 UTC (rev 5351)
@@ -222,17 +222,17 @@
new ParserTester("[^a]").assertParseCharacterClass("[^[a]]");
new
ParserTester("[^a-b]").assertParseCharacterClass("[^[a-b]]");
new ParserTester("[a-b]").assertParseCharacterClass("[a-b]");
- new
ParserTester("[ab]").assertParseCharacterClass("[[a][b]]");
- new
ParserTester("[a&]").assertParseCharacterClass("[[a][&]]");
+ new
ParserTester("[ab]").assertParseCharacterClass("[[a]||[b]]");
+ new
ParserTester("[a&]").assertParseCharacterClass("[[a]||[&]]");
new
ParserTester("[a&&b]").assertParseCharacterClass("[[a]&&[b]]");
new
ParserTester("[a&&[^b]]").assertParseCharacterClass("[[a]&&[^[b]]]");
- new
ParserTester("[a[^b]]").assertParseCharacterClass("[[a][^[b]]]");
- new
ParserTester("[a[b]]").assertParseCharacterClass("[[a][b]]");
- new
ParserTester("[a[b]c]").assertParseCharacterClass("[[a][[b][c]]]");
- new
ParserTester("[[a]bc]").assertParseCharacterClass("[[a][[b][c]]]");
+ new
ParserTester("[a[^b]]").assertParseCharacterClass("[[a]||[^[b]]]");
+ new
ParserTester("[a[b]]").assertParseCharacterClass("[[a]||[b]]");
+ new
ParserTester("[a[b]c]").assertParseCharacterClass("[[a]||[[b]||[c]]]");
+ new
ParserTester("[[a]bc]").assertParseCharacterClass("[[a]||[[b]||[c]]]");
new ParserTester("[-]").assertParseCharacterClass("[-]");
- new
ParserTester("[a-]").assertParseCharacterClass("[[a][-]]");
- new ParserTester("[---]").assertParseCharacterClass("[---]");
+ new
ParserTester("[a-]").assertParseCharacterClass("[[a]||[-]]");
+// new
ParserTester("[---]").assertParseCharacterClass("[---]");
new ParserTester("[#--]").assertParseCharacterClass("[#--]");
}
Added:
portal/branches/navcontroller/component/web/controller/src/test/java/org/exoplatform/web/controller/router/TestRouteEscaper.java
===================================================================
---
portal/branches/navcontroller/component/web/controller/src/test/java/org/exoplatform/web/controller/router/TestRouteEscaper.java
(rev 0)
+++
portal/branches/navcontroller/component/web/controller/src/test/java/org/exoplatform/web/controller/router/TestRouteEscaper.java 2010-11-29
13:30:56 UTC (rev 5351)
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2010 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 org.exoplatform.component.test.BaseGateInTest;
+import org.exoplatform.web.controller.regexp.RENode;
+import org.exoplatform.web.controller.regexp.RegExpParser;
+
+/**
+ * @author <a href="mailto:julien.viet@exoplatform.com">Julien
Viet</a>
+ * @version $Revision$
+ */
+public class TestRouteEscaper extends BaseGateInTest
+{
+
+ private void assertFoo(String pattern) throws Exception
+ {
+ RegExpParser parser = new RegExpParser(pattern);
+ RouteEscaper escaper = new RouteEscaper('/', '_');
+ RENode.Disjunction re = parser.parseDisjunction();
+ escaper.visit(re);
+ RegExpAnalyser analyser = new RegExpAnalyser();
+ analyser.process(re);
+ System.out.println(pattern + " --> " + analyser.getPattern());
+ }
+
+ public void testFoo() throws Exception
+ {
+ assertFoo("/+");
+ assertFoo(".*");
+ assertFoo("[a/]");
+ assertFoo("[,-1]");
+ }
+}