concept Entity > NamedConcept { } abstract concept AbstractJavaObjectEntity > Entity { abstract A object(); toString { S s = super.toString(); O o = object(); if (o != null) s += " " + str_pcall(o); ret s; } } concept TransientJavaObjectEntity > AbstractJavaObjectEntity { gettable transient A object; } concept PersistentJavaObjectEntity > AbstractJavaObjectEntity { gettable A object; } concept ConceptEntity extends AbstractJavaObjectEntity { new Ref concept; *(A concept) { this.concept.set(concept); } A object() { ret concept!; } } concept FieldInObjectEntity > Entity { new Ref> objectEntity; S fieldName; *(AbstractJavaObjectEntity objectEntity, S *fieldName) { this.objectEntity.set(objectEntity); } O value() { ret !objectEntity.has() ? null : getOpt(objectEntity->object(), fieldName); } toString { S s = super.toString() + " " + fieldName + " in " + objectEntity; O value = value(); if (value != null) s += appendBracketed("value of type " + className(value)); ret s; } } concept DeclaredFieldEntity > Entity { Class theClass; S fieldName; *(Class *theClass, S *fieldName) {} Field fieldObject() { ret findFieldOfClass(theClass, fieldName); } toString { var fieldObject = fieldObject(); ret super.toString() + " " + (fieldObject != null ? str(fieldObject) : "[field not found: " + className(theClass) + "." + fieldName + "]"); } }