in json/src/main/java/org/netbeans/html/json/impl/ModelProcessor.java [161:620]
private boolean processModel(Element e) {
boolean ok = true;
Model m = e.getAnnotation(Model.class);
if (m == null) {
return true;
}
String pkg = findPkgName(e);
Writer w;
String className = m.className();
models.put(e, className);
try {
StringWriter body = new StringWriter();
StringBuilder onReceiveType = new StringBuilder();
List<GetSet> propsGetSet = new ArrayList<>();
List<Object> functions = new ArrayList<>();
Map<String, Collection<String[]>> propsDeps = new HashMap<>();
Map<String, Collection<String>> functionDeps = new HashMap<>();
Prprt[] props = createProps(e, m.properties());
final String builderPrefix = findBuilderPrefix(e, m);
if (!generateComputedProperties(className, body, props, e.getEnclosedElements(), propsGetSet, propsDeps)) {
ok = false;
}
if (!generateOnChange(e, propsDeps, props, className, functionDeps)) {
ok = false;
}
Set<String> propertyFQNs = new HashSet<>();
if (!generateProperties(e, builderPrefix, body, className, props, propsGetSet, propsDeps, functionDeps, propertyFQNs)) {
ok = false;
}
if (!generateFunctions(e, body, className, e.getEnclosedElements(), functions)) {
ok = false;
}
int functionsCount = functions.size() / 2;
for (int i = 0; i < functions.size(); i += 2) {
for (Prprt p : props) {
if (p.name().equals(functions.get(i))) {
error("Function cannot have the name of an existing property", e);
ok = false;
}
}
}
if (!generateReceive(e, body, className, e.getEnclosedElements(), onReceiveType, propertyFQNs)) {
ok = false;
}
if (!generateOperation(e, body, className, e.getEnclosedElements(), functions)) {
ok = false;
}
FileObject java = processingEnv.getFiler().createSourceFile(pkg + '.' + className, e);
w = new OutputStreamWriter(java.openOutputStream());
try {
w.append("package " + pkg + ";\n");
w.append("import net.java.html.json.*;\n");
for (String propertyFqns : propertyFQNs) {
w.append("import "+propertyFqns+";\n");
}
final String inPckName = inPckName(e, false);
w.append("/** Generated for {@link ").append(inPckName).append("}*/\n");
w.append("@java.lang.SuppressWarnings(\"all\")\n");
w.append("public final class ").append(className).append(" implements Cloneable {\n");
w.append(" private static Class<").append(inPckName).append("> modelFor() { return ").append(inPckName).append(".class; }\n");
w.append(" private static final Html4JavaType TYPE = new Html4JavaType();\n");
if (m.instance()) {
int cCnt = 0;
for (Element c : e.getEnclosedElements()) {
if (c.getKind() != ElementKind.CONSTRUCTOR) {
continue;
}
cCnt++;
ExecutableElement ec = (ExecutableElement) c;
if (ec.getParameters().size() > 0) {
continue;
}
if (ec.getModifiers().contains(Modifier.PRIVATE)) {
continue;
}
cCnt = 0;
break;
}
if (cCnt > 0) {
ok = false;
error("Needs non-private default constructor when instance=true", e);
w.append(" private final ").append(inPckName).append(" instance = null;\n");
} else {
w.append(" private final ").append(inPckName).append(" instance = new ").append(inPckName).append("();\n");
}
}
w.append(" private final org.netbeans.html.json.spi.Proto proto;\n");
w.append(body.toString());
w.append(" private ").append(className).append("(net.java.html.BrwsrCtx context) {\n");
w.append(" this.proto = TYPE.createProto(this, context);\n");
for (Prprt p : props) {
if (p.array()) {
final String tn = typeName(p);
String[] gs = toGetSet(p.name(), tn, p.array());
w.write(" this.prop_" + p.name() + " = proto.createList(\""
+ p.name() + "\"");
if (p.mutable()) {
if (functionDeps.containsKey(p.name())) {
int index = Arrays.asList(functionDeps.keySet().toArray()).indexOf(p.name());
w.write(", " + index);
} else {
w.write(", -1");
}
} else {
w.write(", java.lang.Integer.MIN_VALUE");
}
Collection<String[]> dependants = propsDeps.get(p.name());
if (dependants != null) {
for (String[] depProp : dependants) {
w.write(", ");
w.write('\"');
w.write(depProp[0]);
w.write('\"');
}
}
w.write(")");
w.write(";\n");
}
}
w.append(" };\n");
w.append(" public ").append(className).append("() {\n");
w.append(" this(net.java.html.BrwsrCtx.findDefault(").append(className).append(".class));\n");
for (Prprt p : props) {
if (!p.array()) {
boolean[] isModel = {false};
boolean[] isEnum = {false};
boolean isPrimitive[] = {false};
String tn = checkType(p, isModel, isEnum, isPrimitive);
if (isModel[0]) {
w.write(" prop_" + p.name() + " = TYPE; /* uninitialized */\n");
}
}
}
w.append(" };\n");
if (props.length > 0 && builderPrefix == null) {
StringBuilder constructorWithArguments = new StringBuilder();
constructorWithArguments.append(" public ").append(className).append("(");
Prprt firstArray = null;
String sep = "";
int parameterCount = 0;
for (Prprt p : props) {
if (p.array()) {
if (firstArray == null) {
firstArray = p;
}
continue;
}
String tn = typeName(p);
constructorWithArguments.append(sep);
constructorWithArguments.append(tn);
String[] third = toGetSet(p.name(), tn, false);
constructorWithArguments.append(" ").append(third[2]);
sep = ", ";
parameterCount++;
}
if (firstArray != null) {
String tn;
boolean[] isModel = {false};
boolean[] isEnum = {false};
boolean isPrimitive[] = {false};
tn = checkType(firstArray, isModel, isEnum, isPrimitive);
constructorWithArguments.append(sep);
constructorWithArguments.append(tn);
String[] third = toGetSet(firstArray.name(), tn, true);
constructorWithArguments.append("... ").append(third[2]);
parameterCount++;
}
constructorWithArguments.append(") {\n");
constructorWithArguments.append(" this(net.java.html.BrwsrCtx.findDefault(").append(className).append(".class));\n");
for (Prprt p : props) {
if (p.array()) {
continue;
}
String[] third = toGetSet(p.name(), null, false);
constructorWithArguments.append(" this.prop_" + p.name() + " = " + third[2] + ";\n");
}
if (firstArray != null) {
String[] third = toGetSet(firstArray.name(), null, true);
constructorWithArguments.append(" proto.initTo(this.prop_" + firstArray.name() + ", " + third[2] + ");\n");
}
constructorWithArguments.append(" };\n");
if (parameterCount < 255) {
w.write(constructorWithArguments.toString());
}
}
w.append(" private static class Html4JavaType extends org.netbeans.html.json.spi.Proto.Type<").append(className).append("> {\n");
w.append(" private Html4JavaType() {\n super(").append(className).append(".class, ").
append(inPckName).append(".class, " + propsGetSet.size() + ", "
+ functionsCount + ");\n");
{
for (int i = 0; i < propsGetSet.size(); i++) {
w.append(" registerProperty(\"").append(propsGetSet.get(i).name).append("\", ");
w.append((i) + ", " + propsGetSet.get(i).readOnly + ", " + propsGetSet.get(i).constant + ");\n");
}
}
{
for (int i = 0; i < functionsCount; i++) {
w.append(" registerFunction(\"").append((String)functions.get(i * 2)).append("\", ");
w.append(i + ");\n");
}
}
w.append(" }\n");
w.append(" @Override public void setValue(" + className + " data, int type, Object value) {\n");
w.append(" switch (type) {\n");
for (int i = 0; i < propsGetSet.size(); i++) {
final GetSet pgs = propsGetSet.get(i);
if (pgs.readOnly) {
continue;
}
final String set = pgs.setter;
String tn = pgs.type;
String btn = findBoxedType(tn);
if (btn != null) {
tn = btn;
}
w.append(" case " + i + ": ");
if (pgs.setter != null) {
w.append("data.").append(pgs.setter).append("(TYPE.extractValue(" + tn + ".class, value)); return;\n");
} else {
w.append("TYPE.replaceValue(data.").append(pgs.getter).append("(), " + tn + ".class, value); return;\n");
}
}
w.append(" }\n");
w.append(" throw new UnsupportedOperationException();\n");
w.append(" }\n");
w.append(" @Override public Object getValue(" + className + " data, int type) {\n");
w.append(" switch (type) {\n");
for (int i = 0; i < propsGetSet.size(); i++) {
final String get = propsGetSet.get(i).getter;
if (get != null) {
w.append(" case " + i + ": return data." + get + "();\n");
}
}
w.append(" }\n");
w.append(" throw new UnsupportedOperationException();\n");
w.append(" }\n");
w.append(" @Override public void call(" + className + " model, int type, Object data, Object ev) throws Exception {\n");
w.append(" switch (type) {\n");
for (int i = 0; i < functions.size(); i += 2) {
final String name = (String)functions.get(i);
final Object param = functions.get(i + 1);
if (param instanceof ExecutableElement) {
ExecutableElement ee = (ExecutableElement)param;
w.append(" case " + (i / 2) + ":\n");
w.append(" ");
if (m.instance()) {
w.append("model.instance");
} else {
w.append(((TypeElement)e).getQualifiedName());
}
w.append(".").append(name).append("(");
w.append(wrapParams(ee, null, className, "model", "ev", "data"));
w.append(");\n");
w.append(" return;\n");
} else {
String call = (String)param;
w.append(" case " + (i / 2) + ":\n"); // model." + name + "(data, ev); return;\n");
w.append(" ").append(call).append("\n");
w.append(" return;\n");
}
}
w.append(" }\n");
w.append(" throw new UnsupportedOperationException();\n");
w.append(" }\n");
w.append(" @Override public org.netbeans.html.json.spi.Proto protoFor(Object obj) {\n");
w.append(" return ((" + className + ")obj).proto;");
w.append(" }\n");
w.append(" @Override public void onChange(" + className + " model, int type) {\n");
w.append(" switch (type) {\n");
{
String[] arr = functionDeps.keySet().toArray(new String[0]);
for (int i = 0; i < arr.length; i++) {
Collection<String> onChange = functionDeps.get(arr[i]);
if (onChange != null) {
w.append(" case " + i + ":\n");
for (String call : onChange) {
w.append(" ").append(call).append("\n");
}
w.write(" return;\n");
}
}
}
w.append(" }\n");
w.append(" throw new UnsupportedOperationException();\n");
w.append(" }\n");
w.append(onReceiveType);
w.append(" @Override public " + className + " read(net.java.html.BrwsrCtx c, Object json) { return new " + className + "(c, json); }\n");
w.append(" @Override public " + className + " cloneTo(" + className + " o, net.java.html.BrwsrCtx c) { return o.clone(c); }\n");
w.append(" }\n");
w.append(" private ").append(className).append("(net.java.html.BrwsrCtx c, Object json) {\n");
w.append(" this(c);\n");
int values = 0;
for (int i = 0; i < propsGetSet.size(); i++) {
Prprt p = findPrprt(props, propsGetSet.get(i).name);
if (p == null) {
continue;
}
values++;
}
w.append(" Object[] ret = new Object[" + values + "];\n");
w.append(" proto.extract(json, new String[] {\n");
for (int i = 0; i < propsGetSet.size(); i++) {
Prprt p = findPrprt(props, propsGetSet.get(i).name);
if (p == null) {
continue;
}
w.append(" \"").append(propsGetSet.get(i).name).append("\",\n");
}
w.append(" }, ret);\n");
for (int i = 0, cnt = 0, prop = 0; i < propsGetSet.size(); i++) {
final String pn = propsGetSet.get(i).name;
Prprt p = findPrprt(props, pn);
if (p == null || prop >= props.length) {
continue;
}
boolean[] isModel = { false };
boolean[] isEnum = { false };
boolean isPrimitive[] = { false };
String type = checkType(props[prop++], isModel, isEnum, isPrimitive);
if (p.array()) {
w.append(" for (Object e : useAsArray(ret[" + cnt + "])) {\n");
if (isModel[0]) {
w.append(" this.prop_").append(pn).append(".add(proto.read");
w.append("(" + type + ".class, e));\n");
} else if (isEnum[0]) {
w.append(" this.prop_").append(pn);
w.append(".add(e == null ? null : ");
w.append(type).append(".valueOf(TYPE.stringValue(e)));\n");
} else {
if (isPrimitive(type)) {
if (type.equals("char")) {
w.append(" this.prop_").append(pn).append(".add(TYPE.charValue(e));\n");
} else if (type.equals("boolean")) {
w.append(" this.prop_").append(pn).append(".add(TYPE.boolValue(e));\n");
} else {
w.append(" this.prop_").append(pn).append(".add(TYPE.numberValue(e).");
w.append(type).append("Value());\n");
}
} else {
w.append(" this.prop_").append(pn).append(".add((");
w.append(type).append(")e);\n");
}
}
w.append(" }\n");
} else {
if (isEnum[0]) {
w.append(" try {\n");
w.append(" this.prop_").append(pn);
w.append(" = ret[" + cnt + "] == null ? null : ");
w.append(type).append(".valueOf(TYPE.stringValue(ret[" + cnt + "]));\n");
w.append(" } catch (IllegalArgumentException ex) {\n");
w.append(" ex.printStackTrace();\n");
w.append(" }\n");
} else if (isPrimitive(type)) {
w.append(" this.prop_").append(pn);
w.append(" = ret[" + cnt + "] == null ? ");
if ("char".equals(type)) {
w.append("0 : (TYPE.charValue(");
} else if ("boolean".equals(type)) {
w.append("false : (TYPE.boolValue(");
} else {
w.append("0 : (TYPE.numberValue(");
}
w.append("ret[" + cnt + "])).");
w.append(type).append("Value();\n");
} else if (isModel[0]) {
w.append(" this.prop_").append(pn).append(" = proto.read");
w.append("(" + type + ".class, ");
w.append("ret[" + cnt + "]);\n");
}else {
w.append(" this.prop_").append(pn);
w.append(" = (").append(type).append(')');
w.append("ret[" + cnt + "];\n");
}
}
cnt++;
}
w.append(" }\n");
w.append(" private static Object[] useAsArray(Object o) {\n");
w.append(" return o instanceof Object[] ? ((Object[])o) : o == null ? new Object[0] : new Object[] { o };\n");
w.append(" }\n");
writeToString(props, w);
writeClone(className, props, w);
String targetId = findTargetId(e);
if (targetId != null) {
w.write("""
/** Activates this model instance in the current {@link
net.java.html.json.Models#bind(java.lang.Object, net.java.html.BrwsrCtx) browser context}.
In case of using Knockout technology, this means to
bind JSON like data in this model instance with Knockout tags in
the surrounding HTML page.
""");
if (targetId != null) {
w.write("This method binds to element '" + targetId + "' on the page\n");
}
w.write("""
@return <code>this</code> object
*/
""");
w.write(" public " + className + " applyBindings() {\n");
w.write(" proto.applyBindings();\n");
// w.write(" proto.applyBindings(id);\n");
w.write(" return this;\n");
w.write(" }\n");
} else {
w.write(" private " + className + " applyBindings() {\n");
w.write(" throw new IllegalStateException(\"Please specify targetId=\\\"\\\" in your @Model annotation\");\n");
w.write(" }\n");
}
w.write(" public boolean equals(Object o) {\n");
w.write(" if (o == this) return true;\n");
w.write(" if (!(o instanceof " + className + ")) return false;\n");
w.write(" " + className + " p = (" + className + ")o;\n");
boolean thisToNull = false;
for (Prprt p : props) {
boolean[] isModel = {false};
boolean[] isEnum = {false};
boolean isPrimitive[] = {false};
checkType(p, isModel, isEnum, isPrimitive);
if (isModel[0]) {
w.write(" if (!TYPE.isSame(thisToNull(prop_" + p.name() + "), p.thisToNull(p.prop_" + p.name() + "))) return false;\n");
thisToNull = true;
} else {
w.write(" if (!TYPE.isSame(prop_" + p.name() + ", p.prop_" + p.name() + ")) return false;\n");
}
}
w.write(" return true;\n");
w.write(" }\n");
w.write(" public int hashCode() {\n");
w.write(" int h = " + className + ".class.getName().hashCode();\n");
for (Prprt p : props) {
boolean[] isModel = {false};
boolean[] isEnum = {false};
boolean isPrimitive[] = {false};
checkType(p, isModel, isEnum, isPrimitive);
if (isModel[0]) {
w.write(" h = TYPE.hashPlus(thisToNull(prop_" + p.name() + "), h);\n");
} else {
w.write(" h = TYPE.hashPlus(prop_" + p.name() + ", h);\n");
}
}
w.write(" return h;\n");
w.write(" }\n");
if (thisToNull) {
w.write(" private Object thisToNull(Object value) {\n");
w.write(" return value == this || value == TYPE ? null : value;\n");
w.write(" }\n");
}
w.write("}\n");
} finally {
w.close();
}
} catch (IOException ex) {
error("Can't create " + className + ".java", e);
return false;
}
return ok;
}