Package com.ocpsoft.pretty.faces.url

Source Code of com.ocpsoft.pretty.faces.url.URLPatternParser

/*
* Copyright 2010 Lincoln Baxter, III
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*     http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.ocpsoft.pretty.faces.url;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import com.ocpsoft.pretty.PrettyException;
import com.ocpsoft.pretty.faces.config.mapping.PathParameter;
import com.ocpsoft.pretty.faces.el.ExpressionProcessorRunner;
import com.ocpsoft.pretty.faces.el.Expressions;

/**
* @author Lincoln Baxter, III <lincoln@ocpsoft.com>
*/
public class URLPatternParser
{

   private static final Pattern EL_REGEX_PATTERN = Pattern.compile(Expressions.EL_REGEX);

   private final String originalPattern;
   private boolean elPattern;
   private URL urlPattern = null;
   private Pattern urlElPattern;
   private List<Segment> pathSegments = new ArrayList<Segment>();
   private List<PathParameter> pathParameters = new ArrayList<PathParameter>();

   /**
    * Set the pattern for which this parser will match. Find and replace all el expressions with regular expressions to
    * extract values from parsed URLs. Also extract all parameter names from expressions, and replace with valid EL
    *
    * @param pattern Pattern to use as a parse template
    */
   public URLPatternParser(final String pattern)
   {
      originalPattern = pattern;
      Matcher expressionMatcher = EL_REGEX_PATTERN.matcher(pattern);
      StringBuffer segmentableExpressions = new StringBuffer();

      elPattern = false;
      if (pattern.indexOf('|') != -1) {
         elPattern = true;
      }

      /*
       * Extract path parameters
       */
      int paramIndex = 0;
      while (expressionMatcher.find())
      {
         elPattern = true;
         String expression = expressionMatcher.group(1);
         PathParameter param = ExpressionProcessorRunner.process(expression);
         param.setPosition(paramIndex);
         pathParameters.add(param);

         expressionMatcher.appendReplacement(segmentableExpressions, Segment.parameterize(paramIndex));
         paramIndex++;
      }
      expressionMatcher.appendTail(segmentableExpressions);
      this.pathParameters = Collections.unmodifiableList(pathParameters);

      URL segmentedPattern = new URL(segmentableExpressions.toString());

      /*
       * Extract path segments, overlaying regexes found during parameter
       * discovery.
       */
      for (String segmentPattern : segmentedPattern.getSegments())
      {
         Segment segment = new Segment();
         segment.setTemplate(segmentPattern);
         StringBuffer regex = new StringBuffer();

         Matcher parameterMatcher = Segment.getTemplateMatcher(segmentPattern);
         while (parameterMatcher.find())
         {
            String group = parameterMatcher.group(1);
            PathParameter parameter = pathParameters.get(Integer.valueOf(group));
            segment.addParameter(parameter);
            parameterMatcher.appendReplacement(regex, "(" + parameter.getRegex() + ")");
         }
         parameterMatcher.appendTail(regex);

         segment.setRegex(regex.toString());
         pathSegments.add(segment);
      }

      List<String> regexSegments = new ArrayList<String>();
      for (Segment s : pathSegments)
      {
         regexSegments.add(s.getRegex());
      }

      urlPattern = new URL(regexSegments, segmentedPattern.getMetadata().copy());
      if (elPattern) {
         urlElPattern = Pattern.compile(urlPattern.toURL());
      }
      this.pathSegments = Collections.unmodifiableList(pathSegments);
   }

   /**
    * Return true if this parser matches the given URL, otherwise, return false.
    */
   public boolean matches(final URL target)
   {
      if (elPattern) {
         return urlElPattern.matcher(target.toURL()).matches();
      }
      else {
         return urlPattern.toURL().equals(target.toURL());
      }
   }

   /**
    * Return true if this parser matches the given URL, otherwise, return false.
    */
   @Deprecated
   public boolean matches(final String target)
   {
      URL url = new URL(target);
      return matches(url);
   }

   /**
    * Return the list of parameters specified by this pattern.
    */
   public List<PathParameter> getPathParameters()
   {
      return pathParameters;
   }

