001/* 002 * Copyright c 2018 Rusi Popov, MDA Tools.net All rights reserved. 003 * 004 * This program and the accompanying materials are made available under the terms of the 005 * Eclipse Public License v2.0 which accompanies this distribution, and is available at 006 * http://www.eclipse.org/legal/epl-v20.html 007 */ 008package net.mdatools.modelant.core.operation.model.export; 009 010import java.io.PrintWriter; 011import java.io.StringWriter; 012import java.util.ArrayList; 013import java.util.Collection; 014import java.util.Collections; 015import java.util.Comparator; 016import java.util.List; 017import java.util.Map; 018 019import javax.jmi.reflect.RefObject; 020 021import net.mdatools.modelant.core.api.diff.AssociationDifference; 022import net.mdatools.modelant.core.api.diff.Export; 023import net.mdatools.modelant.core.api.diff.InstanceDifference; 024import net.mdatools.modelant.core.api.diff.ModelComparisonResult; 025import net.mdatools.modelant.core.api.diff.ModelDifference; 026import net.mdatools.modelant.core.operation.element.PrintModelElement; 027import net.mdatools.modelant.core.util.Navigator; 028 029/** 030 * Export the results of models comparison in a JSON-like structured text 031 * @author Rusi Popov (popovr@mdatools.net) 032 */ 033public class StructuredTextExport implements Export { 034 035 private static final String INDENT = " "; 036 037 private static final String ATTRIBUTE_QUALIFIED_NAME = "qualifiedName"; 038 039 /** 040 * @see net.mdatools.modelant.core.api.diff.Export#export(net.mdatools.modelant.core.api.diff.ModelComparisonResult) 041 */ 042 public void export(ModelComparisonResult modelDiff) { 043 StringWriter result; 044 PrintWriter out; 045 046 result = new StringWriter(); 047 out = new PrintWriter(result); 048 049 try { 050 out.println("{"); 051 052 out.print(INDENT); 053 out.print("deleted = "); 054 exportModelDiff(out, indent(6), modelDiff.getDeleted()); 055 out.println(","); 056 057 out.print(INDENT); 058 out.print("added = "); 059 exportModelDiff(out, indent(6), modelDiff.getAdded()); 060 out.println(","); 061 062 out.print(INDENT); 063 out.print("changed = "); 064 exportInstances(out, indent(6), modelDiff.getChanged()); 065 out.println(","); 066 067 out.print(INDENT); 068 out.print("exactMatch = "); 069 exportInstances(out, indent(6), modelDiff.getExactlyMatched()); 070 out.println(","); 071 072 out.print(INDENT); 073 out.print("exactMatchSize = "); 074 out.print( modelDiff.getExactlyMatched().size()); 075 076 out.println(); 077 out.println("}"); 078 } finally { 079 out.close(); 080 } 081 System.out.println( result.toString() ); 082 } 083 084 /** 085 * Print the difference assuming it is not on a new line, does end with EOLN 086 * @param out 087 * @param indent 088 * @param diff 089 */ 090 private void export(PrintWriter out, String indent, InstanceDifference diff) { 091 PrintModelElement print; 092 093 print = new PrintModelElement(indent+indent(6)); 094 095 out.print( indent ); 096 out.println( "{" ); 097 098 out.print( indent ); 099 out.print( "xObject = " ); 100 out.print( print.execute( diff.getXObject() ) ); 101 out.println(","); 102 103 out.print( indent ); 104 out.print( "yObject = " ); 105 out.print( print.execute( diff.getYObject() ) ); 106 out.println(","); 107 108 out.print( indent ); 109 out.print( "attributesWithDifferences = " ); 110 out.print( diff.getAttributesWithDifferences()); 111 out.println(","); 112 113 out.print( indent ); 114 out.print( "associationDiffs =" ); 115 export( out, indent+indent(6), diff.getAssociationDiffs()); 116 out.print( "}" ); 117 } 118 119 /** 120 * Export a diff, assuming it starts on a new line, ends on a line, not terminating it with EOLN 121 * @param out 122 * @param indent 123 * @param diff 124 */ 125 private void export(PrintWriter out, String indent, ModelDifference diff) { 126 Map<String, Collection<ModelDifference>> associations; 127 128 out.print( indent ); 129 out.print("{ "); 130 out.print( new PrintModelElement( indent+indent(2) ).execute( diff.getElement() ) ); 131 132 associations = diff.getAssociations(); 133 for (Map.Entry<String, Collection<ModelDifference>> entry : associations.entrySet()) { 134 out.println( "," ); 135 136 out.print( indent+INDENT ); 137 out.print( "in the role of "); 138 out.print( entry.getKey()); 139 out.print( " for " ); 140 exportModelDiff( out, indent+indent(5), entry.getValue() ); 141 } 142 out.print("}"); 143 } 144 145 private void export(PrintWriter out, String indent, List<AssociationDifference> diffs) { 146 PrintModelElement print; 147 148 print = new PrintModelElement(indent+indent(6)); 149 150 boolean first; 151 152 out.print( "{" ); 153 first = true; 154 for (AssociationDifference diff : diffs) { 155 if ( !first) { 156 out.print(","); 157 } 158 first = false; 159 out.println(); 160 161 out.print( indent ); 162 out.print( "{" ); 163 out.print("associationName = "); 164 out.print( diff.getAssociationName() ); 165 out.println( "," ); 166 167 out.print( indent ); 168 out.print("xMinusY = "); 169 out.print( print.execute(diff.getXMinusY())); 170 out.println( "," ); 171 172 out.print( indent ); 173 out.print("yMinusX = "); 174 out.print( print.execute(diff.getYMinusX())); 175 out.print( "}" ); 176 } 177 out.print("}"); 178 } 179 180 /** 181 * Export a list of model differences, assuming it starts on a new line, ends without EOLN 182 * @param out not null 183 * @param indent not null indent of anything printed 184 * @param diffs 185 */ 186 private void exportModelDiff(PrintWriter out, String indent, Collection<ModelDifference> diffs) { 187 boolean first; 188 List<ModelDifference> sorted; 189 190 sorted = new ArrayList<>(diffs); 191 Collections.sort( sorted, new OrderModelDifferences() ); 192 193 out.print( "{" ); 194 first = true; 195 for (ModelDifference diff : sorted) { 196 if ( !first) { 197 out.print(","); 198 } 199 first = false; 200 out.println(); 201 export(out, indent, diff); 202 } 203 out.print("}"); 204 } 205 206 /** 207 * 208 * @param out 209 * @param indent 210 * @param diffs 211 */ 212 private void exportInstances(PrintWriter out, String indent, List<InstanceDifference> diffs) { 213 boolean first; 214 215 Collections.sort( diffs, new OrderInstanceDifferences() ); 216 217 out.println( "{" ); 218 first = true; 219 for (InstanceDifference diff : diffs) { 220 if ( !first) { 221 out.print(","); 222 } 223 first = false; 224 out.println(); 225 export(out, indent, diff); 226 } 227 out.print("}"); 228 } 229 230 private String indent(int steps) { 231 StringBuilder result; 232 233 result = new StringBuilder(128); 234 for (int i = 0; i < steps; i++) { 235 result.append( INDENT ); 236 } 237 return result.toString(); 238 } 239 240 /** 241 * Compare model elements by class and qualified name 242 * NOTE: MDR does not support attributes like name, qualifiedNames in the MOF 1.4's 243 * metaobjects, which changes the way MOF metamodels are ordered, compared 244 * to the UML and other models. 245 * @param element1 not null 246 * @param element2 not null 247 * @return int indicating the order of element1 vs element2 248 */ 249 static int orderModelElements(RefObject element1, RefObject element2) { 250 int result; 251 252 result = Navigator.getMetaClassName(element1) 253 .compareTo( Navigator.getMetaClassName( element2) ); 254 if (result == 0 ) { 255 result = getQualifiedName( element1 ) 256 .compareTo( getQualifiedName( element2 ) ); 257 } 258 return result; 259 } 260 261 /** 262 * @param element not null 263 * @return not null qualified name of the element 264 */ 265 private static String getQualifiedName(RefObject element) { 266 String result; 267 268 try { 269 result = element.refGetValue( ATTRIBUTE_QUALIFIED_NAME ).toString(); 270 } catch (Exception ex) { // comparing MOF models in MDR, which does not provide names of 271 result = ""; 272 } 273 return result; 274 } 275 276 /** 277 * Compare by model differences by element's class name and qualified name 278 */ 279 static class OrderModelDifferences implements Comparator<ModelDifference> { 280 public int compare(ModelDifference o1, ModelDifference o2) { 281 return orderModelElements( o1.getElement(), o2.getElement() ); 282 } 283 } 284 285 /** 286 * Compare by class name and 287 */ 288 static class OrderInstanceDifferences implements Comparator<InstanceDifference> { 289 public int compare(InstanceDifference o1, InstanceDifference o2) { 290 return orderModelElements( o1.getXObject(), o2.getXObject() ); 291 } 292 } 293}