001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 */ 019 020package org.apache.isis.core.metamodel.util; 021 022import java.util.List; 023 024import com.google.common.collect.Lists; 025 026import org.apache.isis.applib.annotation.Where; 027import org.apache.isis.applib.filter.Filters; 028import org.apache.isis.core.commons.authentication.AuthenticationSession; 029import org.apache.isis.core.commons.debug.DebugBuilder; 030import org.apache.isis.core.commons.debug.DebugString; 031import org.apache.isis.core.commons.debug.DebugUtils; 032import org.apache.isis.core.commons.debug.DebuggableWithTitle; 033import org.apache.isis.core.metamodel.adapter.ObjectAdapter; 034import org.apache.isis.core.metamodel.facetapi.Facet; 035import org.apache.isis.core.metamodel.facets.collections.modify.CollectionFacet; 036import org.apache.isis.core.metamodel.facets.collections.modify.CollectionFacetUtils; 037import org.apache.isis.core.metamodel.facets.object.bounded.ChoicesFacetUtils; 038import org.apache.isis.core.metamodel.facets.object.cached.CachedFacetUtils; 039import org.apache.isis.core.metamodel.facets.object.immutable.ImmutableFacetUtils; 040import org.apache.isis.core.metamodel.spec.ActionType; 041import org.apache.isis.core.metamodel.spec.ObjectSpecification; 042import org.apache.isis.core.metamodel.spec.feature.Contributed; 043import org.apache.isis.core.metamodel.spec.feature.ObjectAction; 044import org.apache.isis.core.metamodel.spec.feature.ObjectActionParameter; 045import org.apache.isis.core.metamodel.spec.feature.ObjectAssociation; 046 047public final class Dump { 048 049 private static DebugBuilder debugBuilder; 050 051 // REVIEW: should provide this rendering context, rather than hardcoding. 052 // the net effect currently is that class members annotated with 053 // @Hidden(where=Where.ANYWHERE) or @Disabled(where=Where.ANYWHERE) will indeed 054 // be hidden/disabled, but will be visible/enabled (perhaps incorrectly) 055 // for any other value for Where 056 private final static Where where = Where.ANYWHERE; 057 058 private Dump() { 059 } 060 061 // ///////////////////////////////////////////////////////////////////// 062 // specification 063 // ///////////////////////////////////////////////////////////////////// 064 065 /** 066 * @see #specification(ObjectAdapter, DebugBuilder) 067 * @see #specification(ObjectSpecification, DebugBuilder) 068 */ 069 public static String specification(final ObjectAdapter adapter) { 070 final DebugBuilder debugBuilder = new DebugString(); 071 specification(adapter, debugBuilder); 072 return debugBuilder.toString(); 073 } 074 075 /** 076 * Convenience overload of 077 * {@link #specification(ObjectSpecification, DebugBuilder)} that takes the 078 * {@link ObjectSpecification} ( {@link ObjectAdapter#getSpecification()}) 079 * of the provided {@link ObjectAdapter} 080 * 081 * @see #specification(ObjectAdapter) 082 * @see #specification(ObjectSpecification, DebugBuilder) 083 */ 084 public static void specification(final ObjectAdapter adapter, final DebugBuilder debugBuilder) { 085 final ObjectSpecification specification = adapter.getSpecification(); 086 specification(specification, debugBuilder); 087 } 088 089 public static void specification(final ObjectSpecification specification, final DebugBuilder debugBuilder) { 090 try { 091 debugBuilder.appendTitle(specification.getClass().getName()); 092 debugBuilder.appendAsHexln("Hash code", specification.hashCode()); 093 debugBuilder.appendln("ID", specification.getIdentifier()); 094 debugBuilder.appendln("Full Name", specification.getFullIdentifier()); 095 debugBuilder.appendln("Short Name", specification.getShortIdentifier()); 096 debugBuilder.appendln("Singular Name", specification.getSingularName()); 097 debugBuilder.appendln("Plural Name", specification.getPluralName()); 098 debugBuilder.appendln("Description", specification.getDescription()); 099 debugBuilder.blankLine(); 100 debugBuilder.appendln("Features", featureList(specification)); 101 debugBuilder.appendln("Type", specification.isParentedOrFreeCollection() ? "Collection" : "Object"); 102 if (specification.superclass() != null) { 103 debugBuilder.appendln("Superclass", specification.superclass().getFullIdentifier()); 104 } 105 debugBuilder.appendln("Interfaces", specificationNames(specification.interfaces())); 106 debugBuilder.appendln("Subclasses", specificationNames(specification.subclasses())); 107 debugBuilder.blankLine(); 108 debugBuilder.appendln("Service", specification.isService()); 109 debugBuilder.appendln("Encodable", specification.isEncodeable()); 110 debugBuilder.appendln("Parseable", specification.isParseable()); 111 debugBuilder.appendln("Aggregated", specification.isValueOrIsParented()); 112 } catch (final RuntimeException e) { 113 debugBuilder.appendException(e); 114 } 115 116 if (specification instanceof DebuggableWithTitle) { 117 ((DebuggableWithTitle) specification).debugData(debugBuilder); 118 } 119 120 debugBuilder.blankLine(); 121 122 debugBuilder.appendln("Facets"); 123 final Class<? extends Facet>[] facetTypes = specification.getFacetTypes(); 124 debugBuilder.indent(); 125 if (facetTypes.length == 0) { 126 debugBuilder.appendln("none"); 127 } else { 128 for (final Class<? extends Facet> type : facetTypes) { 129 final Facet facet = specification.getFacet(type); 130 debugBuilder.appendln(facet.toString()); 131 } 132 } 133 debugBuilder.unindent(); 134 debugBuilder.blankLine(); 135 136 debugBuilder.appendln("Fields"); 137 debugBuilder.indent(); 138 specificationFields(specification, debugBuilder); 139 debugBuilder.unindent(); 140 141 debugBuilder.appendln("Object Actions"); 142 debugBuilder.indent(); 143 specificationActionMethods(specification, debugBuilder); 144 debugBuilder.unindent(); 145 } 146 147 private static String[] specificationNames(final List<ObjectSpecification> specifications) { 148 final String[] names = new String[specifications.size()]; 149 for (int i = 0; i < names.length; i++) { 150 names[i] = specifications.get(i).getFullIdentifier(); 151 } 152 return names; 153 } 154 155 private static void specificationActionMethods(final ObjectSpecification specification, final DebugBuilder debugBuilder) { 156 try { 157 final List<ObjectAction> userActions = specification.getObjectActions(ActionType.USER, Contributed.INCLUDED, Filters.<ObjectAction>any()); 158 final List<ObjectAction> explActions = specification.getObjectActions(ActionType.EXPLORATION, Contributed.INCLUDED, Filters.<ObjectAction>any()); 159 final List<ObjectAction> prototypeActions = specification.getObjectActions(ActionType.PROTOTYPE, Contributed.INCLUDED, Filters.<ObjectAction>any()); 160 final List<ObjectAction> debActions = specification.getObjectActions(ActionType.DEBUG, Contributed.INCLUDED, Filters.<ObjectAction>any()); 161 specificationMethods(userActions, explActions, prototypeActions, debActions, debugBuilder); 162 } catch (final RuntimeException e) { 163 debugBuilder.appendException(e); 164 } 165 } 166 167 private static void specificationFields(final ObjectSpecification specification, final DebugBuilder debugBuilder) { 168 final List<ObjectAssociation> fields = specification.getAssociations(Contributed.EXCLUDED); 169 debugBuilder.appendln("All"); 170 debugBuilder.indent(); 171 for (int i = 0; i < fields.size(); i++) { 172 debugBuilder.appendln((i + 1) + "." + fields.get(i).getId()); 173 } 174 debugBuilder.unindent(); 175 176 final List<ObjectAssociation> fields2 = specification.getAssociations(Contributed.EXCLUDED, ObjectAssociation.Filters.VISIBLE_AT_LEAST_SOMETIMES); 177 debugBuilder.appendln("Static"); 178 debugBuilder.indent(); 179 for (int i = 0; i < fields2.size(); i++) { 180 debugBuilder.appendln((i + 1) + "." + fields2.get(i).getId()); 181 } 182 debugBuilder.unindent(); 183 debugBuilder.appendln(); 184 185 try { 186 if (fields.size() == 0) { 187 debugBuilder.appendln("none"); 188 } else { 189 for (int i = 0; i < fields.size(); i++) { 190 191 final ObjectAssociation field = fields.get(i); 192 debugBuilder.appendln((i + 1) + "." + field.getId() + " (" + field.getClass().getName() + ")"); 193 194 debugBuilder.indent(); 195 final String description = field.getDescription(); 196 if (description != null && !description.equals("")) { 197 debugBuilder.appendln("Description", description); 198 } 199 final String help = field.getHelp(); 200 if (help != null && !help.equals("")) { 201 debugBuilder.appendln("Help", help.substring(0, Math.min(30, help.length())) + (help.length() > 30 ? "..." : "")); 202 } 203 204 205 debugBuilder.appendln("ID", field.getIdentifier()); 206 debugBuilder.appendln("Short ID", field.getId()); 207 debugBuilder.appendln("Name", field.getName()); 208 final String type = field.isOneToManyAssociation() ? "Collection" : field.isOneToOneAssociation() ? "Object" : "Unknown"; 209 debugBuilder.appendln("Type", type); 210 final ObjectSpecification fieldSpec = field.getSpecification(); 211 final boolean hasIdentity = !(fieldSpec.isParentedOrFreeCollection() || fieldSpec.isParented() || fieldSpec.isValue()); 212 debugBuilder.appendln("Has identity", hasIdentity); 213 debugBuilder.appendln("Spec", fieldSpec.getFullIdentifier()); 214 215 debugBuilder.appendln("Flags", (field.isAlwaysHidden() ? "" : "Visible ") + (field.isNotPersisted() ? "Not Persisted " : " ") + (field.isMandatory() ? "Mandatory " : "")); 216 217 final Class<? extends Facet>[] facets = field.getFacetTypes(); 218 if (facets.length > 0) { 219 debugBuilder.appendln("Facets"); 220 debugBuilder.indent(); 221 boolean none = true; 222 for (final Class<? extends Facet> facet : facets) { 223 debugBuilder.appendln(field.getFacet(facet).toString()); 224 none = false; 225 } 226 if (none) { 227 debugBuilder.appendln("none"); 228 } 229 debugBuilder.unindent(); 230 } 231 232 debugBuilder.appendln(field.debugData()); 233 234 debugBuilder.unindent(); 235 } 236 } 237 } catch (final RuntimeException e) { 238 debugBuilder.appendException(e); 239 } 240 241 } 242 243 private static void specificationMethods(final List<ObjectAction> userActions, final List<ObjectAction> explActions, final List<ObjectAction> prototypeActions, final List<ObjectAction> debugActions, final DebugBuilder debugBuilder) { 244 if (userActions.size() == 0 && explActions.size() == 0 && prototypeActions.size() == 0 && debugActions.size() == 0) { 245 debugBuilder.appendln("no actions..."); 246 } else { 247 appendActionDetails(debugBuilder, "User actions", userActions); 248 appendActionDetails(debugBuilder, "Exploration actions", explActions); 249 appendActionDetails(debugBuilder, "Prototype actions", prototypeActions); 250 appendActionDetails(debugBuilder, "Debug actions", debugActions); 251 } 252 } 253 254 private static void appendActionDetails(final DebugBuilder debug, final String desc, final List<ObjectAction> actions) { 255 debug.appendln(desc); 256 debug.indent(); 257 for (int i = 0; i < actions.size(); i++) { 258 actionDetails(actions.get(i), 8, i, debug); 259 } 260 debug.unindent(); 261 } 262 263 private static void actionDetails(final ObjectAction objectAction, final int indent, final int count, final DebugBuilder debugBuilder) { 264 debugBuilder.appendln((count + 1) + "." + objectAction.getId() + " (" + objectAction.getClass().getName() + ")"); 265 debugBuilder.indent(); 266 try { 267 if (objectAction.getDescription() != null && !objectAction.getDescription().equals("")) { 268 debugBuilder.appendln("Description", objectAction.getDescription()); 269 } 270 debugBuilder.appendln("ID", objectAction.getId()); 271 272 debugBuilder.appendln(objectAction.debugData()); 273 debugBuilder.appendln("On type", objectAction.getOnType()); 274 275 final Class<? extends Facet>[] facets = objectAction.getFacetTypes(); 276 if (facets.length > 0) { 277 debugBuilder.appendln("Facets"); 278 debugBuilder.indent(); 279 for (final Class<? extends Facet> facet : facets) { 280 debugBuilder.appendln(objectAction.getFacet(facet).toString()); 281 } 282 debugBuilder.unindent(); 283 } 284 285 final ObjectSpecification returnType = objectAction.getReturnType(); 286 debugBuilder.appendln("Returns", returnType == null ? "VOID" : returnType.toString()); 287 288 final List<ObjectActionParameter> parameters = objectAction.getParameters(); 289 if (parameters.size() == 0) { 290 debugBuilder.appendln("Parameters", "none"); 291 } else { 292 debugBuilder.appendln("Parameters"); 293 debugBuilder.indent(); 294 final List<ObjectActionParameter> p = objectAction.getParameters(); 295 for (int j = 0; j < parameters.size(); j++) { 296 debugBuilder.append(p.get(j).getName()); 297 debugBuilder.append(" ("); 298 debugBuilder.append(parameters.get(j).getSpecification().getFullIdentifier()); 299 debugBuilder.appendln(")"); 300 debugBuilder.indent(); 301 final Class<? extends Facet>[] parameterFacets = p.get(j).getFacetTypes(); 302 for (final Class<? extends Facet> parameterFacet : parameterFacets) { 303 debugBuilder.appendln(p.get(j).getFacet(parameterFacet).toString()); 304 } 305 debugBuilder.unindent(); 306 } 307 debugBuilder.unindent(); 308 } 309 } catch (final RuntimeException e) { 310 debugBuilder.appendException(e); 311 } 312 313 debugBuilder.unindent(); 314 } 315 316 private static String featureList(final ObjectSpecification specification) { 317 final StringBuilder str = new StringBuilder(); 318 if (specification.isAbstract()) { 319 str.append("Abstract "); 320 } 321 if (ChoicesFacetUtils.hasChoices(specification)) { 322 str.append("WithChoices "); 323 } 324 if (CachedFacetUtils.isCached(specification)) { 325 str.append("Cached "); 326 } 327 if (ImmutableFacetUtils.isAlwaysImmutable(specification)) { 328 str.append("Immutable (always) "); 329 } 330 if (ImmutableFacetUtils.isImmutableOncePersisted(specification)) { 331 str.append("Immutable (once persisted) "); 332 } 333 if (specification.isService()) { 334 str.append("Service "); 335 } 336 return str.toString(); 337 } 338 339 // ///////////////////////////////////////////////////////////////////// 340 // adapter 341 // ///////////////////////////////////////////////////////////////////// 342 343 /** 344 * @see #adapter(ObjectAdapter, DebugBuilder) 345 */ 346 public static String adapter(final ObjectAdapter adapter) { 347 final DebugBuilder debugBuilder = new DebugString(); 348 adapter(adapter, debugBuilder); 349 return debugBuilder.toString(); 350 } 351 352 /** 353 * @see #adapter(ObjectAdapter) 354 */ 355 public static void adapter(final ObjectAdapter adapter, final DebugBuilder builder) { 356 try { 357 builder.appendln("Adapter", adapter.getClass().getName()); 358 builder.appendln("Class", adapter.getObject() == null ? "none" : adapter.getObject().getClass().getName()); 359 builder.appendAsHexln("Hash", adapter.hashCode()); 360 builder.appendln("Object", adapter.getObject()); 361 builder.appendln("Title", adapter.titleString()); 362 builder.appendln("Specification", adapter.getSpecification().getFullIdentifier()); 363 364 builder.appendln(); 365 366 builder.appendln("Icon", adapter.getIconName()); 367 builder.appendln("OID", adapter.getOid()); 368 builder.appendln("State", adapter.getResolveState()); 369 builder.appendln("Version", adapter.getVersion()); 370 371 } catch (final RuntimeException e) { 372 builder.appendException(e); 373 } 374 375 } 376 377 // ///////////////////////////////////////////////////////////////////// 378 // graph 379 // ///////////////////////////////////////////////////////////////////// 380 381 /** 382 * Creates an ascii object graph diagram for the specified object, up to 383 * three levels deep. 384 * 385 * @see #graph(ObjectAdapter, AuthenticationSession, DebugBuilder) 386 */ 387 public static String graph(final ObjectAdapter adapter, final AuthenticationSession authenticationSession) { 388 debugBuilder = new DebugString(); 389 graph(adapter, authenticationSession, debugBuilder); 390 return debugBuilder.toString(); 391 } 392 393 /** 394 * As {@link #graph(ObjectAdapter, AuthenticationSession)}, but appending to 395 * the provided {@link DebugBuilder}. 396 * 397 * @see #graph(ObjectAdapter, AuthenticationSession) 398 */ 399 public static void graph(final ObjectAdapter object, final AuthenticationSession authenticationSession, final DebugBuilder debugBuilder) { 400 simpleObject(object, debugBuilder); 401 debugBuilder.appendln(); 402 debugBuilder.append(object); 403 graph(object, 0, Lists.<ObjectAdapter> newArrayList(), authenticationSession, debugBuilder); 404 } 405 406 private static void simpleObject(final ObjectAdapter collectionAdapter, final DebugBuilder debugBuilder) { 407 debugBuilder.appendln(collectionAdapter.titleString()); 408 final ObjectSpecification objectSpec = collectionAdapter.getSpecification(); 409 if (objectSpec.isParentedOrFreeCollection()) { 410 final CollectionFacet facet = CollectionFacetUtils.getCollectionFacetFromSpec(collectionAdapter); 411 int i = 1; 412 for (final ObjectAdapter element : facet.collection(collectionAdapter)) { 413 debugBuilder.appendln(i++ + " " + element.titleString()); 414 } 415 } else { 416 // object is a regular Object 417 try { 418 final List<ObjectAssociation> fields = objectSpec.getAssociations(Contributed.EXCLUDED); 419 for (int i = 0; i < fields.size(); i++) { 420 final ObjectAssociation field = fields.get(i); 421 final ObjectAdapter obj = field.get(collectionAdapter); 422 423 final String name = field.getId(); 424 if (obj == null) { 425 debugBuilder.appendln(name, "null"); 426 } else { 427 debugBuilder.appendln(name, obj.titleString()); 428 } 429 } 430 } catch (final RuntimeException e) { 431 debugBuilder.appendException(e); 432 } 433 } 434 } 435 436 private static void collectionGraph(final ObjectAdapter collectionAdapter, final int level, final List<ObjectAdapter> ignoreAdapters, final AuthenticationSession authenticationSession, final DebugBuilder debugBuilder) { 437 438 if (ignoreAdapters.contains(collectionAdapter)) { 439 debugBuilder.append("*\n"); 440 } else { 441 ignoreAdapters.add(collectionAdapter); 442 final CollectionFacet facet = CollectionFacetUtils.getCollectionFacetFromSpec(collectionAdapter); 443 for (final ObjectAdapter element : facet.collection(collectionAdapter)) { 444 graphIndent(level, debugBuilder); 445 debugBuilder.append(element); 446 if (ignoreAdapters.contains(element)) { 447 debugBuilder.append("*\n"); 448 } else { 449 graph(element, level + 1, ignoreAdapters, authenticationSession, debugBuilder); 450 } 451 } 452 } 453 } 454 455 private static void graph(final ObjectAdapter adapter, final int level, final List<ObjectAdapter> ignoreAdapters, final AuthenticationSession authenticationSession, final DebugBuilder debugBuilder) { 456 if (level > 3) { 457 debugBuilder.appendln("..."); // only go 3 levels? 458 } else { 459 debugBuilder.append("\n"); 460 if (adapter.getSpecification().isParentedOrFreeCollection()) { 461 collectionGraph(adapter, level, ignoreAdapters, authenticationSession, debugBuilder); 462 } else if (adapter.getSpecification().isNotCollection()) { 463 objectGraph(adapter, level, ignoreAdapters, debugBuilder, authenticationSession); 464 } else { 465 debugBuilder.append("??? " + adapter); 466 } 467 } 468 } 469 470 private static void graphIndent(final int level, final DebugBuilder debugBuilder) { 471 for (int indent = 0; indent < level; indent++) { 472 debugBuilder.append(DebugUtils.indentString(4) + "|"); 473 } 474 debugBuilder.append(DebugUtils.indentString(4) + "+--"); 475 } 476 477 private static void objectGraph(final ObjectAdapter adapter, final int level, final List<ObjectAdapter> ignoreAdapters, final DebugBuilder s, final AuthenticationSession authenticationSession) { 478 ignoreAdapters.add(adapter); 479 480 try { 481 // work through all its fields 482 final List<ObjectAssociation> fields = adapter.getSpecification().getAssociations(Contributed.EXCLUDED); 483 for (int i = 0; i < fields.size(); i++) { 484 final ObjectAssociation field = fields.get(i); 485 final ObjectAdapter obj = field.get(adapter); 486 final String name = field.getId(); 487 graphIndent(level, s); 488 489 if (field.isVisible(authenticationSession, adapter, where).isVetoed()) { 490 s.append(name + ": (not visible)"); 491 s.append("\n"); 492 } else { 493 if (obj == null) { 494 s.append(name + ": null\n"); 495 /* 496 * } else if (obj.getSpecification().isParseable()) { 497 * s.append(name + ": " + obj.titleString()); 498 * s.append("\n"); 499 */} else { 500 if (ignoreAdapters.contains(obj)) { 501 s.append(name + ": " + obj + "*\n"); 502 } else { 503 s.append(name + ": " + obj); 504 graph(obj, level + 1, ignoreAdapters, authenticationSession, s); 505 506 } 507 } 508 } 509 } 510 } catch (final RuntimeException e) { 511 s.appendException(e); 512 } 513 } 514 515}