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.impl;
017
018import java.io.File;
019import java.util.ArrayList;
020import java.util.Collection;
021import java.util.Date;
022import java.util.HashMap;
023import java.util.HashSet;
024import java.util.List;
025import java.util.Map;
026import java.util.Set;
027
028import org.junit.After;
029import org.junit.AfterClass;
030import org.junit.Before;
031import org.junit.BeforeClass;
032import org.junit.Test;
033
034import static org.junit.Assert.*;
035
036import org.junit.Ignore;
037import org.kuali.student.contract.model.MessageStructure;
038import org.kuali.student.contract.model.Service;
039import org.kuali.student.contract.model.ServiceContractModel;
040import org.kuali.student.contract.model.ServiceMethod;
041import org.kuali.student.contract.model.ServiceMethodParameter;
042import org.kuali.student.contract.model.XmlType;
043import org.kuali.student.contract.model.util.HtmlContractMessageStructureWriter;
044import org.kuali.student.contract.model.util.ModelFinder;
045import org.kuali.student.contract.model.validation.ServiceContractModelValidator;
046import org.kuali.student.validation.decorator.mojo.ValidationDecoratorWriterForOneService;
047import org.slf4j.Logger;
048import org.slf4j.LoggerFactory;
049
050/**
051 *
052 * @author nwright
053 */
054@Ignore
055public class R1R2ServiceContractComparisonTest {
056    
057    private static Logger log = LoggerFactory.getLogger(R1R2ServiceContractComparisonTest.class);
058    
059
060    public R1R2ServiceContractComparisonTest() {
061    }
062
063    @BeforeClass
064    public static void setUpClass() throws Exception {
065    }
066
067    @AfterClass
068    public static void tearDownClass() throws Exception {
069    }
070
071    @Before
072    public void setUp() {
073
074        log.info("This section was created by programmatically comparing the message structures.");
075        log.info("Run on: " + new Date());
076        log.info("See [R1R2ServiceContractComparisonTest.java|https://test.kuali.org/svn/student/tools/maven-kscontractdoc-plugin/trunk/src/test/java/org/kuali/student/contract/model/impl/R1R2ServiceContractComparisonTest.java]");
077        log.info("");
078        log.info("*TABLE OF CONTENTS*");
079        log.info("{toc}");
080        log.info("");
081        log.info("h1. Loading models of the contracts from the source code");
082        log.info("h2. Log from loading model for R1");
083        getModel1();
084        log.info("h2. Log from loading model for R2");
085        getModel2();
086        getFinder1();
087        getFinder2();
088        loadKnownObjectRenames();
089        loadKnownUnconvertedObjects();
090        loadKnownFieldRenames();
091        loadKnownFieldIssues();
092        loadKnownMethodRenames();
093        loadKnownMethodIssues ();
094    }
095
096    @After
097    public void tearDown() {
098    }
099    private static final String RESOURCES_DIRECTORY = "src/test/resources";
100    private static final String TEST_SOURCE_DIRECTORY =
101            "src/test/java/org/kuali/student/contract/model/test/source";
102    private static final String ENROLL_PROJECT_SRC_MAIN = "C:/svn/ks-1.3-services/ks-enroll/ks-enroll-api/src/main";
103    private static final String ENROLL_PROJECT_JAVA_DIRECTORY = ENROLL_PROJECT_SRC_MAIN + "/java";
104    private static final String RICE_CORE_API_DIRECTORY = "C:/svn/rice/trunk/core/api/src/main/java";
105    private static final String RICE_KIM_API_DIRECTORY = "C:/svn/rice/trunk/kim/kim-api/src/main/java";
106    private static final String RICE_LOCATION_API_DIRECTORY = "C:/svn/rice/trunk/location/api/src/main/java";
107    private static final String RICE_KEW_API_DIRECTORY = "C:/svn/rice/trunk/kew/api/src/main/java";
108    private static final String RICE_KEN_API_DIRECTORY = "C:/svn/rice/trunk/ken/api/src/main/java";
109    private static final String RICE_KSB_API_DIRECTORY = "C:/svn/rice/trunk/ksb/api/src/main/java";
110    private static final String RICE_KRMS_API_DIRECTORY = "C:/svn/rice/trunk/krms/api/src/main/java";
111    private static final String R1_PROJECT_DIRECTORY = "C:/svn/student/";
112    private static final String CORE_API_DIRECTORY = R1_PROJECT_DIRECTORY + "ks-core/ks-core-api/src/main/java";
113    private static final String COMMON_API_DIRECTORY = R1_PROJECT_DIRECTORY + "ks-common/ks-common-api/src/main/java";
114    private static final String LUM_API_DIRECTORY = R1_PROJECT_DIRECTORY + "ks-lum/ks-lum-api/src/main/java";
115    private static ServiceContractModel model1 = null;
116    private static ServiceContractModel model2 = null;
117
118    /**
119     * Test of getServiceMethods method, of class ServiceContractModelQDoxLoader.
120     */
121    @Test
122    public void testCompareModels() {
123        log.info("");
124        log.info("h1. Message Structure Comparison");
125        compareTypes();
126        log.info("");
127        log.info("h1. Service Method Comparison");
128        compareMethods();
129    }
130
131    private ServiceContractModel getModel1() {
132        if (model1 != null) {
133            return model1;
134        }
135        List<String> srcDirs = new ArrayList();
136        log.info("User directory=" + System.getProperty("user.dir"));
137        log.info("Current directory=" + new File(".").getAbsolutePath());
138        srcDirs.add(COMMON_API_DIRECTORY);
139        srcDirs.add(CORE_API_DIRECTORY);
140        srcDirs.add(LUM_API_DIRECTORY);
141        log.info ("Reading as input:");
142        for (String directory : srcDirs) {
143            log.info ("* " + directory);
144        }
145        log.info ("");
146        ServiceContractModel instance = new ServiceContractModelQDoxLoader(srcDirs);
147
148        instance = new ServiceContractModelCache(instance);
149        validate(instance);
150        model1 = instance;
151        return instance;
152    }
153
154    private ServiceContractModel getModel2() {
155        if (model2 != null) {
156            return model2;
157        }
158        List<String> srcDirs = new ArrayList();
159        log.info("User directory=" + System.getProperty("user.dir"));
160        log.info("Current directory=" + new File(".").getAbsolutePath());
161        srcDirs.add(ENROLL_PROJECT_JAVA_DIRECTORY);
162        log.info ("Reading as input:");
163        for (String directory : srcDirs) {
164            log.info ("* " + directory);
165        }
166        log.info ("");     
167        ServiceContractModel instance = new ServiceContractModelQDoxLoader(srcDirs);
168
169        instance = new ServiceContractModelCache(instance);
170        validate(instance);
171        model2 = instance;
172        return instance;
173    }
174
175    private String dump(ServiceMethod method) {
176        StringBuilder bldr = new StringBuilder();
177        bldr.append(method.getName());
178        String comma = "";
179        bldr.append("(");
180        for (ServiceMethodParameter param : method.getParameters()) {
181            bldr.append(comma);
182            comma = ", ";
183            bldr.append(param.getType());
184            bldr.append(" ");
185            bldr.append(param.getName());
186        }
187        bldr.append(")");
188        return bldr.toString();
189    }
190
191    private void validate(ServiceContractModel model) {
192        Collection<String> errors =
193                new ServiceContractModelValidator(model).validate();
194        if (errors.size() > 0) {
195            StringBuilder buf = new StringBuilder();
196            buf.append(errors.size()).append(" errors found while validating the data.");
197            int cnt = 0;
198            for (String msg : errors) {
199                cnt++;
200                buf.append("\n");
201                buf.append("*error*").append(cnt).append(":").append(msg);
202            }
203
204            fail(buf.toString());
205        }
206    }
207    private ModelFinder finder1 = null;
208
209    private ModelFinder getFinder1() {
210        if (finder1 == null) {
211            finder1 = new ModelFinder(getModel1());
212        }
213        return finder1;
214    }
215    private ModelFinder finder2 = null;
216
217    private ModelFinder getFinder2() {
218        if (finder2 == null) {
219            finder2 = new ModelFinder(getModel2());
220        }
221        return finder2;
222    }
223
224    private void compareTypes() {
225        for (Service service : model1.getServices()) {
226            log.info("");
227            log.info("h2. " + service.getName() + " Structures");
228            for (XmlType type : finder1.findAllComplexTypesInService(service.getKey())) {
229                findCompareType(type);
230            }
231        }
232    }
233
234    private String calcService(XmlType xmlType) {
235        StringBuilder bldr = new StringBuilder();
236        String comma = "";
237        for (String serviceKey : HtmlContractMessageStructureWriter.calcUsageByService(model1, xmlType)) {
238            bldr.append(comma);
239            comma = ", ";
240            bldr.append(serviceKey);
241        }
242        return bldr.toString();
243    }
244
245    private String calcFieldNames(XmlType xmlType) {
246        StringBuilder bldr = new StringBuilder();
247        String comma = "";
248        for (MessageStructure ms : finder2.findMessageStructures(xmlType.getName())) {
249            bldr.append(comma);
250            comma = ", ";
251            bldr.append(ms.getShortName());
252        }
253        return bldr.toString();
254    }
255    private Map<String, String> knownUnconvertedObjects = null;
256
257    private void loadKnownUnconvertedObjects() {
258        Map<String, String> missings = new HashMap<String, String>();
259        missings.put("ObjectStructureDefinition", "Old R1 dictionary not converted");
260        missings.put("FieldDefinition", "Old R1 dictionary not converted");
261        missings.put("ValidCharsConstraint", "Old R1 dictionary not converted");
262        missings.put("RequiredConstraint", "Old R1 dictionary not converted");
263        missings.put("CaseConstraint", "Old R1 dictionary not converted");
264        missings.put("WhenConstraint", "Old R1 dictionary not converted");
265        missings.put("Constraint", "Old R1 dictionary not converted");
266        missings.put("MustOccurConstraint", "Old R1 dictionary not converted");
267        missings.put("LookupConstraint", "Old R1 dictionary not converted");
268        missings.put("CommonLookupParam", "Old R1 dictionary not converted");
269        missings.put("CommonLookup", "Old R1 dictionary not converted");
270        missings.put("DateRangeInfo", "DateRange was merged in with Milestone");
271        missings.put("CredentialInfo", "LRC was revamped and Class II like objects were dropped");
272        missings.put("CreditInfo", "LRC was revamped and Class II like objects were dropped");
273        missings.put("ScaleInfo", "Changed to be ResultScaleInfo");
274        missings.put("GradeInfo", "LRC was revamped and Class II like objects were dropped");
275        missings.put("ResultComponentInfo", "Changed to be ResultValuesGroupInfo");
276        missings.put("QueryParamInfo", "Is really a type object that holds typing info information about a parameter model as TypeInfo and use type-type relation to connnect it to search criteria");
277        missings.put("FieldDescriptor", "Old pre-R1 dictionary structure that were attached to search param types were dropped -- ui dictionary provided that info");
278        missings.put("SearchSelector", "Old pre-R1 dictionary structure that were attached to search param types were dropped -- ui dictionary provided that info");
279        missings.put("ObjectStructure", "Old pre-R1 dictionary structure that were attached to search param types were dropped -- ui dictionary provided that info");
280        missings.put("Type", "Old pre-R1 dictionary structure that were attached to search param types were dropped -- ui dictionary provided that info");
281        missings.put("State", "Old pre-R1 dictionary structure that were attached to search param types were dropped -- ui dictionary provided that info");
282        missings.put("Field", "Old pre-R1 dictionary structure that were attached to search param types were dropped -- ui dictionary provided that info");
283        missings.put("ConstraintDescriptor", "Old pre-R1 dictionary structure that were attached to search param types were dropped -- ui dictionary provided that info");
284        missings.put("ConstraintSelector", "Old pre-R1 dictionary structure that were attached to search param types were dropped -- ui dictionary provided that info");
285        missings.put("RequireConstraint", "Old pre-R1 dictionary structure that were attached to search param types were dropped -- ui dictionary provided that info");
286        missings.put("TypeStateCaseConstraint", "Old pre-R1 dictionary structure that were attached to search param types were dropped -- ui dictionary provided that info");
287        missings.put("TypeStateWhenConstraint", "Old pre-R1 dictionary structure that were attached to search param types were dropped -- ui dictionary provided that info");
288        missings.put("OccursConstraint", " Old pre-R1 dictionary structure that were attached to search param types were dropped -- ui dictionary provided that info");
289        missings.put("ResultColumnInfo", " is really a type that describes the type of result that comes back, store as a TypeInfo object and use type-type relation to connect to result");
290//        missings.put("SearchCriteriaTypeInfo", "The search criteria is really a type stucture that should be modeled as as TypeInfo and type-type relationship to connect it to a search type");
291        missings.put("java.lang.String", "");
292        missings.put("Map<String, String>", "");
293        missings.put("LuiInfo", "Lui was pulled out and put in it's own service.  The LuiInfo object was not used in R1 and was radically redesigned in R2");
294
295        knownUnconvertedObjects = missings;
296        return;
297    }
298    private Map<String, String> knownObjectRenames = null;
299
300    private void loadKnownObjectRenames() {
301        Map<String, String> renames = new HashMap<String, String>();
302        renames.put("Message", "MessageInfo");
303        renames.put("SearchRequest", "SearchRequestInfo");
304        renames.put("SearchResult", "SearchResultInfo");
305        renames.put("SearchParam", "SearchParamInfo");
306        renames.put("SearchResultRow", "SearchResultRowInfo");
307        renames.put("SearchResultCell", "SearchResultCellInfo");
308        renames.put("Message", "MessageInfo");
309        knownObjectRenames = renames;
310        return;
311    }
312    private Map<String, String> knownFieldRenames = null;
313
314    private void loadKnownFieldRenames() {
315        Map<String, String> renames = new HashMap<String, String>();
316        renames.put("id", "key"); // not all the time but when it happens want to catch if id not found
317        renames.put("desc", "descr");
318        renames.put("state", "stateKey");
319        renames.put("type", "typeKey");
320        renames.put("metaInfo", "meta");
321        renames.put("desc", "descr");
322        renames.put("startTerm", "startTermId");
323        renames.put("endTerm", "endTermId");
324        renames.put("longDesc", "longDescr");
325        renames.put("shortDesc", "shortDescr");
326        renames.put("objectTypeURI", "refObjectUri");
327        // TODO: this works but really should make these specific to the object they are connected with
328        renames.put("detailDesc", "descr");
329        renames.put("milestoneDate", "startDate");
330        renames.put("success", "isSuccess");
331        renames.put("relationType", "relationTypeKey");
332        renames.put("unitType", "unitTypeKey");
333        renames.put("enrollable", "isEnrollable");
334        renames.put("hazardousForDisabledStudents", "isHazardousForDisabledStudents");
335        renames.put("versionInfo", "version");
336        renames.put("primary", "isPrimary");
337        renames.put("activityType", "typeKey");
338        renames.put("loRepository", "loRepositoryKey");
339        renames.put("queryParamValueList", "queryParamValues");
340        renames.put("credentialProgramType", "typeKey");
341        knownFieldRenames = renames;
342        return;
343    }
344    private Map<String, String> knownFieldIssues = null;
345
346    private void loadKnownFieldIssues() {
347        Map<String, String> issues = new HashMap<String, String>();
348        issues.put("AtpInfo.key", "Switched from key to Id");
349        issues.put("MilestoneInfo.key", "Switched from key to Id");
350        issues.put("AtpInfo.id", ""); // suppress the extra field message from the r2 side
351        issues.put("MilestoneInfo.id", ""); // ditto
352        issues.put("MilestoneInfo.atpId", "Is not in R2 because a Milestone can be connected to more than one ATP so it is managed through a relationship");
353        issues.put("Message.locale", "the type was changed from String to LocaleInfo to hold the different parts of the locale info");
354        issues.put("SearchRequest.params", "");
355        issues.put("SearchResult.rows", "");
356        issues.put("SearchResultRow.cells", "");
357        issues.put("ValidationResultInfo.errorLevel", "");
358        issues.put("ValidationResultInfo.level", "");
359        issues.put("ValidationResultInfo.ok", "");
360        issues.put("ValidationResultInfo.warn", "");
361        issues.put("ValidationResultInfo.error", "");
362        issues.put("DocumentInfo.documentBinaryInfo", "renamd to just documentBinary (removing the trailing Info from the field name)");
363        issues.put("OrgHierarchyInfo.key", "Switched from key to Id");
364        issues.put("SearchResultTypeInfo.resultColumns", "ResultColumns is really anotther type to describe the column, Use type-type relation to hold that info");
365        issues.put("ReqCompFieldTypeInfo.fieldDescriptor", "was dropped because it was an Old Pre-R1 dictionary and was not used -- UI dictionary provides that info instead");
366        issues.put("LuTypeInfo.instructionalFormat", "Instructional format is a TypeInfo object and should be modeled as such using the type-type relation to connect it to a learning unit type");
367        issues.put("LuTypeInfo.deliveryMethod", "Delivery method is a TypeInfo object and should be modeled as such using type-type relation to connect it to a learning unit type");
368        issues.put("SearchCriteriaTypeInfo.queryParams", "Query Params is a TypeInfo that describes the parameter, model as type and type-type relation");
369        issues.put("OrgOrgRelationTypeInfo.orgHierarchyKey", "This was removed because a particular relation type can participate in more than one hierarchies!");
370        issues.put("SearchParam.value", "Renamed to values which is List<String>, in R1 the setValue method was overloaded to take a string or List, Kept in R2 but marked as deprecated");
371        issues.put("", "");
372        issues.put("", "");
373        issues.put("", "");
374        issues.put("", "");
375        issues.put("", "");
376        issues.put("", "");
377        issues.put("", "");
378        issues.put("", "");
379        issues.put("", "");
380        issues.put("", "");
381
382        knownFieldIssues = issues;
383        return;
384    }
385
386    private XmlType findType(XmlType r1) {
387        XmlType r2 = finder2.findXmlType(r1.getName());
388        if (r2 == null) {
389            String renamedName = this.knownObjectRenames.get(r1.getName());
390            if (renamedName != null) {
391                r2 = finder2.findXmlType(renamedName);
392                if (r2 == null) {
393                    log.info("# (-) " + r1.getName() + ": was not found even after being renamed to " + renamedName);
394                    return null;
395                }
396                log.info("# (/) " + r1.getName() + ": was renamed to " + renamedName);
397                return r2;
398            }
399        }
400        if (r2 == null) {
401            if (r1.getName().endsWith("TypeInfo")) {
402                r2 = finder2.findXmlType("TypeInfo");
403            }
404        }
405        return r2;
406    }
407
408    private void findCompareType(XmlType r1) {
409        if (r1.getName().endsWith("List")) {
410            return;
411        }
412        if (this.knownUnconvertedObjects.containsKey(r1.getName())) {
413            String message = this.knownUnconvertedObjects.get(r1.getName());
414            if (message.isEmpty()) {
415                return;
416            }
417            log.info("# (/) " + r1.getName() + ":" + message);
418            return;
419        }
420        XmlType r2 = findType(r1);
421        if (r2 == null) {
422            log.info("# " + r1.getName() + ": has no corresponding object in r2");
423            return;
424        }
425        Set<MessageStructure> usedInR2 = new HashSet<MessageStructure>();
426        for (MessageStructure ms : finder1.findMessageStructures(r1.getName())) {
427            MessageStructure used = findCompareMessageStructure(ms, r2);
428            if (used != null) {
429                usedInR2.add(used);
430            }
431        }
432        // Don't report extra fields on type info
433        if (!r2.getName().equals("TypeInfo")) {
434            for (MessageStructure ms : finder2.findMessageStructures(r2.getName())) {
435                if (usedInR2.contains(ms)) {
436                    continue;
437                }
438                String issue = this.knownFieldIssues.get(ms.getXmlObject() + "." + ms.getShortName());
439                if (issue != null) {
440                    if (!issue.isEmpty()) {
441                        log.info("# (*g) " + ms.getXmlObject() + "." + ms.getShortName() + ": " + issue);
442                    }
443                    continue;
444                }
445                log.info("# (+) " + ms.getXmlObject() + "." + ms.getShortName() + " - new field added in R2");
446            }
447        }
448    }
449
450    private MessageStructure findCompareMessageStructure(MessageStructure r1, XmlType xmlType2) {
451        MessageStructure r2 = findMessageStructure(r1, xmlType2);
452        String issue = this.knownFieldIssues.get(r1.getXmlObject() + "." + r1.getShortName());
453        if (issue != null) {
454            if (!issue.isEmpty()) {
455                log.info("# (*g) " + r1.getXmlObject() + "." + r1.getShortName() + ": " + issue);
456            }
457            return r2;
458        }
459        if (r2 == null) {
460            if (xmlType2.getName().equals("TypeInfo")) {
461                if (r1.getShortName().endsWith("Type")
462                        || r1.getShortName().endsWith("TypeInfo")
463                        || r1.getShortName().endsWith("Types")
464                        || r1.getShortName().endsWith("TypeInfos")) {
465                    log.info("# (*g) " + r1.getXmlObject() + "." + r1.getShortName() + " was a type stored on a type: use type-type relationship instead");
466                    return null;
467                }
468                log.info("# (!) " + r1.getXmlObject() + "." + r1.getShortName() + " was extra data on type, store in dynamic attribute if actually used");
469                return null;
470            }
471            log.info("# (-) " + r1.getXmlObject() + "." + r1.getShortName() + " not found in r2: renamed to one of these? " + calcFieldNames(xmlType2));
472            return null;
473        }
474        compareType(r1, r2);
475        return r2;
476    }
477
478    private void compareType(MessageStructure r1, MessageStructure r2) {
479        if (r1.getType().equalsIgnoreCase(r2.getType())) {
480            return;
481        }
482        if (r1.getShortName().equals("attributes")) {
483            if (r1.getType().equals("Map<String, String>")) {
484                if (r2.getType().equals("AttributeInfoList")) {
485                    return;
486                }
487            }
488        }
489        if (r1.getShortName().equals("desc") || r1.getShortName().equals("descr")) {
490            if (r1.getType().equals("String")) {
491                if (r2.getType().equals("RichTextInfo")) {
492                    log.info("# (*g) " + r1.getXmlObject() + "." + r1.getShortName() + ": description type were changed to RichText, use plain version");
493                    return;
494                }
495            }
496        }
497        log.info("# (!) " + r1.getXmlObject() + "." + r1.getShortName() + ": the type was changed from " + r1.getType() + " to " + r2.getType());
498    }
499
500    private MessageStructure findMessageStructure(MessageStructure r1, XmlType xmlType2) {
501        MessageStructure r2 = finder2.findMessageStructure(xmlType2.getName(), r1.getShortName());
502        if (r2 == null) {
503            String renamed = this.knownFieldRenames.get(r1.getShortName());
504            if (renamed != null) {
505                r2 = finder2.findMessageStructure(xmlType2.getName(), renamed);
506                if (r2 == null) {
507                    log.info("# (-) " + r1.getXmlObject() + "." + r1.getShortName()
508                            + " was renamed to " + xmlType2.getName() + "." + renamed
509                            + " BUT IT STILL DIDN'T EXIST IN R2");
510                    return null;
511                }
512                log.info("# (*g) " + r1.getXmlObject() + "." + r1.getShortName()
513                        + " was renamed to " + xmlType2.getName() + "." + renamed);
514            }
515        }
516        return r2;
517    }
518
519    private void compareMethods() {
520        for (Service service : model1.getServices()) {
521            log.info("");
522            log.info("h2. " + service.getName() + " Methods");
523            List<ServiceMethod> methodsInService = finder1.findServiceMethods(service.getKey());
524            for (ServiceMethod method : methodsInService) {
525                findCompareMethod(method);
526            }
527        }
528    }
529
530    private void findCompareMethod(ServiceMethod method1) {
531        String issue = knownMethodIssues.get (method1.getService() + "Service." + method1.getName());
532        if (issue != null) {
533            if (!issue.isEmpty()) {
534                log.info("# (*g) " + method1.getService() + "Service." + method1.getName()
535                        + ": " + issue);
536            }
537            return;
538        }
539        ServiceMethod method2 = findMethod(method1);
540        if (method2 == null) {
541//            String possibleMethods = calcPossibleMethods(method1);
542            if (isTypeMethod(method1)) {
543                log.info("# (*g) " + method1.getService() + "Service." + method1.getName()
544                        + " was dropped because it is a type, use TypeService instead");
545                return;
546            }
547            String possibleMethods = this.calcPossibleMethods(method1);
548            if (possibleMethods.isEmpty()) {
549                log.info("# (-) " + method1.getService() + "Service." + method1.getName()
550                        + " could not be found in R2");
551            } else {
552                log.info("# (!) " + method1.getService() + "Service." + method1.getName()
553                        + " might have been renamed to one of these: "
554                        + possibleMethods);
555            }
556            return;
557        }
558        if (!method1.getName().equals(method2.getName())) {
559            log.info("# (*g) " + method1.getService() + "Service." + method1.getName()
560                    + " was renamed to " + method2.getService() + "Service." + method2.getName());
561        }
562    }
563
564    private ServiceMethod findMethod(ServiceMethod method1) {
565        ServiceMethod method2 = findMethod2(method1.getService(), method1.getName());
566        if (method2 == null) {
567            String methodRename = knownMethodRenames.get(method1.getService() + "Service." + method1.getName());
568            if (methodRename != null) {
569                method2 = findMethod2(method1.getService(), methodRename);
570                if (method2 == null) {
571                    log.info("# (x) " + method1.getService() + "Service." + method1.getName() 
572                            + " could not be found even after being renamed to " + methodRename);
573                    return null;
574                }
575            }
576        }
577        return method2;
578    }
579    private Map<String, String> knownMethodRenames = null;
580
581    private void loadKnownMethodRenames() {
582        Map<String, String> renames = new HashMap<String, String>();
583        renames.put("AtpService.getAtpsByAtpType", "getAtpIdsByType");
584        renames.put("AtpService.getMilestonesByAtp", "getMilestonesForAtp");
585        renames.put("AtpService.addMilestone", "addMilestoneToAtp");
586        renames.put("AtpService.removeMilestone", "removeMilestoneFromAtp");
587        renames.put("MessageService.getMessageGroups", "getMessageGroupKeys");
588        renames.put("CommentService.getComments", "getCommentsByReferenceAndType");
589        renames.put("CommentService.getTags", "getTagsByReferenceAndType"); 
590        renames.put("CommentService.addTag", "createTag");
591        renames.put("CommentService.addComment", "createComment");
592        renames.put("CommentService.removeComment", "deleteComment");
593        renames.put("CommentService.removeTag", "deleteTag");
594        renames.put("CommentService.removeComments", "deleteCommentsByReference");
595        renames.put("CommentService.removeTags", "deleteTagsByReference");
596        renames.put("DocumentService.getDocumentsByIdList", "getDocumentsByIds");
597        renames.put("DocumentService.getCategoriesByDocument", "getDocumentCategoriesByDocumentId");
598        renames.put("DocumentService.getRefDocRelationsByDoc", "getRefDocRelationsByDocument");
599        renames.put("EnumerationManagementService.removeEnumeratedValue", "deleteEnumeratedValue");
600        renames.put("OrganizationService.getOrganization", "getOrg");
601        renames.put("OrganizationService.getOrganizationsByIdList", "getOrgsByIds");
602        renames.put("OrganizationService.getOrgOrgRelationsByIdList", "getOrgOrgRelationsByIds");
603        renames.put("OrganizationService.getOrgPersonRelationsByIdList", "getOrgPersonRelationsByIds");
604        renames.put("OrganizationService.getPersonIdsForOrgByRelationType", "");
605        renames.put("OrganizationService.getAllOrgPersonRelationsByPerson", "getOrgPersonRelationsByPerson");
606        renames.put("OrganizationService.getAllOrgPersonRelationsByOrg", "getOrgPersonRelationsByOrg");
607        renames.put("OrganizationService.createOrganization", "createOrg");
608        renames.put("OrganizationService.updateOrganization", "updateOrg");
609        renames.put("OrganizationService.deleteOrganization", "deleteOrg");
610        renames.put("OrganizationService.validateOrganization", "validateOrg");
611        renames.put("OrganizationService.removeOrgOrgRelation", "deleteOrgOrgRelation");
612        renames.put("OrganizationService.removeOrgPersonRelation", "deleteOrgPersonRelation");
613        renames.put("OrganizationService.addPositionRestrictionToOrg", "createOrgPositionRestriction");
614        renames.put("OrganizationService.updatePositionRestrictionForOrg", "updateOrgPositionRestriction");
615        renames.put("OrganizationService.removePositionRestrictionFromOrg", "deleteOrgPositionRestriction");
616        renames.put("StatementService.getStatementsUsingReqComponent", "getStatementsByReqComponent");
617        renames.put("StatementService.getStatementsUsingStatement", "getStatementsForStatement");
618        renames.put("CourseService.getCourseFormats", "getCourseFormatsByCourse");
619        renames.put("CourseService.getCourseActivities", "getCourseActivitiesByCourseFormat");
620        renames.put("CourseService.getCourseLos", "getCourseLearningObjectivesByCourse");
621        renames.put("LearningObjectiveService.getLoCategories", "getLoCategoriesByLoRepository");
622        renames.put("LearningObjectiveService.getLoByIdList", "getLosByIds");
623        renames.put("LearningObjectiveService.getLosByRepository", "getLosByLoRepository");
624        renames.put("LearningObjectiveService.getLoCategoriesForLo", "getLoCategoriesByLo");
625        renames.put("LrcService.getResultComponent", "getResultValuesGroup");
626        renames.put("LuService.getClusByIdList", "getClusByIds");
627        renames.put("LuService.getAllowedLuLuRelationTypesByCluId", "getAllowedCluCluRelationTypesByClu");
628        renames.put("LuService.getClusByRelation", "getClusByRelatedCluAndRelationType");
629        renames.put("LuService.getCluIdsByRelation", "getCluIdsByRelatedCluAndRelationType");
630        renames.put("LuService.getRelatedClusByCluId", "getRelatedClusByCluAndRelationType");
631        renames.put("LuService.getRelatedCluIdsByCluId", "getRelatedCluIdsByCluAndRelationType");
632        renames.put("LuService.getCluPublicationsByCluId", "getCluPublicationsByClu");
633        renames.put("LuService.getResourceRequirementsForCluId", "getResourceRequirementsForClu");
634        renames.put("LuService.getCluSetInfo", "getCluSet");
635        renames.put("LuService.getCluSetInfoByIdList", "getCluSetsByIds");
636        renames.put("LuService.getLuisByIdList", "getLuisByIds");
637        renames.put("ProgramService.getMajorIdsByCredentialProgramType", "getMajorDisciplineIdsByCredentialProgramType");
638        renames.put("ProgramService.getVariationsByMajorDisciplineId", "getProgramVariationsByMajorDiscipline");
639        renames.put("ProgramService.getHonorsByCredentialProgramType", "getHonorProgramIdsByCredentialProgramType");
640        renames.put("ProposalService.getProposalsByIdList", "getProposalsByIds");
641        renames.put("", "");
642        renames.put("", "");
643        knownMethodRenames = renames;
644        return;
645    }
646
647      private Map<String, String> knownMethodIssues = null;
648
649    private void loadKnownMethodIssues() {
650        Map<String, String> issues = new HashMap<String, String>();
651        issues.put("AtpService.validateDateRange", "Dropped because DateRange objects were merged in with milestones");
652        issues.put("AtpService.getDateRange", "Dropped because DateRange objects were merged in with milestones");
653        issues.put("AtpService.getDateRangesByAtp", "Dropped because DateRange objects were merged in with milestones");
654        issues.put("AtpService.getDateRangesByDate", "Dropped because DateRange objects were merged in with milestones");
655        issues.put("AtpService.addDateRange", "Dropped because DateRange objects were merged in with milestones");
656        issues.put("AtpService.updateDateRange", "Dropped because DateRange objects were merged in with milestones");
657        issues.put("AtpService.removeDateRange", "Dropped because DateRange objects were merged in with milestones");
658        issues.put("DictionaryService.getObjectTypes", "Dictionary service was completely revamped to match KRAD, old one is still around use that for R1 stuff");
659        issues.put("DictionaryService.getObjectStructure", "Dictionary service was completely revamped to match KRAD, old one is still around use that for R1 stuff");
660        issues.put("CommentService.getCommentsByType", "Renamed and changed to just get Ids, so use getCommentIdsByType then call getCommentsByIds");
661        issues.put("CommentService.getTagsByType", "Renamed and changed to just get Ids, so use getTagIdsByType then call getTagsByIds");
662        issues.put("DocumentService.getRefObjectTypes", "(!) has been dropped from the contract, the document service should store any uri");
663        issues.put("DocumentService.getRefObjectSubTypes", "(!) has been dropped from the contract, the document service should store any uri and sub-object URI");
664        issues.put("OrganizationService.getOrgOrgRelationsByRelatedOrg", " (!) the two methods for tranversing by one side of the relationship or other has replaced by a single method that finds relationships no matter which side it is on (?) Need to possibly rethink this it imposes a big change on both the implementation and on the the application. ");        
665        issues.put("OrganizationService.getPersonIdsForOrgByRelationType", "Was removed, instead use getOrgPersonRelationsByTypeAndPerson and loop through the relationships to get the list of personIds that you want.  The issue was the old method did not take into account relationships that are old/inactive so using it would lead to errors that would only appear once transitions occured in the people being related to the org.");
666        issues.put("OrganizationService.getOrgPersonRelationsByPerson", "Renamd to getOrgPersonRelationsByOrgAndPerson, because the R1 was badly named, it said just by person but the parameters required an Org as well!");
667        issues.put("OrganizationService.getPositionRestrictionsByOrg", "use getOrgPositionRestrictionIdsByOrg then call getOrgPositionRestrictionsByIds to get the objects");
668        issues.put("LearningObjectiveService.getAllowedLoLoRelationTypesForLoType", "is a type method, use Type Service instead");
669        issues.put("LrcService.getCredential", "Is a Class 2 concept and as dropped from the Class 1 service");
670        issues.put("LrcService.getCredentialsByKeyList", "Is a Class 2 concept and as dropped from the Class 1 service");
671        issues.put("LrcService.getCredentialKeysByCredentialType", "Is a Class 2 concept and as dropped from the Class 1 service");
672        issues.put("LrcService.getCredit", "Is a Class 2 concept and as dropped from the Class 1 service");
673        issues.put("LrcService.getCreditsByKeyList", "Is a Class 2 concept and as dropped from the Class 1 service");
674        issues.put("LrcService.getCreditKeysByCreditType", "Is a Class 2 concept and as dropped from the Class 1 service");
675        issues.put("LrcService.getGrade", "Is a Class 2 concept and as dropped from the Class 1 service");
676        issues.put("LrcService.getGradesByKeyList", "Is a Class 2 concept and as dropped from the Class 1 service");
677        issues.put("LrcService.getGradeKeysByGradeType", "Is a Class 2 concept and as dropped from the Class 1 service");
678        issues.put("LrcService.getGradesByScale", "Is a Class 2 concept and as dropped from the Class 1 service");
679        issues.put("LrcService.translateGrade", "(-) is not being supported at this time, translations will be added later");
680        issues.put("LrcService.compareGrades", "(-) is not being supported at this time, comparisons will be added later");
681        issues.put("LrcService.getResultComponentIdsByResultComponentType", "roughly maps to getResultValuesGroupIdsByType but they are different objects and the types have changed as well");
682        issues.put("LrcService.getResultComponentIdsByResult", "roughly maps to getResultValuesGroupsByResultValue but doesn't take the extra type parameter");
683        issues.put("LrcService.createResultComponent", "rougly maps to createResultValuesGroup");
684        issues.put("LrcService.updateResultComponent", "rougly maps to updateResultValuesGroup");
685        issues.put("LrcService.deleteResultComponent", "rougly maps to deleteResultValuesGroup");
686        issues.put("LrcService.getScale", "roughly maps to getResultScale");
687        issues.put("LuService.getAllowedLuLuRelationTypesByLuiId", "is a type method, use TypeService instead");
688        issues.put("", "");
689        issues.put("", "");
690        issues.put("", "");
691        issues.put("", "");
692        knownMethodIssues = issues;
693        return;
694    }
695    
696    
697    private ServiceMethod findMethod2(String serviceKey, String methodName) {
698        ServiceMethod method2 = finder2.findServiceMethod(serviceKey, methodName);
699        if (method2 == null) {
700            if (serviceKey.equals("Lu")) {
701                method2 = finder2.findServiceMethod("Clu", methodName);
702                if (method2 == null) {
703                    method2 = finder2.findServiceMethod("Lui", methodName);
704                }
705            }
706        }
707        return method2;
708    }
709
710    private String calcMethods(ServiceMethod method1) {
711        StringBuilder bldr = new StringBuilder();
712        String comma = "";
713        for (ServiceMethod method2 : finder2.findServiceMethods(method1.getService())) {
714            bldr.append(comma);
715            comma = ", ";
716            bldr.append(method2.getName());
717        }
718        return bldr.toString();
719    }
720
721    private String calcPossibleMethods(ServiceMethod method1) {
722        StringBuilder bldr = new StringBuilder();
723        String comma = "";
724        for (ServiceMethod method2 : findPossibleMethods(method1)) {
725            bldr.append(comma);
726            comma = ", ";
727            bldr.append(method2.getName());
728        }
729        return bldr.toString();
730    }
731
732    private List<ServiceMethod> findPossibleMethods(ServiceMethod method1) {
733        List<ServiceMethod> methods = new ArrayList<ServiceMethod>();
734        List<ServiceMethod> wideNet = null;
735        if (method1.getService().equals("Lu")) {
736            wideNet = finder2.findServiceMethods("Clu");
737            wideNet.addAll(finder2.findServiceMethods("Lui"));
738        } else {
739            wideNet = finder2.findServiceMethods(method1.getService());
740        }
741        for (ServiceMethod method2 : wideNet) {
742            if (isPossibleMatch(method1, method2)) {
743                methods.add(method2);
744            }
745        }
746        return methods;
747    }
748
749    private boolean isPossibleMatch(ServiceMethod method1, ServiceMethod method2) {
750         if (method1.getName().contains(method2.getName())) {
751            return true;
752        }
753        if (method2.getName().contains(method1.getName())) {
754            return true;
755        }
756        if (method1.getName().startsWith("get") && method2.getName().startsWith("get")) {
757            return true;
758        }
759        if (method1.getName().startsWith("add") && method2.getName().startsWith("create")) {
760            return true;
761        }
762        if (method1.getName().startsWith("create") && method2.getName().startsWith("create")) {
763            return true;
764        }
765        if (method1.getName().startsWith("update") && method2.getName().startsWith("update")) {
766            return true;
767        }
768        if (method1.getName().startsWith("delete") && method2.getName().startsWith("delete")) {
769            return true;
770        }
771        if (method1.getName().startsWith("remove") && method2.getName().startsWith("delete")) {
772            return true;
773        }
774        if (method1.getName().startsWith("validate") && method2.getName().startsWith("validate")) {
775            return true;
776        }
777        return false;
778    }
779
780    private boolean isTypeMethod(ServiceMethod method1) {
781        if (method1.getReturnValue().getType().endsWith("TypeInfo")) {
782            return true;
783        }
784        if (method1.getReturnValue().getType().endsWith("TypeInfoList")) {
785            return true;
786        }
787        return false;
788    }
789}