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