});
// Types are registered as FQPN
group.registerRenderer(Type.class, new AttributeRenderer() {
@Override
public String toString(Object o, String formatString, Locale locale) {
Type type = (Type) o;
return toString(type);
}
protected String toString(Type type) {
// Allow a TypeHint to override any interpretation of the Type
if (type.getTypeHint() != null) {
return type.getTypeHint().getValue();
}
switch (type.getJsonKind()) {
case ANY:
return JsonElement.class.getCanonicalName();
case BOOLEAN:
return Boolean.class.getCanonicalName();
case DOUBLE:
return Double.class.getCanonicalName();
case INTEGER:
return Integer.class.getCanonicalName();
case LIST:
return List.class.getCanonicalName() + "<" + toString(type.getListElement()) + ">";
case MAP:
return Map.class.getCanonicalName() + "<" + toString(type.getMapKey()) + ","
+ toString(type.getMapValue()) + ">";
case NULL:
return Void.class.getCanonicalName();
case STRING: {
// Look for the presence of enum values
if (type.getEnumValues() != null) {
// Register a referenced enum
usedEnums.add(type);
return typePrefix + upcase(type.getName());
}
// Any other named type must be an entity type
if (type.getName() != null) {
// Allow type to be overridden
return typePrefix + upcase(type.getName());
}
// Otherwise it must be a plain string
return String.class.getCanonicalName();
}
}
throw new UnsupportedOperationException("Unknown JsonKind " + type.getJsonKind());
}
});
group.registerModelAdaptor(ApiDescription.class,
new ObjectModelAdaptor() {
@Override
public Object getProperty(Interpreter interp, ST self, Object o,
Object property, String propertyName)
throws STNoSuchPropertyException {
ApiDescription apiDescription = (ApiDescription) o;
if ("endpoints".equals(propertyName)) {
Set<EndpointDescription> uniqueEndpoints =
new HashSet<EndpointDescription>(apiDescription.getEndpoints());
List<EndpointDescription> sortedEndpoints = new ArrayList<EndpointDescription>(
uniqueEndpoints);
Collections.sort(sortedEndpoints, new Comparator<EndpointDescription>() {
@Override
public int compare(EndpointDescription e1, EndpointDescription e2) {
return e1.getPath().compareTo(e2.getPath());
}
});
return sortedEndpoints;
}
return super.getProperty(interp, self, o, property, propertyName);
}
});
group.registerModelAdaptor(EndpointDescription.class, new ObjectModelAdaptor() {
@Override
public Object getProperty(Interpreter interp, ST self, Object o, Object property,
String propertyName) throws STNoSuchPropertyException {
EndpointDescription end = (EndpointDescription) o;
if ("combinedArgs".equals(propertyName)) {
// Return the path and query parameters together
List<ParameterDescription> toReturn = new ArrayList<ParameterDescription>();
if (end.getPathParameters() != null) {
toReturn.addAll(end.getPathParameters());
}
if (end.getQueryParameters() != null) {
toReturn.addAll(end.getQueryParameters());
}
return toReturn;
} else if ("javaName".equals(propertyName) || "javaNameUpcase".equals(propertyName)) {
// Convert a path like /api/2/foo/bar/{}/baz to fooBarBazMethod
String path = end.getPath();
String[] parts = path.split(Pattern.quote("/"));
StringBuilder sb = new StringBuilder();
for (int i = stripPathSegments, j = parts.length; i < j; i++) {
try {
String part = parts[i];
if (part.length() == 0) {
continue;
}
StringBuilder decodedPart = new StringBuilder(URLDecoder.decode(part, "UTF8"));
// Trim characters that aren't legal
for (int k = decodedPart.length() - 1; k >= 0; k--) {
if (!Character.isJavaIdentifierPart(decodedPart.charAt(k))) {
decodedPart.deleteCharAt(k);
}
}
// Append the new name part, using camel-cased names
String newPart = decodedPart.toString();
if (sb.length() > 0) {
newPart = upcase(newPart);
}
sb.append(newPart);
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
}
sb.append(upcase(end.getMethod().toLowerCase()));
String javaName = sb.toString();
if ("javaNameUpcase".equals(propertyName)) {
javaName = upcase(javaName);
}
return javaName;
} else if ("pathDecoded".equals(propertyName)) {
// URL-decode the path in the endpoint description
try {
String decoded = URLDecoder.decode(end.getPath(), "UTF8");
return decoded;
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
} else if ("hasPayload".equals(propertyName)) {
return end.getEntity() != null;
}
return super.getProperty(interp, self, o, property, propertyName);
}
});
group.registerModelAdaptor(EntityDescription.class, new ObjectModelAdaptor() {
@Override
public Object getProperty(Interpreter interp, ST self, Object o, Object property,
String propertyName) throws STNoSuchPropertyException {
EntityDescription entity = (EntityDescription) o;
if ("baseType".equals(propertyName)) {
return baseTypes.contains(entity.getTypeName());
} else if ("payloadName".equals(propertyName)) {
return entity.getTypeName();
} else if ("supertype".equals(propertyName)) {
EntityDescription supertype = entity.getSupertype();
if (supertype == null) {
return entity.isPersistent() ? BasePersistenceAware.class.getCanonicalName()
: BaseHasUuid.class.getCanonicalName();
} else {
return supertype;
}
} else if ("simpleName".equals(propertyName)) {
return typePrefix + upcase(entity.getTypeName());
}
return super.getProperty(interp, self, o, property, propertyName);
}
});
group.registerModelAdaptor(ParameterDescription.class, new ObjectModelAdaptor() {
@Override
public Object getProperty(Interpreter interp, ST self, Object o, Object property,
String propertyName) throws STNoSuchPropertyException {
ParameterDescription param = (ParameterDescription) o;
if ("javaNameUpcase".equals(propertyName)) {
return upcase(param.getName());
}
return super.getProperty(interp, self, o, property, propertyName);
}
});
group.registerModelAdaptor(Property.class, new ObjectModelAdaptor() {
@Override
public Object getProperty(Interpreter interp, ST self, Object o, Object property,
String propertyName) throws STNoSuchPropertyException {
Property p = (Property) o;
if ("getterName".equals(propertyName)) {
return upcase(p.getName());
} else if ("needsImplied".equals(propertyName)) {
// Returns true if the property has @Implies / @OneToMany and is a list
return p.getImpliedProperty() != null && p.getType().getListElement() != null;
} else if ("needsAdder".equals(propertyName)) {
return JsonKind.LIST.equals(p.getType().getJsonKind());
} else if ("needsPutter".equals(propertyName)) {
return JsonKind.MAP.equals(p.getType().getJsonKind());
}
return super.getProperty(interp, self, o, property, propertyName);
}
});
group.registerModelAdaptor(Type.class, new ObjectModelAdaptor() {
@Override
public Object getProperty(Interpreter interp, ST self, Object o, Object property,
String propertyName) throws STNoSuchPropertyException {
Type type = (Type) o;
if ("flatTypes".equals(propertyName)) {
return flatten(type);
}
return super.getProperty(interp, self, o, property, propertyName);
}
private List<String> flatten(Type type) {
switch (type.getJsonKind()) {
case ANY:
return Collections.singletonList(Object.class.getCanonicalName());
case BOOLEAN:
return Collections.singletonList(Boolean.class.getCanonicalName());
case DOUBLE:
return Collections.singletonList(Double.class.getCanonicalName());
case INTEGER:
return Collections.singletonList(Integer.class.getCanonicalName());
case LIST: {
List<String> toReturn = new ArrayList<String>();
toReturn.add(List.class.getCanonicalName());
toReturn.addAll(flatten(type.getListElement()));
return toReturn;
}
case MAP: {
List<String> toReturn = new ArrayList<String>();
toReturn.add(Map.class.getCanonicalName());
toReturn.addAll(flatten(type.getMapKey()));
toReturn.addAll(flatten(type.getMapValue()));
return toReturn;
}
case NULL:
return Collections.singletonList(Void.class.getCanonicalName());
case STRING: {
if (type.getName() != null) {
return Collections.singletonList(typePrefix + upcase(type.getName()));
} else if (type.getTypeHint() != null) {
return Collections.singletonList(type.getTypeHint().getValue());
} else {
return Collections.singletonList(String.class.getCanonicalName());
}
}
}
throw new UnsupportedOperationException("Unknown JsonKind " + type.getJsonKind());
}
});
group.registerModelAdaptor(String.class, new ObjectModelAdaptor() {
@Override