Sonar Fixes: Variable name changes
[aaf/authz.git] / auth / auth-core / src / main / java / org / onap / aaf / auth / rserv / TypedCode.java
1 /**
2  * ============LICENSE_START====================================================
3  * org.onap.aaf
4  * ===========================================================================
5  * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved.
6  * ===========================================================================
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  * ============LICENSE_END====================================================
19  *
20  */
21
22 package org.onap.aaf.auth.rserv;
23
24 import java.util.ArrayList;
25 import java.util.HashMap;
26 import java.util.List;
27
28
29
30 import org.onap.aaf.misc.env.Env;
31 import org.onap.aaf.misc.env.TimeTaken;
32 import org.onap.aaf.misc.env.Trans;
33
34
35 /**
36  * TypedCode organizes implementation code based on the Type and Version of code it works with so that it can
37  * be located quickly at runtime based on the "Accept" HTTP Header.
38  *
39  * FYI: For those in the future wondering why I would create a specialized set of "Pair" for the data content:
40  *   1) TypeCode is used in Route, and this code is used for every transaction... it needs to be blazingly fast
41  *   2) The actual number of objects accessed is quite small and built at startup.  Arrays are best
42  *   3) I needed a small, well defined tree where each level is a different Type.  Using a "Pair" Generic definitions,
43  *      I created type-safety at each level, which you can't get from a TreeSet, etc.
44  *   4) Chaining through the Network is simply object dereferencing, which is as fast as Java can go.
45  *   5) The drawback is that in your code is that all the variables are named "x" and "y", which can be a bit hard to
46  *       read both in code, and in the debugger.  However, TypeSafety allows your IDE (Eclipse) to help you make the
47  *      choices.  Also, make sure you have a good "toString()" method on each object so you can see what's happening
48  *      in the IDE Debugger.
49  *
50  * Empirically, this method of obtaining routes proved to be much faster than the HashSet implementations available in otherwise
51  * competent Open Source.
52  *
53  * @author Jonathan
54  *
55  * @param <TRANS>
56  */
57 public class TypedCode<TRANS extends Trans> extends Content<TRANS> {
58         private List<Pair<String, Pair<HttpCode<TRANS,?>,List<Pair<String, Object>>>>> types;
59
60         public TypedCode() {
61             types = new ArrayList<>();
62         }
63
64         /**
65          * Construct Typed Code based on ContentType parameters passed in
66          *
67          * @param code
68          * @param others
69          * @return
70          */
71         public TypedCode<TRANS> add(HttpCode<TRANS,?> code, String ... others) {
72             StringBuilder sb = new StringBuilder();
73             boolean first = true;
74             for (String str : others) {
75                 if (first) {
76                     first = false;
77                 } else {
78                     sb.append(',');
79                 }
80                 sb.append(str);
81             }
82             parse(code, sb.toString());
83
84             return this;
85         }
86
87         @Override
88         protected Pair<String, Pair<HttpCode<TRANS,?>, List<Pair<String, Object>>>> types(HttpCode<TRANS,?> code, String str) {
89             Pair<String, Pair<HttpCode<TRANS,?>,List<Pair<String, Object>>>> type = null;
90             ArrayList<Pair<String, Object>> props = new ArrayList<>();
91             // Want Q percentage is to be first in the array everytime.  If not listed, 1.0 is default
92             props.add(new Pair<String,Object>(Q,1f));
93             Pair<HttpCode<TRANS,?>, List<Pair<String,Object>>> cl = new Pair<HttpCode<TRANS,?>, List<Pair<String,Object>>>(code, props);
94                 type = new Pair<String, Pair<HttpCode<TRANS,?>,List<Pair<String,Object>>>>(str, cl);
95                 types.add(type);
96                 return type;
97         }
98
99         @Override
100         protected boolean props(Pair<String, Pair<HttpCode<TRANS,?>, List<Pair<String, Object>>>> type, String tag, String value) {
101             if (tag.equals(Q)) { // reset the Q value (first in array)
102                 boolean rv = true;
103                 try {
104                     type.y.y.get(0).y=Float.parseFloat(value);
105                     return rv;
106                 } catch (NumberFormatException e) {
107                     rv=false; // Note: this awkward syntax forced by Sonar, which doesn't like doing nothing with Exception
108                               // which is what should happen
109                 }
110             }
111             return type.y.y.add(new Pair<String,Object>(tag,"version".equals(tag)?new Version(value):value));
112         }
113
114         public Pair<String, Pair<HttpCode<TRANS, ?>, List<Pair<String, Object>>>> prep(TRANS trans, String compare){
115             Pair<String, Pair<HttpCode<TRANS,?>, List<Pair<String, Object>>>> c,rv=null;
116             if (types.size()==1 && "".equals((c=types.get(0)).x)) { // if there are no checks for type, skip
117                 rv = c;
118             } else {
119                 if (compare==null || compare.length()==0) {
120                     rv = types.get(0); // first code is used
121                 } else {
122                     Acceptor<TRANS> acc = new Acceptor<TRANS>(types);
123                     boolean accepted;
124                     TimeTaken tt = trans.start(compare, Env.SUB);
125                     try {
126                         accepted = acc.parse(null, compare);
127                     } finally {
128                         tt.done();
129                     }
130                     if (accepted) {
131                         switch(acc.acceptable.size()) {
132                             case 0:
133                                 break;
134                             case 1:
135                                 rv = acc.acceptable.get(0);
136                                 break;
137                             default: // compare Q values to get Best Match
138                                 float bestQ = -1.0f;
139                                 Pair<String, Pair<HttpCode<TRANS,?>, List<Pair<String, Object>>>> bestT = null;
140                                 for (Pair<String, Pair<HttpCode<TRANS,?>, List<Pair<String, Object>>>> type : acc.acceptable) {
141                                     Float f = (Float)type.y.y.get(0).y; // first property is always Q
142                                     if (f>bestQ) {
143                                         bestQ=f;
144                                         bestT = type;
145                                     }
146                                 }
147                                 if (bestT!=null) {
148                                     rv = bestT;
149                                 }
150                         }
151                     } else {
152                         trans.checkpoint("No Match found for Accept");
153                     }
154                 }
155             }
156             return rv;
157         }
158
159         /**
160          * Print on String Builder content related to specific Code
161          *
162          * This is for Reporting and Debugging purposes, so the content is not cached.
163          *
164          * If code is "null", then all content is matched
165          *
166          * @param code
167          * @return
168          */
169         public StringBuilder relatedTo(HttpCode<TRANS, ?> code, StringBuilder sb) {
170             boolean first = true;
171             for (Pair<String, Pair<HttpCode<TRANS, ?>, List<Pair<String, Object>>>> pair : types) {
172                 if (code==null || pair.y.x == code) {
173                     if (first) {
174                         first = false;
175                     } else {
176                         sb.append(',');
177                     }
178                     sb.append(pair.x);
179                     for (Pair<String,Object> prop : pair.y.y) {
180                         // Don't print "Q".  it's there for internal use, but it is only meaningful for "Accepts"
181                         if (!prop.x.equals(Q) || !prop.y.equals(1f) ) {
182                             sb.append(';');
183                             sb.append(prop.x);
184                             sb.append('=');
185                             sb.append(prop.y);
186                         }
187                     }
188                 }
189             }
190             return sb;
191         }
192
193         public List<Pair<String, Object>> getContent(HttpCode<TRANS,?> code) {
194             for (Pair<String, Pair<HttpCode<TRANS, ?>, List<Pair<String, Object>>>> pair : types) {
195                 if (pair.y.x == code) {
196                     return pair.y.y;
197                 }
198             }
199             return null;
200         }
201
202         public String toString() {
203             return relatedTo(null,new StringBuilder()).toString();
204         }
205
206         public void api(RouteReport tr) {
207             // Need to build up a map, because Prop entries can be in several places.
208             HashMap<HttpCode<?,?>,StringBuilder> psb = new HashMap<>();
209             StringBuilder temp;
210             tr.desc = null;
211
212             // Read through Code/TypeCode trees for all accepted Typecodes
213             for (Pair<String, Pair<HttpCode<TRANS, ?>, List<Pair<String, Object>>>> tc : types) {
214                 // If new, then it's new Code set, create prefix content
215                 if ((temp=psb.get(tc.y.x))==null) {
216                     psb.put(tc.y.x,temp=new StringBuilder());
217                     if (tr.desc==null) {
218                         tr.desc = tc.y.x.desc();
219                     }
220                 } else {
221                     temp.append(',');
222                 }
223                 temp.append(tc.x);
224
225                 // add all properties
226                 for (Pair<String, Object> props : tc.y.y) {
227                     temp.append(';');
228                     temp.append(props.x);
229                     temp.append('=');
230                     temp.append(props.y);
231                 }
232             }
233             // Gather all ContentType possibilities for the same code together
234
235             for (StringBuilder sb : psb.values()) {
236                 tr.contextTypes.add(sb.toString());
237             }
238         }
239
240         public String first() {
241             if (types.size()>0) {
242                 return types.get(0).x;
243             }
244             return null;
245         }
246
247     }