001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.juneau.rest.util;
018
019import static org.apache.juneau.collections.JsonMap.*;
020import static org.apache.juneau.common.utils.Utils.*;
021
022import java.util.*;
023
024import org.apache.juneau.common.utils.*;
025import org.apache.juneau.internal.*;
026
027/**
028 * Represents a URL path pattern match.
029 *
030 * For example, given the pattern <js>"/foo/{bar}/*"</js> and the path <js>"/foo/123/baz/qux"</js>, this match gives
031 * you a map containing <js>"{bar:123}"</js> and a remainder string containing <js>"baz/qux"</js>.
032 *
033 * <h5 class='section'>See Also:</h5><ul>
034 * </ul>
035 */
036public class UrlPathMatch {
037
038   private final int matchedParts;
039   private final String path;
040   private final Map<String,String> vars;
041
042   /**
043    * Constructor.
044    *
045    * @param path The path being matched against.  Can be <jk>null</jk>.
046    * @param matchedParts The number of parts that were matched against the path.
047    * @param keys The variable keys.  Can be <jk>null</jk>.
048    * @param values The variable values.  Can be <jk>null</jk>.
049    */
050   protected UrlPathMatch(String path, int matchedParts, String[] keys, String[] values) {
051      this.path = path;
052      this.matchedParts = matchedParts;
053      this.vars = keys == null ? Collections.emptyMap() : new SimpleMap<>(keys, values);
054   }
055
056   /**
057    * Returns a map of the path variables and values.
058    *
059    * @return
060    *    An unmodifiable map of variable keys/values.
061    *    <br>Returns an empty map if no variables were found in the path.
062    */
063   public Map<String,String> getVars() {
064      return vars;
065   }
066
067   /**
068    * Returns <jk>true</jk> if this match contains one or more variables.
069    *
070    * @return <jk>true</jk> if this match contains one or more variables.
071    */
072   public boolean hasVars() {
073      return ! vars.isEmpty();
074   }
075
076   /**
077    * Returns <jk>true</jk> if any of the variable values are blank.
078    *
079    * @return <jk>true</jk> if any of the variable values are blank.
080    */
081   public boolean hasEmptyVars() {
082      for (String v : vars.values())
083         if (Utils.isEmpty(v))
084            return true;
085      return false;
086   }
087
088   /**
089    * Returns the remainder of the path after the pattern match has been made.
090    *
091    * <p>
092    * Same as {#link {@link #getSuffix()} but trims the leading slash if there is one.
093    *
094    * @return The remainder of the path after the pattern match has been made.
095    */
096   public String getRemainder() {
097      String suffix = getSuffix();
098      if (isNotEmpty(suffix) && suffix.charAt(0) == '/')
099         suffix = suffix.substring(1);
100      return suffix;
101   }
102
103   /**
104    * Returns the remainder of the URL after the pattern was matched.
105    *
106    * @return
107    * The remainder of the URL after the pattern was matched.
108    * <br>Can be <jk>null</jk> if nothing remains to be matched.
109    * <br>Otherwise, always starts with <js>'/'</js>.
110    */
111   public String getSuffix() {
112      String s = path;
113      for (int j = 0; j < matchedParts; j++) {
114         int k = s.indexOf('/', 1);
115         if (k == -1)
116            return null;
117         s = s.substring(k);
118      }
119      return s;
120   }
121
122   /**
123    * Returns the part of the URL that the pattern matched against.
124    *
125    * @return
126    * The part of the URL that the pattern matched against.
127    * <br>Can be <jk>null</jk> if nothing matched.
128    * <br>Otherwise, always starts with <js>'/'</js>.
129    */
130   public String getPrefix() {
131      int c = 0;
132      for (int j = 0; j < matchedParts; j++) {
133         c = path.indexOf('/', c+1);
134         if (c == -1)
135            c = path.length();
136      }
137      return Utils.nullIfEmpty3(path.substring(0, c));
138   }
139
140   @Override /* Object */
141   public String toString() {
142      return filteredMap()
143         .append("v", getVars())
144         .append("r", getRemainder())
145         .asString();
146   }
147}