/* * 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 java.io.IOException; import java.util.BitSet; import java.util.Collections; import java.util.Iterator; import java.util.Map; import org.exoplatform.web.controller.QualifiedName; import org.exoplatform.web.controller.metadata.ControllerDescriptor; import org.exoplatform.web.controller.metadata.RouteDescriptor; import org.exoplatform.web.url.MimeType; import org.gatein.common.io.UndeclaredIOException; import org.gatein.common.util.Tools; /** * The router takes care of mapping a request to a a map. * * @author <a href="mailto:julien.viet@exoplatform.com">Julien Viet</a> * @version $Revision$ */ public class Router { /** . */ private static final BitSet escapeSet; static { // A subset of the path literals BitSet bs = new BitSet(); bs.set('_'); bs.set('.'); bs.set('-'); bs.set('~'); bs.set('!'); bs.set('$'); bs.set('&'); bs.set('+'); bs.set(':'); bs.set('@'); // escapeSet = bs; } /** . */ private final RegexFactory regexFactory; /** The root route. */ final Route root; /** The slash escape char. */ final char separatorEscape; /** . */ final char separatorEscapeNible1; /** . */ final char separatorEscapeNible2; /** . */ private Regex[] regexes; public Router(ControllerDescriptor metaData) throws RouterConfigException { this(metaData, RegexFactory.JAVA); } public Router(ControllerDescriptor metaData, RegexFactory regexFactory) throws RouterConfigException { char separtorEscape = metaData.getSeparatorEscape(); // int i = separtorEscape & ~0x7F; if (i > 0 || !escapeSet.get(separtorEscape)) { throw new RouterConfigException("Char " + (int) separtorEscape + " cannot be used a separator escape"); } // String s = Integer.toString(separtorEscape, 16).toUpperCase(); separatorEscapeNible1 = s.charAt(0); separatorEscapeNible2 = s.charAt(1); // this.regexFactory = regexFactory; this.root = new Route(this); this.separatorEscape = separtorEscape; this.regexes = new Regex[0]; // for (RouteDescriptor routeMetaData : metaData.getRoutes()) { root.append(routeMetaData); } } Regex compile(String pattern) { for (Regex regex : regexes) { if (regex.getPattern().equals(pattern)) { return regex; } } Regex regex = regexFactory.compile(pattern); regex.index = regexes.length; regexes = Tools.appendTo(regexes, regex); return regex; } public void render(Map<QualifiedName, String> parameters, URIWriter writer) throws IOException { render(new RenderContext(parameters), writer); } public String render(Map<QualifiedName, String> parameters) { return render(new RenderContext(parameters)); } public void render(RenderContext context, URIWriter writer) throws IOException { if (context.matchers == null) { context.matchers = new Regex.Matcher[regexes.length]; } root.render(context, writer); } public String render(RenderContext context) { try { StringBuilder sb = new StringBuilder(); URIWriter renderContext = new URIWriter(sb, MimeType.PLAIN); render(context, renderContext); return sb.toString(); } catch (IOException e) { throw new UndeclaredIOException(e); } } public Map<QualifiedName, String> route(String path) { return route(path, Collections.<String, String[]> emptyMap()); } public Map<QualifiedName, String> route(String path, Map<String, String[]> queryParams) { Iterator<Map<QualifiedName, String>> matcher = matcher(path, queryParams); if (matcher.hasNext()) { return matcher.next(); } else { return null; } } public Iterator<Map<QualifiedName, String>> matcher(String path, Map<String, String[]> queryParams) { return root.route(path, queryParams); } @Override public String toString() { return "Router[" + root.toString() + "]"; } }