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