   /**
    * Builds a list of PathParameters for this UrlPattern, extracted from the provided URL (assuming a match is found).
    * This list excludes duplicate named parameters.
    */
   public List<PathParameter> parse(final URL url)
   {
      List<PathParameter> result = new ArrayList<PathParameter>();

      String inboundUrl = url.toURL();
      if (this.matches(url))
      {
         Iterator<Segment> iter = pathSegments.iterator();
         while (iter.hasNext())
         {
            Segment segment = iter.next();

            String regex = "";
            if (url.hasLeadingSlash() && !"/".equals(url.toURL()))
            {
               regex += "/";
            }

            regex += segment.getRegex();
            if (iter.hasNext() || url.hasTrailingSlash())
            {
               regex += "/";
            }

            Matcher segmentMatcher = Pattern.compile(regex).matcher(inboundUrl);
            if (segmentMatcher.find())
            {
               for (int j = 0; j < segment.numParameters(); j++)
               {
                  String value = segmentMatcher.group(j + 1);
                  PathParameter param = segment.getParameter(j).copy();
                  param.setValue(value);
                  result.add(param);
               }

               int regionEnd = segmentMatcher.end();

               inboundUrl = inboundUrl.substring(regionEnd - 1);
            }
            else
            {
               throw new PrettyException("Error parsing url: <" + url
                        + ">, a parameter did not match compiled segment in pattern: " + originalPattern);
            }
         }
      }
      else
      {
         throw new IllegalArgumentException("The given URL: " + url + ", cannot be parsed by the pattern: "
                  + originalPattern);
      }
      return result;
   }

   /**
    * URL encoding/decoding is not a concern of this method.
    *
    * @param params Array of Object parameters, in order, to be substituted for mapping pattern values or el
    *           expressions. This method will call the toString() method on each object provided.
    *           <p>
    *           If only one param is specified and it is an instance of List, the list items will be used as parameters
    *           instead. An empty list or a single null parameter are both treated as if no parameters were specified.
    *           </p>
    *           E.g: getMappedUrl(12,55,"foo","bar") for a pattern of /#{el.one}/#{el.two}/#{el.three}/#{el.four}/ will
    *           return the String: "/12/55/foo/bar/"
    * @return A URL based on this object's urlPatten, with values substituted for el expressions in the order provided
    */
   public URL getMappedURL(final Object... params)
   {
      URL result = null;
      if (params != null)
      {
         Object[] parameters = params;

         /*
          * Check to see if our parameters were provided as a single List
          */
         if ((params.length == 1) && (params[0] != null) && (params[0] instanceof List<?>))
         {
            List<?> list = (List<?>) params[0];
            if (list.size() == 0)
            {
               parameters = new Object[0];
            }
            else
            {
               parameters = list.toArray(params);
            }
         }
         else if ((params.length == 1) && (params[0] == null))
         {
            parameters = new Object[0];
         }

         /*
          * Assert that we have the proper number of parameters.
          */
         if (getParameterCount() != parameters.length)
         {
            throw new PrettyException("Invalid number of path parameters supplied for pattern: " + originalPattern
                     + ", expected <" + getParameterCount() + ">, but got <" + parameters.length + ">");
         }

         /*
          * Build the result URL
          */
         int paramIndex = 0;
         List<String> resultSegments = new ArrayList<String>();
         for (Segment segment : pathSegments)
         {
            String template = segment.getTemplate();
            Matcher parameterMatcher = Segment.getTemplateMatcher(template);

            StringBuffer sb = new StringBuffer();
            while (parameterMatcher.find())
            {
               /*
                * We need to escape $ and \ because they have a special meaning when
                * used in Matcher#appendReplacement(). From the docs:
                *
                * Note that backslashes (\) and dollar signs ($) in the replacement string
                * may cause the results to be different than if it were being treated as a
                * literal replacement string. Dollar signs may be treated as references to
                * captured subsequences as described above, and backslashes are used to
                * escape literal characters in the replacement string.
                */
               String replacement = parameters[paramIndex].toString()
                        .replace("$", "\\$")
                        .replace("\\", "\\\\");

               parameterMatcher.appendReplacement(sb, replacement);
               paramIndex++;
            }
            parameterMatcher.appendTail(sb);
            resultSegments.add(sb.toString());
         }
         result = new URL(resultSegments, urlPattern.getMetadata());
      }
      else if (getParameterCount() > 0)
      {
         throw new PrettyException("Invalid number of parameters supplied: " + originalPattern + ", expected <"
                  + getParameterCount() + ">, got <0>");
      }

      return result;
   }

   /**
    * Get the number of URL parameters that this parser expects to find in any given input string
    *
    * @return Number of parameters
    */
   public int getParameterCount()
   {
      return pathParameters.size();
   }

   public Object getPattern()
   {
      return originalPattern;
   }

   /**
    * @return whether the URL pattern is an expression language
    */
   public boolean isElPattern()
   {
      return elPattern;
   }

}
TOP

Related Classes of com.ocpsoft.pretty.faces.url.URLPatternParser

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.