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.Collection; 011import java.util.Iterator; 012import java.util.List; 013import java.util.logging.Level; 014import java.util.logging.Logger; 015 016import javax.jmi.model.GeneralizableElement; 017import javax.jmi.reflect.RefAssociation; 018import javax.jmi.reflect.RefAssociationLink; 019import javax.jmi.reflect.RefBaseObject; 020import javax.jmi.reflect.RefEnum; 021import javax.jmi.reflect.RefFeatured; 022import javax.jmi.reflect.RefObject; 023import javax.jmi.reflect.RefStruct; 024 025import net.mdatools.modelant.core.api.Function; 026import net.mdatools.modelant.core.api.match.MatchingCriteria; 027import net.mdatools.modelant.core.util.Navigator; 028 029/** 030 * Print model elements showing only their "important" attributes 031 * and associations closer to the human language 032 * @author Rusi Popov (popovr@mdatools.net) 033 */ 034public class PrintElementRestricted implements Function<Object, String> { 035 036 /** 037 * This is a common logger 038 */ 039 private static Logger LOGGER = Logger.getLogger( PrintElementRestricted.class.getPackage().getName() ); 040 041 /** 042 * What is important to show in order to identify the object being printed 043 */ 044 private final MatchingCriteria criteria; 045 046 /** 047 * A common prefix of every line of the string presentation of the printed object 048 */ 049 private final String prefix; 050 051 /** 052 * @param prefix 053 * @param criteria not null 054 */ 055 public PrintElementRestricted(String prefix, MatchingCriteria criteria) { 056 this.criteria = criteria; 057 058 if ( prefix == null ) { 059 this.prefix = ""; 060 } else { 061 this.prefix = prefix; 062 } 063 } 064 065 /** 066 * @param argument The object to start printing from 067 * @return non-null string representation of the 068 */ 069 public String execute(Object argument) throws RuntimeException, IllegalArgumentException { 070 StringBuilder result = new StringBuilder(2048); 071 072 append( result, argument, prefix ); 073 return result.toString(); 074 } 075 076 private void append(StringBuilder result, Object object, String prefix) { 077 if ( object instanceof RefFeatured ) { // note: RefStruct is not checked 078 append( result, (RefBaseObject) object, prefix); 079 080 } else if ( object instanceof Collection ) { 081 append( result, (Collection) object, prefix); 082 } else { 083 result.append( object ); 084 } 085 } 086 087 /** 088 * This method dumps the object into the output string buffer indenting the nested elements 089 * with the prefix provided. 090 * @param result 091 * @param forObject 092 * @param prefix 093 */ 094 private void append(StringBuilder result, RefBaseObject forObject, String prefix) { 095 Iterator<String> namesIterator; 096 String name; 097 List values; 098 List associated; 099 String nestedPrefix; 100 List<String> asociationNames; 101 List<String> attributeNames; 102 String nested2Prefix; 103 104 result.append( ((GeneralizableElement) forObject.refMetaObject()).getName() ); 105 106 if ( forObject instanceof RefObject ) { 107 attributeNames = criteria.getAttributes( (RefObject) forObject ); 108 asociationNames = criteria.getAssociations( (RefObject) forObject ); 109 110 if ( attributeNames.size() + asociationNames.size() == 0) { 111 // print nothing 112 } else { 113 nestedPrefix = prefix+" "; 114 nested2Prefix = nestedPrefix+" "; 115 116 if ( attributeNames.size() + asociationNames.size() == 1 117 || attributeNames.size() == 1 118 && asociationNames.size() == 1) { // there is chance for compact printing 119 120 if ( attributeNames.size() == 1 ) { // a single attribute - print its value only 121 values = Navigator.collectValues( (RefObject) forObject, 122 attributeNames.get( 0 ), 123 LOGGER.isLoggable( Level.FINE )); 124 125 if ( values.size() > 0 ) { // a non-empty list of non-empty values 126 append( result, values, nestedPrefix ); 127 } 128 } 129 130 if ( asociationNames.size() == 1 ) { // a single association 131 // use the value to select the printing format 132 133 name = asociationNames.get( 0 ); 134 135 associated = Navigator.collectValues( (RefObject) forObject, name, false ); 136 137 if ( associated.size() == 1 ) { // a single value - use compact print 138 result.append( "/" ); 139 append(result, (RefObject) associated.get( 0 ), prefix ); 140 141 } else if ( associated.size() > 1 ) { // multiple values, use regular print 142 appendAssociated( result, name, associated, nestedPrefix, nested2Prefix ); 143 } 144 } 145 } else { // there are many attributes and/or associations, use regular (wide) printing 146 147 // print the non-empty attributes 148 namesIterator = attributeNames.iterator(); 149 while ( namesIterator.hasNext() ) { 150 name = namesIterator.next(); 151 152 try { 153 values = Navigator.collectValues( (RefObject) forObject, name, false ); 154 155 if ( values.size() > 0 ) { // a non-empty list of non-empty values 156 result.append("\n") 157 .append( nestedPrefix ) 158 .append( name ) 159 .append( "="); 160 append( result, values, prefix ); 161 } 162 } catch (Exception ex) { 163 LOGGER.log( Level.FINE, 164 "Model element: {0} does not support attribute: {1}", 165 new Object[]{new PrintModelElement(nestedPrefix).execute( forObject ), name}); 166 } 167 } 168 169 // print the non-empty associations 170 namesIterator = asociationNames.iterator(); 171 while ( namesIterator.hasNext() ) { 172 name = namesIterator.next(); 173 174 try { 175 associated = Navigator.collectValues( (RefObject) forObject, name, false ); 176 177 appendAssociated( result, name, associated, nestedPrefix, nested2Prefix ); 178 } catch (Exception ex) { 179 LOGGER.log( Level.INFO, 180 "Accessing associated {1} on model element {0} caused {2}", 181 new Object[]{new PrintModelElement(nestedPrefix).execute( forObject ), name, ex.getMessage()}); 182 } 183 } 184 } 185 } 186 } 187 } 188 189 /** 190 * @param result 191 * @param name 192 * @param associated 193 * @param nestedPrefix 194 * @param nested2Prefix 195 */ 196 private void appendAssociated(StringBuilder result, String name, List associated, String nestedPrefix, 197 String nested2Prefix) { 198 Object node; 199 Iterator associatedIterator; 200 if ( associated.size() > 0 ) { 201 result.append( "\n" ) 202 .append( nestedPrefix ) 203 .append( name ) 204 .append( "="); 205 206 // result.append("{"); 207 if ( associated.size() == 1 ) { 208 append(result, (RefObject) associated.get( 0 ), nestedPrefix ); 209 } else { 210 associatedIterator = associated.iterator(); 211 while (associatedIterator.hasNext()) { 212 node = associatedIterator.next(); 213 214 result.append("\n").append( nested2Prefix ); 215 append(result, (RefObject) node, nested2Prefix ); 216 } 217 } 218 } 219 } 220 221 /** 222 * This method requires an M1 object object and a StringBuilder where to describe the object. 223 * 224 * @param result is the buffer where to add the obect's description 225 * @param collection is the M1 object to investigate 226 */ 227 private void append(StringBuilder result, Collection collection, String prefix) { 228 Object value; 229 Iterator valuesIterator; 230 boolean onSeparateLine; 231 String nested; 232 233 nested = prefix+" "; 234 result.append( " " ); 235 236 valuesIterator = collection.iterator(); 237 if ( !valuesIterator.hasNext() ) { // an empty list 238 result.append( "{}" ); 239 240 } else { 241 value = valuesIterator.next(); 242 243 onSeparateLine = shouldPrintOnSeparateLine( value ); 244 245 if ( !valuesIterator.hasNext() ) { // a single value list - print it without {} 246 appendSingleValue( result, value, onSeparateLine, nested ); 247 248 } else { // a list of size > 1 249 result.append( "{" ); 250 251 appendSingleValue( result, value, onSeparateLine, nested ); 252 while ( valuesIterator.hasNext() ) { 253 value = valuesIterator.next(); 254 255 result.append(", "); 256 onSeparateLine |= shouldPrintOnSeparateLine( value ); 257 258 appendSingleValue( result, value, onSeparateLine, nested ); 259 } 260 if ( onSeparateLine ) { // model elements were printed 261 result.append("\n") 262 .append( prefix ) 263 .append( " "); 264 } 265 result.append( "}" ); 266 } 267 } 268 } 269 270 /** 271 * @return true when the value is a model element, so it should be printed on separate line(s) 272 */ 273 private boolean shouldPrintOnSeparateLine(Object value) { 274 return value instanceof RefBaseObject 275 || value instanceof RefStruct 276 || value instanceof RefEnum 277 || value instanceof RefAssociation 278 || value instanceof RefAssociationLink; 279 } 280 281 /** 282 * @param result 283 * @param value 284 * @param isModelElement is true when value != null && value is a model element 285 * @param nested 286 */ 287 private void appendSingleValue(StringBuilder result, Object value, boolean isModelElement, String nested) { 288 if ( isModelElement ) { // print the model elements with the prefix 289 result.append( "\n" ) 290 .append( nested ); 291 append( result, value, nested); 292 293 } else { // a "primitive" value 294// result.append( "\"" ); 295 append( result, value, nested); 296// result.append( "\"" ); 297 } 298 } 299}