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