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