1 /*******************************************************************************
\r
2 * ============LICENSE_START====================================================
\r
4 * * ===========================================================================
\r
5 * * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
\r
6 * * ===========================================================================
\r
7 * * Licensed under the Apache License, Version 2.0 (the "License");
\r
8 * * you may not use this file except in compliance with the License.
\r
9 * * You may obtain a copy of the License at
\r
11 * * http://www.apache.org/licenses/LICENSE-2.0
\r
13 * * Unless required by applicable law or agreed to in writing, software
\r
14 * * distributed under the License is distributed on an "AS IS" BASIS,
\r
15 * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
\r
16 * * See the License for the specific language governing permissions and
\r
17 * * limitations under the License.
\r
18 * * ============LICENSE_END====================================================
\r
20 * * ECOMP is a trademark and service mark of AT&T Intellectual Property.
\r
22 ******************************************************************************/
\r
23 package com.att.cssa.rserv;
\r
25 import java.util.HashMap;
\r
26 import java.util.Map;
\r
27 import java.util.Set;
\r
30 * This path matching algorithm avoids using split strings during the critical transactional run-time. By pre-analyzing the
\r
31 * content at "set Param" time, and storing data in an array-index model which presumably is done once and at the beginning,
\r
32 * we can match in much less time when it actually counts.
\r
36 public class Match {
\r
37 private Map<String, Integer> params;
\r
38 private byte[] values[];
\r
39 private Integer vars[];
\r
40 private boolean wildcard;
\r
44 * These two methods are pairs of searching performance for variables Spark Style.
\r
45 * setParams evaluates the target path, and sets a HashMap that will return an Integer.
\r
46 * the Keys are both :key and key so that there will be no string operations during
\r
49 * For the Integer, if the High Order is 0, then it is just one value. If High Order >0, then it is
\r
50 * a multi-field option, i.e. ending with a wild-card.
\r
52 public Match(String path) {
\r
53 // IF DEBUG: System.out.print("\n[" + path + "]");
\r
54 params = new HashMap<String,Integer>();
\r
56 String[] pa = path.split("/");
\r
57 values = new byte[pa.length][];
\r
58 vars = new Integer[pa.length];
\r
62 for(int i=0;i<pa.length && !wildcard;++i) {
\r
63 if(pa[i].startsWith(":")) {
\r
64 if(pa[i].endsWith("*")) {
\r
65 val = i | pa.length<<16; // load end value in high order bits
\r
66 key = pa[i].substring(0, pa[i].length()-1);// remove *
\r
72 params.put(key,val); //put in :key
\r
73 params.put(key.substring(1,key.length()), val); // put in just key, better than adding a missing one, like Spark
\r
74 // values[i]=null; // null stands for Variable
\r
77 values[i]=pa[i].getBytes();
\r
78 if(pa[i].endsWith("*")) {
\r
80 if(pa[i].length()>1) {
\r
81 /* remove * from value */
\r
82 int newlength = values[i].length-1;
\r
83 byte[] real = new byte[newlength];
\r
84 System.arraycopy(values[i],0,real,0,newlength);
\r
87 vars[i]=0; // this is actually a variable, if it only contains a "*"
\r
97 * This is the second of the param evaluation functions. First, we look up to see if there is
\r
98 * any reference by key in the params Map created by the above.
\r
100 * The resulting Integer, if not null, is split high/low order into start and end.
\r
101 * We evaluate the string for '/', rather than splitting into String[] to avoid the time/mem needed
\r
102 * We traverse to the proper field number for slash, evaluate the end (whether wild card or no),
\r
103 * and return the substring.
\r
105 * The result is something less than .003 milliseconds per evaluation
\r
108 public String param(String path,String key) {
\r
109 Integer val = params.get(key); // :key or key
\r
111 int start = val & 0xFFFF;
\r
112 int end = (val >> 16) & 0xFFFF;
\r
115 for(i=0;i<start;++i) {
\r
116 idx = path.indexOf('/',idx+1);
\r
122 end = path.indexOf('/',idx);
\r
123 if(end<0)end=path.length();
\r
127 return path.substring(idx,end);
\r
128 } else if(i==start-1) { // if last spot was left blank, i.e. :key*
\r
135 public boolean match(String path) {
\r
136 if(path==null|| path.length()==0 || "/".equals(path) ) {
\r
137 if(values==null)return true;
\r
138 switch(values.length) {
\r
139 case 0: return true;
\r
140 case 1: return values[0].length==0;
\r
141 default: return false;
\r
145 byte[] pabytes = path.getBytes();
\r
149 int lastField = values.length;
\r
150 int lastByte = pabytes.length;
\r
151 boolean fieldMatched = false; // = lastByte>0?(pabytes[0]=='/'):false;
\r
152 // IF DEBUG: System.out.println("\n -- " + path + " --");
\r
153 for(int i=0;rv && i<lastByte;++i) {
\r
154 if(field>=lastField) { // checking here allows there to be a non-functional ending /
\r
158 if(values[field]==null) { // it's a variable, just look for /s
\r
159 if(wildcard && field==lastField-1) return true;// we've made it this far. We accept all remaining characters
\r
160 Integer val = vars[field];
\r
161 int start = val & 0xFFFF;
\r
162 int end = (val >> 16) & 0xFFFF;
\r
163 if(end==0)end=start+1;
\r
165 for(int j=start; j<end && k<lastByte; ++k) {
\r
166 // IF DEBUG: System.out.print((char)pabytes[k]);
\r
167 if(pabytes[k]=='/') {
\r
173 if(k==lastByte && pabytes[k-1]!='/')++field;
\r
174 if(k>i)i=k-1; // if we've incremented, have to accommodate the outer for loop incrementing as well
\r
175 fieldMatched = false; // reset
\r
178 // IF DEBUG: System.out.print((char)pabytes[i]);
\r
179 if(pabytes[i]=='/') { // end of field, eval if Field is matched
\r
180 // if double slash, check if supposed to be empty
\r
181 if(fieldIdx==0 && values[field].length==0) {
\r
182 fieldMatched = true;
\r
184 rv = fieldMatched && ++field<lastField;
\r
186 fieldMatched = false;
\r
188 } else if(values[field].length==0) {
\r
189 // double slash in path, but content in field. We check specially here to avoid
\r
190 // Array out of bounds issues.
\r
194 rv =false; // field is already matched, now there's too many bytes
\r
196 rv = pabytes[i]==values[field][fieldIdx++]; // compare expected (pabytes[i]) with value for particular field
\r
197 fieldMatched=values[field].length==fieldIdx; // are all the bytes match in the field?
\r
198 if(fieldMatched && (i==lastByte-1 || (wildcard && field==lastField-1)))
\r
199 return true; // last field info
\r
204 if(field!=lastField || pabytes.length!=lastByte) rv = false; // have we matched all the fields and all the bytes?
\r
208 public Set<String> getParamNames() {
\r
209 return params.keySet();
\r