javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/PageProcessor.java
branchmodel
changeset 949 3bd43aa6f08d
parent 947 26f7eeb81aec
child 950 445d5f1d4177
     1.1 --- a/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/PageProcessor.java	Sun Apr 07 22:32:41 2013 +0200
     1.2 +++ b/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/PageProcessor.java	Mon Apr 08 12:12:42 2013 +0200
     1.3 @@ -26,6 +26,7 @@
     1.4  import java.util.Collection;
     1.5  import java.util.Collections;
     1.6  import java.util.HashMap;
     1.7 +import java.util.HashSet;
     1.8  import java.util.LinkedHashSet;
     1.9  import java.util.List;
    1.10  import java.util.Map;
    1.11 @@ -60,6 +61,7 @@
    1.12  import org.apidesign.bck2brwsr.htmlpage.api.Model;
    1.13  import org.apidesign.bck2brwsr.htmlpage.api.On;
    1.14  import org.apidesign.bck2brwsr.htmlpage.api.OnFunction;
    1.15 +import org.apidesign.bck2brwsr.htmlpage.api.OnPropertyChange;
    1.16  import org.apidesign.bck2brwsr.htmlpage.api.OnReceive;
    1.17  import org.apidesign.bck2brwsr.htmlpage.api.Page;
    1.18  import org.apidesign.bck2brwsr.htmlpage.api.Property;
    1.19 @@ -76,6 +78,7 @@
    1.20      "org.apidesign.bck2brwsr.htmlpage.api.Page",
    1.21      "org.apidesign.bck2brwsr.htmlpage.api.OnFunction",
    1.22      "org.apidesign.bck2brwsr.htmlpage.api.OnReceive",
    1.23 +    "org.apidesign.bck2brwsr.htmlpage.api.OnPropertyChange",
    1.24      "org.apidesign.bck2brwsr.htmlpage.api.On"
    1.25  })
    1.26  public final class PageProcessor extends AbstractProcessor {
    1.27 @@ -128,10 +131,14 @@
    1.28              List<String> propsGetSet = new ArrayList<>();
    1.29              List<String> functions = new ArrayList<>();
    1.30              Map<String, Collection<String>> propsDeps = new HashMap<>();
    1.31 +            Map<String, Collection<String>> functionDeps = new HashMap<>();
    1.32              if (!generateComputedProperties(body, m.properties(), e.getEnclosedElements(), propsGetSet, propsDeps)) {
    1.33                  ok = false;
    1.34              }
    1.35 -            if (!generateProperties(e, body, m.properties(), propsGetSet, propsDeps)) {
    1.36 +            if (!generateOnChange(e, propsDeps, m.properties(), className, functionDeps)) {
    1.37 +                ok = false;
    1.38 +            }
    1.39 +            if (!generateProperties(e, body, m.properties(), propsGetSet, propsDeps, functionDeps)) {
    1.40                  ok = false;
    1.41              }
    1.42              if (!generateFunctions(e, body, className, e.getEnclosedElements(), functions)) {
    1.43 @@ -265,10 +272,14 @@
    1.44              List<String> propsGetSet = new ArrayList<>();
    1.45              List<String> functions = new ArrayList<>();
    1.46              Map<String, Collection<String>> propsDeps = new HashMap<>();
    1.47 +            Map<String, Collection<String>> functionDeps = new HashMap<>();
    1.48              if (!generateComputedProperties(body, p.properties(), e.getEnclosedElements(), propsGetSet, propsDeps)) {
    1.49                  ok = false;
    1.50              }
    1.51 -            if (!generateProperties(e, body, p.properties(), propsGetSet, propsDeps)) {
    1.52 +            if (!generateOnChange(e, propsDeps, p.properties(), className, functionDeps)) {
    1.53 +                ok = false;
    1.54 +            }
    1.55 +            if (!generateProperties(e, body, p.properties(), propsGetSet, propsDeps, functionDeps)) {
    1.56                  ok = false;
    1.57              }
    1.58              if (!generateFunctions(e, body, className, e.getEnclosedElements(), functions)) {
    1.59 @@ -461,7 +472,9 @@
    1.60      private boolean generateProperties(
    1.61          Element where,
    1.62          Writer w, Property[] properties,
    1.63 -        Collection<String> props, Map<String,Collection<String>> deps
    1.64 +        Collection<String> props, 
    1.65 +        Map<String,Collection<String>> deps,
    1.66 +        Map<String,Collection<String>> functionDeps
    1.67      ) throws IOException {
    1.68          boolean ok = true;
    1.69          for (Property p : properties) {
    1.70 @@ -498,13 +511,19 @@
    1.71                  w.write("  prop_" + p.name() + " = v;\n");
    1.72                  w.write("  if (ko != null) {\n");
    1.73                  w.write("    ko.valueHasMutated(\"" + p.name() + "\");\n");
    1.74 -                final Collection<String> dependants = deps.get(p.name());
    1.75 +                Collection<String> dependants = deps.get(p.name());
    1.76                  if (dependants != null) {
    1.77                      for (String depProp : dependants) {
    1.78                          w.write("    ko.valueHasMutated(\"" + depProp + "\");\n");
    1.79                      }
    1.80                  }
    1.81                  w.write("  }\n");
    1.82 +                dependants = functionDeps.get(p.name());
    1.83 +                if (dependants != null) {
    1.84 +                    for (String call : dependants) {
    1.85 +                        w.append(call);
    1.86 +                    }
    1.87 +                }
    1.88                  w.write("}\n");
    1.89              }
    1.90              
    1.91 @@ -744,6 +763,68 @@
    1.92          return true;
    1.93      }
    1.94  
    1.95 +    private boolean generateOnChange(Element clazz, Map<String,Collection<String>> propDeps,
    1.96 +        Property[] properties, String className, 
    1.97 +        Map<String, Collection<String>> functionDeps
    1.98 +    ) {
    1.99 +        for (Element m : clazz.getEnclosedElements()) {
   1.100 +            if (m.getKind() != ElementKind.METHOD) {
   1.101 +                continue;
   1.102 +            }
   1.103 +            ExecutableElement e = (ExecutableElement) m;
   1.104 +            OnPropertyChange onPC = e.getAnnotation(OnPropertyChange.class);
   1.105 +            if (onPC == null) {
   1.106 +                continue;
   1.107 +            }
   1.108 +            for (String pn : onPC.value()) {
   1.109 +                if (findProperty(properties, pn) == null && findDerivedFrom(propDeps, pn).isEmpty()) {
   1.110 +                    err().printMessage(Diagnostic.Kind.ERROR, "No property named '" + pn + "' in the model");
   1.111 +                    return false;
   1.112 +                }
   1.113 +            }
   1.114 +            if (!e.getModifiers().contains(Modifier.STATIC)) {
   1.115 +                err().printMessage(
   1.116 +                    Diagnostic.Kind.ERROR, "@OnPropertyChange method needs to be static", e);
   1.117 +                return false;
   1.118 +            }
   1.119 +            if (e.getModifiers().contains(Modifier.PRIVATE)) {
   1.120 +                err().printMessage(
   1.121 +                    Diagnostic.Kind.ERROR, "@OnPropertyChange method cannot be private", e);
   1.122 +                return false;
   1.123 +            }
   1.124 +            if (e.getReturnType().getKind() != TypeKind.VOID) {
   1.125 +                err().printMessage(
   1.126 +                    Diagnostic.Kind.ERROR, "@OnPropertyChange method should return void", e);
   1.127 +                return false;
   1.128 +            }
   1.129 +            String n = e.getSimpleName().toString();
   1.130 +            
   1.131 +            
   1.132 +            for (String pn : onPC.value()) {
   1.133 +                StringBuilder call = new StringBuilder();
   1.134 +                call.append("  ").append(clazz.getSimpleName()).append(".").append(n).append("(");
   1.135 +                call.append(wrapPropName(e, className, "name", pn));
   1.136 +                call.append(");\n");
   1.137 +                
   1.138 +                Collection<String> change = functionDeps.get(pn);
   1.139 +                if (change == null) {
   1.140 +                    change = new ArrayList<>();
   1.141 +                    functionDeps.put(pn, change);
   1.142 +                }
   1.143 +                change.add(call.toString());
   1.144 +                for (String dpn : findDerivedFrom(propDeps, pn)) {
   1.145 +                    change = functionDeps.get(dpn);
   1.146 +                    if (change == null) {
   1.147 +                        change = new ArrayList<>();
   1.148 +                        functionDeps.put(dpn, change);
   1.149 +                    }
   1.150 +                    change.add(call.toString());
   1.151 +                }
   1.152 +            }
   1.153 +        }
   1.154 +        return true;
   1.155 +    }
   1.156 +    
   1.157      private boolean generateReceive(
   1.158          Element clazz, StringWriter body, String className, 
   1.159          List<? extends Element> enclosedElements, List<String> functions
   1.160 @@ -898,6 +979,14 @@
   1.161                      params.append(dataName);
   1.162                      params.append(", null");
   1.163                  } else {
   1.164 +                    if (evName == null) {
   1.165 +                        final StringBuilder sb = new StringBuilder();
   1.166 +                        sb.append("Unexpected string parameter name.");
   1.167 +                        if (dataName != null) {
   1.168 +                            sb.append(" Try \"").append(dataName).append("\"");
   1.169 +                        }
   1.170 +                        err().printMessage(Diagnostic.Kind.ERROR, sb.toString(), ee);
   1.171 +                    }
   1.172                      params.append(evName);
   1.173                      params.append(", \"");
   1.174                      params.append(ve.getSimpleName().toString());
   1.175 @@ -923,6 +1012,42 @@
   1.176          return params;
   1.177      }
   1.178      
   1.179 +    
   1.180 +    private CharSequence wrapPropName(
   1.181 +        ExecutableElement ee, String className, String propName, String propValue
   1.182 +    ) {
   1.183 +        TypeMirror stringType = processingEnv.getElementUtils().getTypeElement("java.lang.String").asType();
   1.184 +        StringBuilder params = new StringBuilder();
   1.185 +        boolean first = true;
   1.186 +        for (VariableElement ve : ee.getParameters()) {
   1.187 +            if (!first) {
   1.188 +                params.append(", ");
   1.189 +            }
   1.190 +            first = false;
   1.191 +            if (ve.asType() == stringType) {
   1.192 +                if (propName != null && ve.getSimpleName().contentEquals(propName)) {
   1.193 +                    params.append('"').append(propValue).append('"');
   1.194 +                } else {
   1.195 +                    err().printMessage(Diagnostic.Kind.ERROR, "Unexpected string parameter name. Try \"" + propName + "\".");
   1.196 +                }
   1.197 +                continue;
   1.198 +            }
   1.199 +            String rn = fqn(ve.asType(), ee);
   1.200 +            int last = rn.lastIndexOf('.');
   1.201 +            if (last >= 0) {
   1.202 +                rn = rn.substring(last + 1);
   1.203 +            }
   1.204 +            if (rn.equals(className)) {
   1.205 +                params.append(className).append(".this");
   1.206 +                continue;
   1.207 +            }
   1.208 +            err().printMessage(Diagnostic.Kind.ERROR,
   1.209 +                "@OnPropertyChange method can only accept String or " + className + " arguments",
   1.210 +                ee);
   1.211 +        }
   1.212 +        return params;
   1.213 +    }
   1.214 +    
   1.215      private boolean isModel(TypeMirror tm) {
   1.216          final Element e = processingEnv.getTypeUtils().asElement(tm);
   1.217          if (e == null) {
   1.218 @@ -1078,4 +1203,14 @@
   1.219              "byte".equals(type) ||
   1.220              "float".equals(type);
   1.221      }
   1.222 +
   1.223 +    private static Collection<String> findDerivedFrom(Map<String, Collection<String>> propsDeps, String derivedProp) {
   1.224 +        Set<String> names = new HashSet<>();
   1.225 +        for (Map.Entry<String, Collection<String>> e : propsDeps.entrySet()) {
   1.226 +            if (e.getValue().contains(derivedProp)) {
   1.227 +                names.add(e.getKey());
   1.228 +            }
   1.229 +        }
   1.230 +        return names;
   1.231 +    }
   1.232  }