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.element; 009 010import java.util.ArrayList; 011import java.util.Collection; 012import java.util.HashSet; 013import java.util.Iterator; 014import java.util.List; 015import java.util.Set; 016 017import javax.jmi.model.ModelElement; 018import javax.jmi.model.MofClass; 019import javax.jmi.reflect.JmiException; 020import javax.jmi.reflect.RefAssociation; 021import javax.jmi.reflect.RefAssociationLink; 022import javax.jmi.reflect.RefBaseObject; 023import javax.jmi.reflect.RefClass; 024import javax.jmi.reflect.RefEnum; 025import javax.jmi.reflect.RefFeatured; 026import javax.jmi.reflect.RefPackage; 027import javax.jmi.reflect.RefStruct; 028 029import net.mdatools.modelant.core.api.Function; 030 031/** 032 * Print the actual attributes and values of model elements using only the 033 * reflection interfaces of JMI - it is independent of the actual metamodel being processed. Its 034 * toString() method handles the proper result formatting. 035 * <pre> 036 * Usage: 037 * 038 * System.err.println( "model element ="+ new ModelElementPrinter().execute( modelElement ) ); 039 * </pre> 040 * @author Rusi Popov (popovr@mdatools.net) 041 */ 042public class PrintModelElement implements Function<Object, String>{ 043 /** 044 * The wrapped model element 045 */ 046 private final String prefix; 047 048 public PrintModelElement() { 049 this(""); 050 } 051 052 /** 053 * The only constructor of ModelElementPrinter is to wrap up a non-null model element to print. 054 * @param prefix to all lines 055 */ 056 public PrintModelElement(String prefix) { 057 if ( prefix != null ) { 058 this.prefix = prefix; 059 } else { 060 this.prefix = ""; 061 } 062 } 063 064 /** 065 * Prints the elements nested according to the prefix 066 * @param object the model element to print 067 * @return the string representation of this, with the prefix added to each line 068 */ 069 public String execute(Object object) throws RuntimeException, IllegalArgumentException { 070 StringBuilder result = new StringBuilder( 2048 ); 071 072 toString( object, result, prefix, new HashSet() ); 073 074 return result.toString(); 075 } 076 077 /** 078 * @param prefix 079 * @see java.lang.Object#toString() 080 */ 081 private void toString(Object wrapped, StringBuilder result, String prefix, Set visited) { 082 if ( !visited.contains( wrapped ) ) { 083 // Note that only the model elements are checked if they were visited 084 085 if ( wrapped instanceof JmiException ) { 086 append( result, prefix, (JmiException) wrapped, visited ); 087 088 } else if ( wrapped instanceof RefClass ) { 089 visited.add( wrapped ); 090 append( result, (RefClass) wrapped, visited ); 091 092 } else if ( wrapped instanceof RefPackage ) { 093 visited.add( wrapped ); 094 append( result, (RefPackage) wrapped, visited ); 095 096 } else if ( wrapped instanceof RefAssociation ) { 097 visited.add( wrapped ); 098 append( result, prefix, (RefAssociation) wrapped, visited ); 099 100 } else if ( wrapped instanceof RefFeatured ) { 101 visited.add( wrapped ); 102 append( result, prefix, (RefFeatured) wrapped, visited ); 103 104 } else if ( wrapped instanceof RefStruct ) { 105 visited.add( wrapped ); 106 append( result, prefix, (RefStruct) wrapped, visited ); 107 108 } else if ( wrapped instanceof RefAssociationLink ) { 109 visited.add( wrapped ); 110 append( result, prefix, (RefAssociationLink) wrapped, visited ); 111 112 } else if ( wrapped instanceof RefEnum ) { 113 visited.add( wrapped ); 114 append( result, (RefEnum) wrapped, visited ); 115 116 } else if ( wrapped instanceof Collection ) { 117 append( result, prefix, (Collection) wrapped, visited ); 118 119// } else if ( wrapped != null ) { 120// result.append( wrapped.getClass() ).append( " " ); 121// result.append( wrapped ); 122// 123 } else { 124 result.append( wrapped ); 125 } 126 } else { 127 result.append("<visited>"); 128 } 129 } 130 131 /** 132 * @param result 133 * @param exception 134 */ 135 private void append(StringBuilder result, String prefix, JmiException exception, Set visited) { 136 result.append( exception.getClass() ); 137 result.append( " {\n" ); 138 result.append( prefix ); 139 result.append( " message = " ); 140 result.append( exception.getMessage() ); 141 result.append( "\n" ); 142 143 result.append( prefix ); 144 result.append( " elementInError = " ); 145 toString(exception.getElementInError(), result, prefix+" ", visited); 146 result.append( "\n" ); 147 148 result.append( prefix ); 149 result.append( " objectInError = " ); 150 toString( exception.getObjectInError(), result, prefix+" ", visited ); 151 result.append( "\n" ); 152 153 result.append( prefix ); 154 result.append( "}" ); 155 } 156 157 158 /** 159 * This method requires an M1 object object and a StringBuilder where to describe the object. 160 * 161 * @param result is the buffer where to add the obect's description 162 * @param object is the M1 object to investigate 163 */ 164 private void append(StringBuilder result, String prefix, RefFeatured object, Set visited) { 165 MofClass metaObject; 166 List allMetaObjects; 167 ModelElement contents; 168 Object value; 169 Iterator contentsIterator; 170 Iterator metaObjectsIterator; 171 String prefixNested; 172 173 metaObject = (MofClass) object.refMetaObject(); 174 prefixNested = prefix+" "; 175 176 result.append( metaObject.getName() ) 177 .append( " {\n" ); 178 179 // find the description of the class of this object and their superclasses 180 // Each element of allMetaObjects is an M2 object of MOF classes that describes 181 // the object's class or its superclass 182 183 allMetaObjects = new ArrayList(); 184 185 allMetaObjects.add( metaObject ); 186 allMetaObjects.addAll( metaObject.allSupertypes() ); 187 188 // explore all features (attributes) of all (super)classes print their values 189 metaObjectsIterator = allMetaObjects.iterator(); 190 while ( metaObjectsIterator.hasNext() ) { 191 metaObject = (MofClass) metaObjectsIterator.next(); 192 193 contentsIterator = metaObject.getContents().iterator(); 194 while ( contentsIterator.hasNext() ) { 195 contents = (ModelElement) contentsIterator.next(); 196 197 if ( contents instanceof javax.jmi.model.Attribute ) { // Handling Structs causes infinite loop, so StructuralFeatures are not considered 198 // print the attribute's name 199 result.append(prefixNested).append( contents.getName() ); 200 try { 201 value = object.refGetValue( contents ); 202 203 // print the defaultAttribute value 204 result.append( "=" ); 205 toString( value, result, prefixNested, visited ); 206 207 } catch (Exception e) { // this should not happen 208 result.append( " exception caused: " + e.getClass().getName() +" : "+ e.getMessage() ); 209 } 210 result.append( ",\n" ); 211 } 212 } 213 } 214 result.append( prefix ) 215 .append( "}" ); 216 } 217 218 219 /** 220 * This method requires an M1 object object and a StringBuilder where to describe the object. 221 * 222 * @param result is the buffer where to add the obect's description 223 * @param object is the M1 object to investigate 224 */ 225 private void append(StringBuilder result, String prefix, RefStruct object, Set visited) { 226 String name; 227 Object value; 228 Iterator namesIterator; 229 List typeNames; 230 String prefixNested; 231 232 prefixNested = prefix+" "; 233 234 typeNames = object.refTypeName(); // size > 0 235 result.append( typeNames.get( typeNames.size()-1 ) ) 236 .append( " {\n" ); 237 238 namesIterator = object.refFieldNames().iterator(); 239 while ( namesIterator.hasNext() ) { 240 name = (String) namesIterator.next(); 241 242 // print the attribute's name 243 result.append(prefixNested).append( name ); 244 try { 245 // get the value of the defaultAttribute 246 value = object.refGetValue( name ); 247 248 // print the defaultAttribute value 249 result.append( "=" ); 250 toString( value, result, prefixNested, visited ); 251 252 } catch (Exception e) { // this should not happen 253 result.append( " exception caused: " + e.getClass().getName() + " : " + e.getMessage() ); 254 } 255 result.append( ",\n" ); 256 } 257 result.append( prefix ) 258 .append( "}" ); 259 } 260 261 262 /** 263 * This method requires an M1 object object and a StringBuilder where to describe the object. 264 * 265 * @param result is the buffer where to add the obect's description 266 * @param object is the M1 object to investigate 267 */ 268 private void append(StringBuilder result, RefPackage object, Set visited) { 269 result.append( "RefPackage ") 270 .append( object.refMetaObject().refGetValue( "name" ) ); 271 } 272 273 /** 274 * @param result is the buffer where to add the obect's description 275 * @param object non-null enum to print 276 */ 277 private void append(StringBuilder result, RefEnum object, Set visited) { 278 result.append( "RefEnum ") 279 .append( object.refTypeName() ) 280 .append( " " ) 281 .append( object.toString() ); 282 } 283 284 285 /** 286 * This method requires an M1 object object and a StringBuilder where to describe the object. 287 * 288 * @param result is the buffer where to add the obect's description 289 * @param object is the M1 object to investigate 290 */ 291 private void append(StringBuilder result, RefClass object, Set visited) { 292 result.append( "RefClass ") 293 .append( object.refMetaObject().refGetValue( "name" ) ); 294 } 295 296 297 /** 298 * This method requires an M1 object object and a StringBuilder where to describe the object. 299 * 300 * @param result is the buffer where to add the obect's description 301 * @param object is the M1 object/association to investigate 302 */ 303 private void append(StringBuilder result, String prefix, RefAssociation object, Set visited) { 304 Iterator<RefAssociationLink> linksIterator; 305 RefAssociationLink link; 306 307 result.append( "RefAssociation ") 308 .append( object.refMetaObject().refGetValue( "name" ) ); 309 result.append( " {\n"); 310 311// linksIterator = object.refAllLinks().iterator(); 312// while ( linksIterator.hasNext() ) { 313// link = linksIterator.next(); 314// 315// result.append( prefix ); 316// toString( link, result, prefix+" ", visited ); 317// result.append( "\n"); 318// } 319 320 result.append( prefix ) 321 .append( "}"); 322 } 323 324 325 /** 326 * This method requires an M1 object object and a StringBuilder where to describe the object. 327 * 328 * @param result is the buffer where to add the obect's description 329 * @param object is the M1 object to investigate 330 */ 331 private void append(StringBuilder result, String prefix, RefAssociationLink object, Set visited) { 332 result.append( "RefAssociationLink ") 333 .append( "{\n") 334 .append( prefix ) 335 .append( " firstEnd="); 336 toString( object.refFirstEnd(), result, prefix+" ", visited); 337 result.append("\n") 338 .append( prefix ) 339 .append( " secondEnd="); 340 toString( object.refSecondEnd(), result, prefix+" ", visited); 341 result.append("\n") 342 .append( prefix ) 343 .append("}"); 344 } 345 346 /** 347 * This method requires an M1 object object and a StringBuilder where to describe the object. 348 * 349 * @param result is the buffer where to add the obect's description 350 * @param collection is the M1 object to investigate 351 */ 352 private void append(StringBuilder result, String prefix, Collection collection, Set visited) { 353 Object value; 354 Iterator valuesIterator; 355 boolean onSeparateLine; 356 357 result.append( "{" ); 358 359 onSeparateLine = false; 360 361 valuesIterator = collection.iterator(); 362 while ( valuesIterator.hasNext() ) { 363 value = valuesIterator.next(); 364 365 onSeparateLine |= value instanceof RefBaseObject 366 || value instanceof RefStruct 367 || value instanceof RefEnum 368 || value instanceof RefAssociation 369 || value instanceof RefAssociationLink; 370 371 if ( onSeparateLine ) { // print the model elements with the prefix 372 result.append( "\n" ); 373 result.append( prefix+" " ); 374 } 375 toString( value, result, prefix+" ", visited ); 376 if ( valuesIterator.hasNext() ) { 377 result.append(","); 378 } 379 } 380 if ( onSeparateLine ) { // model elements were printed 381 result.append("\n") 382 .append( prefix ) 383 .append( "}" ); 384 385 } else { // a regular list 386 result.append( "}" ); 387 } 388 } 389 390 /** 391 * @param wrapped 392 * @return a non-null object with its toString() printing the wrapped element 393 */ 394 public Object toPrint(Object wrapped ) { 395 return new ToString(wrapped); 396 } 397 398 /** 399 * Use the parent printer to form the toString() contents 400 */ 401 private class ToString { 402 private final Object wrapped; 403 404 public ToString(Object wrapped) { 405 this.wrapped = wrapped; 406 } 407 408 /** 409 * @see java.lang.Object#toString() 410 */ 411 public String toString() { 412 return execute(wrapped); 413 } 414 } 415}