001/**
002 * Copyright 2004-2014 The Kuali Foundation
003 *
004 * Licensed under the Educational Community License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.opensource.org/licenses/ecl2.php
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016package org.kuali.student.contract.model.util;
017
018import org.kuali.student.contract.exception.DictionaryExecutionException;
019import org.kuali.student.contract.model.*;
020
021import java.util.ArrayList;
022import java.util.LinkedHashSet;
023import java.util.List;
024import java.util.Set;
025
026/**
027 * Utility that implements searches of the spreadsheet model that are needed
028 * TODO: refactor all the *Writer to use this instead of their own finds.
029 * @author nwright
030 */
031public class ModelFinder {
032
033    private SearchModel searchModel;
034    private ServiceContractModel serviceContractModel;
035    private DictionaryModel dictionaryModel;
036
037    public ModelFinder(DictionaryModel model) {
038        this.dictionaryModel = model;
039        this.searchModel = model;
040        this.serviceContractModel = model;
041    }
042
043    public ModelFinder(SearchModel model) {
044        this.searchModel = model;
045    }
046
047    public ModelFinder(ServiceContractModel model) {
048        this.serviceContractModel = model;
049    }
050
051    public XmlType findXmlType(String xmlTypeName) {
052        for (XmlType xmlType : serviceContractModel.getXmlTypes()) {
053            if (xmlTypeName.equalsIgnoreCase(xmlType.getName())) {
054                return xmlType;
055            }
056        }
057        return null;
058    }
059
060    public List<State> findStates(String xmlObject) {
061        List<State> list = new ArrayList();
062        for (State state : dictionaryModel.getStates()) {
063            if (state.getXmlObject().equalsIgnoreCase(xmlObject)) {
064                list.add(state);
065            }
066        }
067        return list;
068    }
069
070    public List<Type> findTypes(String xmlObject) {
071        List<Type> list = new ArrayList();
072        for (Type type : dictionaryModel.getTypes()) {
073            if (type.getXmlObject().equalsIgnoreCase(xmlObject)) {
074                list.add(type);
075            }
076        }
077        return list;
078    }
079
080    public Type findType(String xmlObject, String typeName) {
081        if (typeName.equalsIgnoreCase(State.DEFAULT)) {
082            return this.getDefaultType();
083        }
084        for (Type type : dictionaryModel.getTypes()) {
085            if (type.getXmlObject().equalsIgnoreCase(xmlObject)) {
086                if (type.getName().equalsIgnoreCase(typeName)) {
087                    return type;
088                }
089            }
090        }
091        return null;
092    }
093
094    public Type findType(String typeKey) {
095        for (Type type : dictionaryModel.getTypes()) {
096            if (type.getTypeKey().equalsIgnoreCase(typeKey)) {
097                return type;
098            }
099        }
100        return null;
101    }
102
103    public State findState(String xmlObject, String stateName) {
104        if (stateName.equalsIgnoreCase(State.DEFAULT)) {
105            return this.getDefaultState();
106        }
107        for (State state : dictionaryModel.getStates()) {
108            if (state.getXmlObject().equalsIgnoreCase(xmlObject)) {
109                if (state.getName().equalsIgnoreCase(stateName)) {
110                    return state;
111                }
112            }
113        }
114        return null;
115    }
116
117    public State findState(String stateKey) {
118        for (State state : dictionaryModel.getStates()) {
119            if (state.getStateKey().equalsIgnoreCase(stateKey)) {
120                return state;
121            }
122        }
123        return null;
124    }
125
126    public Dictionary findRoot(Dictionary dict) {
127        if (dict.getParent() == null) {
128            return dict;
129        }
130        return findRoot(dict.getParent());
131    }
132
133    /**
134     * get dictionary entry for the id
135     * @return
136     */
137    public Dictionary findDictionaryEntry(String dictId) {
138        for (Dictionary d : dictionaryModel.getDictionary()) {
139            if (d.getId().equalsIgnoreCase(dictId)) {
140                return d;
141            }
142        }
143        return null;
144    }
145
146    /**
147     * get dictionary entries for the state overries
148     * @return
149     */
150    public List<Dictionary> findChildDictionaryEntries(Dictionary parent) {
151        List<Dictionary> list = new ArrayList();
152        for (Dictionary d : dictionaryModel.getDictionary()) {
153            if (d.getParent() == null) {
154                continue;
155            }
156            if (d.getParent().equals(parent)) {
157                list.add(d);
158            }
159        }
160        return list;
161    }
162
163    /**
164     * get dictionary entries for the state overries
165     * @return
166     */
167    public List<Dictionary> findDefaultDictionary() {
168        List<Dictionary> list = new ArrayList(dictionaryModel.getDictionary().size());
169        for (Dictionary d : dictionaryModel.getDictionary()) {
170            if (d.getState().equalsIgnoreCase(State.DEFAULT)) {
171                list.add(d);
172            }
173        }
174        return list;
175    }
176
177    public List<Dictionary> findDictionaryEntriees(String xmlTypeName) {
178        List<Dictionary> list = new ArrayList();
179        for (Dictionary dict : dictionaryModel.getDictionary()) {
180            if (dict.getXmlObject().equalsIgnoreCase(xmlTypeName)) {
181                list.add(dict);
182            }
183        }
184        return list;
185    }
186
187    public List<Dictionary> findDefaultDictionaryEntriees(String xmlTypeName) {
188        List<Dictionary> list = new ArrayList();
189        for (Dictionary dict : this.findDefaultDictionary()) {
190            if (dict.getXmlObject().equalsIgnoreCase(xmlTypeName)) {
191                list.add(dict);
192            }
193        }
194        return list;
195    }
196
197    /**
198     * get dictionary entries for the state overries
199     * @return
200     */
201    public List<Dictionary> findStateOverrideDictionary() {
202        List<Dictionary> list = new ArrayList(dictionaryModel.getDictionary().size());
203        for (Dictionary d : dictionaryModel.getDictionary()) {
204            if (!d.getState().equalsIgnoreCase(State.DEFAULT)) {
205                list.add(d);
206            }
207        }
208        return list;
209    }
210
211    /**
212     * Expands a type that has a status of Type.GROUPING.
213     * A group can contain another group
214     * @param state
215     * @return
216     */
217    public Set<Type> expandType(Type type) {
218        Set<Type> types = new LinkedHashSet();
219        if (!type.getStatus().equalsIgnoreCase(Type.GROUPING)) {
220            types.add(type);
221            return types;
222        }
223        String pattern = type.getTypeKey();
224        GroupTypeStatePatternMatcher matcher =
225                new GroupTypeStatePatternMatcher(pattern);
226        for (Type t : dictionaryModel.getTypes()) {
227            // can't match yourself
228            if (t == type) {
229                //System.out.println ("skipping itself " + type.getName ());
230                continue;
231            }
232            if (!t.getInclude()) {
233                continue;
234            }
235
236            // must match the same type of object
237            if (type.getXmlObject().equalsIgnoreCase(t.getXmlObject())) {
238                if (matcher.matches(t.getTypeKey())) {
239                    if (t.getStatus().equalsIgnoreCase(Type.GROUPING)) {
240                        //TODO: Worry about self-recursion
241                        types.addAll(expandType(t));
242                    } else {
243                        types.add(t);
244                    }
245                }
246            }
247        }
248        return types;
249    }
250
251    /**
252     * Expands a type that has a status of Type.GROUPING.
253     * A group can contain another group
254     * @param state
255     * @return
256     */
257    public Set<State> expandState(State state) {
258        Set<State> states = new LinkedHashSet();
259        if (!state.getStatus().equalsIgnoreCase(State.GROUPING)) {
260            states.add(state);
261            return states;
262        }
263        String pattern = state.getStateKey();
264        GroupTypeStatePatternMatcher matcher =
265                new GroupTypeStatePatternMatcher(pattern);
266        for (State s : dictionaryModel.getStates()) {
267            // can't match yourself
268            if (s == state) {
269                //System.out.println ("skipping itself " + state.getName ());
270                continue;
271            }
272            if (!s.getInclude()) {
273                continue;
274            }
275
276            // must match the same type of object
277            if (state.getXmlObject().equalsIgnoreCase(s.getXmlObject())) {
278                if (matcher.matches(s.getStateKey())) {
279                    if (s.getStatus().equalsIgnoreCase(Type.GROUPING)) {
280                        //TODO: Worry about self-recursion
281                        states.addAll(expandState(s));
282                    } else {
283                        states.add(s);
284                    }
285                }
286            }
287        }
288        return states;
289    }
290
291    public Constraint findConstraint(String id) {
292        for (Constraint cons : dictionaryModel.getConstraints()) {
293            if (cons.getId().equalsIgnoreCase(id)) {
294                return cons;
295            }
296        }
297        return null;
298    }
299
300    private static String stripListFromType(String type) {
301        if (type.endsWith("List")) {
302            return type.substring(0, type.length() - "List".length());
303        }
304        return type;
305    }
306
307    private void loadAllComplexSubTypes(Set<XmlType> types, String xmlTypeName) {
308        for (MessageStructure ms : this.findMessageStructures(xmlTypeName)) {
309            XmlType type = this.findXmlType(stripListFromType(ms.getType()));
310            if (type != null) {
311                if (type.getPrimitive().equalsIgnoreCase(XmlType.COMPLEX)) {
312                    if (types.add(type)) {
313//                        System.out.println("debug: " + xmlTypeName + "." + ms.getShortName() + " " + type.getName());
314                        loadAllComplexSubTypes(types, type.getName());
315                    }
316                }
317            }
318        }
319    }
320
321    public Set<XmlType> findAllComplexSubTypes(String xmlTypeName) {
322        Set<XmlType> types = new LinkedHashSet();
323        this.loadAllComplexSubTypes(types, xmlTypeName);
324        return types;
325    }
326
327    public Set<XmlType> findAllComplexTypesInService(String service) {
328        Set<XmlType> allTypes = findMainXmlTypesInService(service);
329        for (XmlType type : findMainXmlTypesInService(service)) {
330            this.loadAllComplexSubTypes(allTypes, type.getName());
331        }
332        return allTypes;
333    }
334
335    public Set<XmlType> findMainXmlTypesInService(String service) {
336        Set<XmlType> types = new LinkedHashSet();
337        for (ServiceMethod method : this.findServiceMethods(service)) {
338            XmlType type = this.findXmlType(stripListFromType(method.getReturnValue().getType()));
339            if (type != null) {
340                if (type.getPrimitive().equalsIgnoreCase(XmlType.COMPLEX)) {
341                    types.add(type);
342                }
343            }
344
345            for (ServiceMethodParameter param : method.getParameters()) {
346                type = this.findXmlType(stripListFromType(param.getType()));
347                if (type != null) {
348                    if (type.getPrimitive().equalsIgnoreCase(XmlType.COMPLEX)) {
349                        types.add(type);
350                    }
351                }
352                break;
353            }
354        }
355        return types;
356    }
357
358    public Field findField(String id) {
359        for (Field field : dictionaryModel.getFields()) {
360            if (field.getId().equalsIgnoreCase(id)) {
361                return field;
362            }
363        }
364        return null;
365    }
366
367    public Field findField(String xmlTypeName, String shortName) {
368        return findField(xmlTypeName + "." + shortName);
369    }
370
371    public Field findField(Dictionary dict) {
372        if (dict.isDynamic()) {
373            return findField(dict.getXmlObject(), "attributes");
374        }
375        return findField(dict.getXmlObject(), dict.getShortName());
376    }
377
378    public List<Field> findFields(String xmlTypeName) {
379        List<Field> list = new ArrayList();
380        for (Field field : dictionaryModel.getFields()) {
381            if (field.getXmlObject().equalsIgnoreCase(xmlTypeName)) {
382                list.add(field);
383            }
384        }
385        return list;
386    }
387
388    public Service findService(String key) {
389        for (Service serv : serviceContractModel.getServices()) {
390            if (serv.getKey().equalsIgnoreCase(key)) {
391                return serv;
392            }
393        }
394        return null;
395    }
396
397    public Project findProject(String key) {
398        for (Project proj : dictionaryModel.getProjects()) {
399            if (proj.getKey().equalsIgnoreCase(key)) {
400                return proj;
401            }
402        }
403        return null;
404    }
405
406    public SearchType findSearchType(String key) {
407        for (SearchType st : searchModel.getSearchTypes()) {
408            if (st.getKey().equalsIgnoreCase(key)) {
409                return st;
410            }
411        }
412        return null;
413    }
414
415    public List<ServiceMethod> findServiceMethods(String service) {
416        List<ServiceMethod> methods = new ArrayList<ServiceMethod>();
417        for (ServiceMethod method : serviceContractModel.getServiceMethods()) {
418            if (method.getService().equalsIgnoreCase(service)) {
419                methods.add(method);
420            }
421        }
422        return methods;
423    }
424
425    public ServiceMethod findServiceMethod(String service, String name) {
426        for (ServiceMethod method : serviceContractModel.getServiceMethods()) {
427            if (method.getService().equalsIgnoreCase(service)) {
428                if (method.getName().equalsIgnoreCase(name)) {
429                    return method;
430                }
431            }
432        }
433        return null;
434    }
435
436    public List<ServiceMethod> getServiceMethodsInService(String service) {
437        List<ServiceMethod> list = new ArrayList();
438        for (ServiceMethod method : serviceContractModel.getServiceMethods()) {
439            if (method.getService().equalsIgnoreCase(service)) {
440                list.add(method);
441            }
442        }
443        return list;
444    }
445
446    public List<MessageStructure> findMessageStructures(String xmlType) {
447        List<MessageStructure> list = new ArrayList();
448        for (MessageStructure ms : serviceContractModel.getMessageStructures()) {
449            if (ms.getXmlObject().equalsIgnoreCase(xmlType)) {
450                list.add(ms);
451            }
452        }
453        return list;
454    }
455
456    public MessageStructure findMessageStructure(String xmlType, String shortName) {
457        for (MessageStructure ms : serviceContractModel.getMessageStructures()) {
458            if (ms.getXmlObject().equalsIgnoreCase(xmlType)) {
459                if (ms.getShortName().equalsIgnoreCase(shortName)) {
460                    return ms;
461                }
462            }
463        }
464        return null;
465    }
466    private Type defaultType = null;
467
468    public Type getDefaultType() {
469        if (defaultType != null) {
470            return defaultType;
471        }
472        List<Type> list = findTypes(Type.DEFAULT);
473        if (list.size() == 0) {
474            throw new DictionaryExecutionException("No default Type found");
475        }
476        if (list.size() > 1) {
477            throw new DictionaryExecutionException("More than one default Type found");
478        }
479        defaultType = list.get(0);
480        return defaultType;
481    }
482    private State defaultState = null;
483
484    public State getDefaultState() {
485        if (defaultState != null) {
486            return defaultState;
487        }
488        List<State> list = findStates(State.DEFAULT);
489        if (list.size() == 0) {
490            throw new DictionaryExecutionException("No default State found");
491        }
492        if (list.size() > 1) {
493            throw new DictionaryExecutionException("More than one default State found");
494        }
495        defaultState = list.get(0);
496        return defaultState;
497    }
498}