AT&T 2.0.19 Code drop, stage 1
[aaf/authz.git] / misc / rosetta / src / main / java / org / onap / aaf / misc / rosetta / JaxInfo.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.misc.rosetta;
23
24 import java.lang.reflect.Field;
25 import java.lang.reflect.Type;
26 import java.util.ArrayList;
27 import java.util.HashMap;
28 import java.util.List;
29 import java.util.Map;
30
31 import javax.xml.bind.annotation.XmlElement;
32 import javax.xml.bind.annotation.XmlRootElement;
33 import javax.xml.bind.annotation.XmlSchema;
34 import javax.xml.bind.annotation.XmlType;
35 import javax.xml.datatype.XMLGregorianCalendar;
36
37 public class JaxInfo {
38         private static final String DEFAULT = "##default";
39         public static final int DATA = 0;
40         public static final int ARRAY = 1;
41         public static final int OBJECT = 2;
42         
43         public final String name;
44         public final Class<?> clss;
45         public Map<String, JaxInfo> extensions; // Classes, which might be found at runtime, that extend this class.  Lazy Instantiation
46         public final JaxInfo[] members;
47         public final boolean isArray;
48         public final boolean isString;
49         public final boolean required;
50         public final boolean nillable;
51         public String ns;
52         public boolean isObject() {return members!=null;}
53         
54         private JaxInfo(String n, String ns, Class<?> c, JaxInfo[] members, boolean string, boolean array, boolean required, boolean nillable) {
55                 name = n;
56                 this.ns = ns;
57                 clss = c;
58                 this.members = members;
59                 this.isString = string;
60                 isArray = array;
61                 this.required = required;
62                 this.nillable = nillable;
63                 extensions = null;
64         }
65         
66
67         public int getType() {
68                 if(isArray)return ARRAY;
69                 else if(members!=null)return OBJECT;
70                 return DATA;
71         }
72         
73         public JaxInfo getDerived(String derivedName) {
74                 JaxInfo derived;
75                 // Lazy Instantiation
76                 if(extensions == null) {
77                         extensions = new HashMap<String,JaxInfo>();
78                         derived = null;
79                 } else {
80                         derived = extensions.get(derivedName);
81                 }
82                 
83                 if(derived == null) {
84                         //TODO for the moment, Classes are in same package
85                         Package pkg = clss.getPackage();
86                         try {
87                                 Class<?> dc = getClass().getClassLoader().loadClass(pkg.getName()+'.'+Character.toUpperCase(derivedName.charAt(0))+derivedName.substring(1));
88                                 derived = JaxInfo.build(dc, this); // Use this JAXInfo's name so the tags are correct
89                                 extensions.put(derivedName, derived);
90                         } catch (Exception e) {
91                                 e.printStackTrace();
92                         }
93                 }
94                 return derived;
95         }
96
97         public static JaxInfo get(JaxInfo[] fields, String name) {
98                 for(JaxInfo f : fields) {
99                         if(name.equals(f.name)) return f;
100                 }
101                 return null;
102         }
103
104         /**
105          * Build up JAXB Information (recursively)
106          * 
107          * @param cls
108          * @param rootNns
109          * @return
110          * @throws SecurityException
111          * @throws NoSuchFieldException
112          * @throws ClassNotFoundException
113          * @throws ParseException
114          */
115         public static JaxInfo build(Class<?> cls, JaxInfo parent) throws NoSuchFieldException, ClassNotFoundException, ParseException {
116                 return new JaxInfo(parent.name,parent.ns, cls,buildFields(cls,parent.ns),parent.isString, parent.isArray,parent.required,parent.nillable);
117         }
118         /**
119          * Build up JAXB Information (recursively)
120          * 
121          * @param cls
122          * @param rootNns
123          * @return
124          * @throws SecurityException
125          * @throws NoSuchFieldException
126          * @throws ClassNotFoundException
127          * @throws ParseException
128          */
129         public static JaxInfo build(Class<?> cls, String ... rootNns) throws SecurityException, NoSuchFieldException, ClassNotFoundException, ParseException {
130                 String defaultNS;
131                 if(rootNns.length>0 && rootNns[0]!=null) {
132                         defaultNS = rootNns[0];
133                 } else {
134                         Package pkg = cls.getPackage();
135                         XmlSchema xs = pkg.getAnnotation(XmlSchema.class);
136                         defaultNS = xs==null?"":xs.namespace();
137                 }
138                 String name;
139                 if(rootNns.length>1) {
140                         name = rootNns[1];
141                 } else {
142                         XmlRootElement xre = cls.getAnnotation(XmlRootElement.class);
143                         if(xre!=null) {
144                                 name = xre.name();
145                         } else {
146                                 XmlType xt = cls.getAnnotation(XmlType.class);
147                                 if(xt!=null) {
148                                         name=xt.name();
149                                 } else {
150                                         throw new ParseException("Need a JAXB Object with XmlRootElement, or stipulate in parms");
151                                 }
152                         }
153                 }
154                 
155                 return new JaxInfo(name,defaultNS, cls,buildFields(cls,defaultNS),false,false,false,false);
156         }
157         
158         // Build up the name and members of this particular class
159         // This is recursive, if a member is a JAXB Object as well.
160         private static JaxInfo[] buildFields(Class<?> clazz, String defaultNS) throws SecurityException, NoSuchFieldException, ClassNotFoundException {
161                 ArrayList<JaxInfo> fields = null; // allow for lazy instantiation, because many structures won't have XmlType
162                 Class<?> cls = clazz;
163                 // Build up Method names from JAXB Annotations
164                 XmlType xt;
165                 while((xt = cls.getAnnotation(XmlType.class))!=null) {
166                         if(fields==null)fields = new ArrayList<JaxInfo>();
167                         for(String field : xt.propOrder()) {
168                                 if("".equals(field)) break; // odd bug.  "" returned when no fields exist, rather than empty array
169                                 Field rf = cls.getDeclaredField(field);
170                                 Class<?> ft = rf.getType();
171                                 
172                                 boolean required = false;
173                                 boolean nillable = false;
174                                 String xmlName = field;
175                                 String namespace = defaultNS;
176                                 
177                                 XmlElement xe = rf.getAnnotation(XmlElement.class);
178                                 if(xe!=null) {
179                                         xmlName=xe.name();
180                                         required = xe.required();
181                                         nillable = false;
182                                         if(DEFAULT.equals(xmlName)) {
183                                                 xmlName = field;
184                                         }
185                                         namespace = xe.namespace();
186                                         if(DEFAULT.equals(namespace)) {
187                                                 namespace = defaultNS;
188                                         }
189                                 }
190                                 // If object is a List, then it is possible multiple, per XML/JAXB evaluation
191                                 if(ft.isAssignableFrom(List.class)) {
192                                         Type t = rf.getGenericType();
193                                         String classname = t.toString();
194                                         int start = classname.indexOf('<');
195                                         int end = classname.indexOf('>');
196                                         Class<?> genClass = Class.forName(classname.substring(start+1, end));
197                                         xe = genClass.getAnnotation(XmlElement.class);
198                                         if(xe!=null && !DEFAULT.equals(xe.namespace())) {
199                                                 namespace = xe.namespace();
200                                         }
201                                         // add recursed recursed member, marked as array
202                                         fields.add(new JaxInfo(xmlName,namespace,genClass,buildFields(genClass,namespace), genClass.equals(String.class),true,required,nillable));
203                                 } else {
204                                         boolean isString = ft.equals(String.class) || ft.equals(XMLGregorianCalendar.class);
205                                         // add recursed member
206                                         fields.add(new JaxInfo(xmlName,namespace,ft,buildFields(ft,namespace),isString,false,required,nillable));
207                                 }
208                         }
209                         cls = cls.getSuperclass();
210                 };
211                 if(fields!=null) {
212                         JaxInfo[] rv = new JaxInfo[fields.size()];
213                         fields.toArray(rv);
214                         return rv;
215                 } else {
216                         return null;
217                 }
218         }
219
220
221         public StringBuilder dump(StringBuilder sb, int idx) {
222                 for(int i=0;i<idx;++i)sb.append(' ');
223                 sb.append("Field ");
224                 sb.append(name);
225                 sb.append(" [");
226                 sb.append(clss.getName());
227                 sb.append("] ");
228                 if(isArray)sb.append(" (array)");
229                 if(required)sb.append(" (required)");
230                 if(nillable)sb.append(" (nillable)");
231                 if(members!=null) {
232                         for(JaxInfo f : members) {
233                                 sb.append('\n');
234                                 f.dump(sb,idx+2);
235                         }
236                 }
237                 return sb;
238         }
239
240         public String toString() {
241                 StringBuilder sb = new StringBuilder();
242                 sb.append("Structure of ");
243                 sb.append(clss.getName());
244                 sb.append('\n');
245                 dump(sb,2);
246                 return sb.toString();
247         }
248 }