Removing trailing spaces and ensuring that if a URL parameter is used multiple times, it generates just a single method parameter
authorJaroslav Tulach <jtulach@netbeans.org>
Wed, 20 May 2015 06:27:27 +0200
changeset 93822997afdeb75
parent 937 64f89b8b7244
child 939 a7bec1c91507
Removing trailing spaces and ensuring that if a URL parameter is used multiple times, it generates just a single method parameter
json/src/main/java/org/netbeans/html/json/impl/ModelProcessor.java
json/src/test/java/net/java/html/json/ModelTest.java
     1.1 --- a/json/src/main/java/org/netbeans/html/json/impl/ModelProcessor.java	Wed May 13 10:21:37 2015 +0300
     1.2 +++ b/json/src/main/java/org/netbeans/html/json/impl/ModelProcessor.java	Wed May 20 06:27:27 2015 +0200
     1.3 @@ -91,8 +91,8 @@
     1.4  import javax.tools.Diagnostic;
     1.5  import javax.tools.FileObject;
     1.6  import net.java.html.json.ComputedProperty;
     1.7 +import net.java.html.json.Function;
     1.8  import net.java.html.json.Model;
     1.9 -import net.java.html.json.Function;
    1.10  import net.java.html.json.ModelOperation;
    1.11  import net.java.html.json.OnPropertyChange;
    1.12  import net.java.html.json.OnReceive;
    1.13 @@ -170,7 +170,7 @@
    1.14      private void error(String msg, Element e) {
    1.15          processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, msg, e);
    1.16      }
    1.17 -    
    1.18 +
    1.19      private boolean processModel(Element e) {
    1.20          boolean ok = true;
    1.21          Model m = e.getAnnotation(Model.class);
    1.22 @@ -189,7 +189,7 @@
    1.23              Map<String, Collection<String[]>> propsDeps = new HashMap<String, Collection<String[]>>();
    1.24              Map<String, Collection<String>> functionDeps = new HashMap<String, Collection<String>>();
    1.25              Prprt[] props = createProps(e, m.properties());
    1.26 -            
    1.27 +
    1.28              if (!generateComputedProperties(body, props, e.getEnclosedElements(), propsGetSet, propsDeps)) {
    1.29                  ok = false;
    1.30              }
    1.31 @@ -381,7 +381,7 @@
    1.32                          w.append("        case " + (i / 2) + ":\n"); // model." + name + "(data, ev); return;\n");
    1.33                          w.append("          ").append(call).append("\n");
    1.34                          w.append("          return;\n");
    1.35 -                        
    1.36 +
    1.37                      }
    1.38                  }
    1.39                  w.append("      }\n");
    1.40 @@ -550,11 +550,11 @@
    1.41          }
    1.42          return ok;
    1.43      }
    1.44 -    
    1.45 +
    1.46      private boolean generateProperties(
    1.47          Element where,
    1.48          Writer w, String className, Prprt[] properties,
    1.49 -        List<GetSet> props, 
    1.50 +        List<GetSet> props,
    1.51          Map<String,Collection<String[]>> deps,
    1.52          Map<String,Collection<String>> functionDeps
    1.53      ) throws IOException {
    1.54 @@ -564,10 +564,10 @@
    1.55              tn = typeName(where, p);
    1.56              String[] gs = toGetSet(p.name(), tn, p.array());
    1.57              String castTo;
    1.58 -            
    1.59 +
    1.60              if (p.array()) {
    1.61                  w.write("  private final java.util.List<" + tn + "> prop_" + p.name() + ";\n");
    1.62 -            
    1.63 +
    1.64                  castTo = "java.util.List";
    1.65                  w.write("  public java.util.List<" + tn + "> " + gs[0] + "() {\n");
    1.66                  w.write("    proto.accessProperty(\"" + p.name() + "\");\n");
    1.67 @@ -590,7 +590,7 @@
    1.68                      Collection<String[]> dependants = deps.get(p.name());
    1.69                      if (dependants != null) {
    1.70                          for (String[] pair : dependants) {
    1.71 -                            w.write("    proto.valueHasMutated(\"" + pair[0] + "\", null, " + pair[1] + "());\n"); 
    1.72 +                            w.write("    proto.valueHasMutated(\"" + pair[0] + "\", null, " + pair[1] + "());\n");
    1.73                          }
    1.74                      }
    1.75                  }
    1.76 @@ -605,14 +605,14 @@
    1.77                  }
    1.78                  w.write("  }\n");
    1.79              }
    1.80 -            
    1.81 +
    1.82              for (int i = 0; i < props.size(); i++) {
    1.83                  if (props.get(i).name.equals(p.name())) {
    1.84                      error("Cannot have the name " + p.name() + " defined twice", where);
    1.85                      ok = false;
    1.86                  }
    1.87              }
    1.88 -            
    1.89 +
    1.90              props.add(new GetSet(
    1.91                  p.name(),
    1.92                  gs[0],
    1.93 @@ -657,9 +657,9 @@
    1.94              } else {
    1.95                  toCheck = rt;
    1.96              }
    1.97 -            
    1.98 +
    1.99              final String sn = ee.getSimpleName().toString();
   1.100 -            
   1.101 +
   1.102              if (toCheck.getKind().isPrimitive()) {
   1.103                  // OK
   1.104              } else {
   1.105 @@ -682,9 +682,9 @@
   1.106                      }
   1.107                  }
   1.108              }
   1.109 -            
   1.110 +
   1.111              String[] gs = toGetSet(sn, tn, array);
   1.112 -            
   1.113 +
   1.114              w.write("  public " + tn);
   1.115              if (array) {
   1.116                  w.write("<" + toCheck + ">");
   1.117 @@ -694,7 +694,7 @@
   1.118              boolean deep = false;
   1.119              for (VariableElement pe : ee.getParameters()) {
   1.120                  final String dn = pe.getSimpleName().toString();
   1.121 -                
   1.122 +
   1.123                  if (!verifyPropName(pe, dn, fixedProps)) {
   1.124                      ok = false;
   1.125                  }
   1.126 @@ -712,7 +712,7 @@
   1.127                  String[] call = toGetSet(dn, dt, false);
   1.128                  w.write("    " + dt + " arg" + (++arg) + " = ");
   1.129                  w.write(call[0] + "();\n");
   1.130 -                
   1.131 +
   1.132                  Collection<String[]> depends = deps.get(dn);
   1.133                  if (depends == null) {
   1.134                      depends = new LinkedHashSet<String[]>();
   1.135 @@ -750,7 +750,7 @@
   1.136                  true
   1.137              ));
   1.138          }
   1.139 -        
   1.140 +
   1.141          return ok;
   1.142      }
   1.143  
   1.144 @@ -762,7 +762,7 @@
   1.145              pref = "is";
   1.146          }
   1.147          if (array) {
   1.148 -            return new String[] { 
   1.149 +            return new String[] {
   1.150                  pref + n,
   1.151                  null,
   1.152                  "a" + n,
   1.153 @@ -770,8 +770,8 @@
   1.154              };
   1.155          }
   1.156          return new String[]{
   1.157 -            pref + n, 
   1.158 -            "set" + n, 
   1.159 +            pref + n,
   1.160 +            "set" + n,
   1.161              "a" + n,
   1.162              ""
   1.163          };
   1.164 @@ -791,7 +791,7 @@
   1.165          }
   1.166          return ret;
   1.167      }
   1.168 -    
   1.169 +
   1.170      private static String findBoxedType(String ret) {
   1.171          if (ret.equals("boolean")) {
   1.172              return Boolean.class.getName();
   1.173 @@ -850,7 +850,7 @@
   1.174      }
   1.175  
   1.176      private boolean generateFunctions(
   1.177 -        Element clazz, StringWriter body, String className, 
   1.178 +        Element clazz, StringWriter body, String className,
   1.179          List<? extends Element> enclosedElements, List<Object> functions
   1.180      ) {
   1.181          for (Element m : enclosedElements) {
   1.182 @@ -882,7 +882,7 @@
   1.183      }
   1.184  
   1.185      private boolean generateOnChange(Element clazz, Map<String,Collection<String[]>> propDeps,
   1.186 -        Prprt[] properties, String className, 
   1.187 +        Prprt[] properties, String className,
   1.188          Map<String, Collection<String>> functionDeps
   1.189      ) {
   1.190          for (Element m : clazz.getEnclosedElements()) {
   1.191 @@ -913,14 +913,14 @@
   1.192                  return false;
   1.193              }
   1.194              String n = e.getSimpleName().toString();
   1.195 -            
   1.196 -            
   1.197 +
   1.198 +
   1.199              for (String pn : onPC.value()) {
   1.200                  StringBuilder call = new StringBuilder();
   1.201                  call.append("  ").append(clazz.getSimpleName()).append(".").append(n).append("(");
   1.202                  call.append(wrapPropName(e, className, "name", pn));
   1.203                  call.append(");\n");
   1.204 -                
   1.205 +
   1.206                  Collection<String> change = functionDeps.get(pn);
   1.207                  if (change == null) {
   1.208                      change = new ArrayList<String>();
   1.209 @@ -940,8 +940,8 @@
   1.210          return true;
   1.211      }
   1.212  
   1.213 -    private boolean generateOperation(Element clazz, 
   1.214 -        StringWriter body, String className, 
   1.215 +    private boolean generateOperation(Element clazz,
   1.216 +        StringWriter body, String className,
   1.217          List<? extends Element> enclosedElements,
   1.218          List<Object> functions
   1.219      ) {
   1.220 @@ -1022,14 +1022,14 @@
   1.221                  call.append("); }");
   1.222                  functions.add(call.toString());
   1.223              }
   1.224 -            
   1.225 +
   1.226          }
   1.227          return true;
   1.228      }
   1.229 -    
   1.230 -    
   1.231 +
   1.232 +
   1.233      private boolean generateReceive(
   1.234 -        Element clazz, StringWriter body, String className, 
   1.235 +        Element clazz, StringWriter body, String className,
   1.236          List<? extends Element> enclosedElements, StringBuilder inType
   1.237      ) {
   1.238          inType.append("  @Override public void onMessage(").append(className).append(" model, int index, int type, Object data, Object[] params) {\n");
   1.239 @@ -1088,9 +1088,9 @@
   1.240                  } else {
   1.241                      error("First parameter needs to be " + className, e);
   1.242                      return false;
   1.243 -                }                    
   1.244 +                }
   1.245              }
   1.246 -                
   1.247 +
   1.248              String modelClass;
   1.249              {
   1.250                  final Types tu = processingEnv.getTypeUtils();
   1.251 @@ -1152,7 +1152,7 @@
   1.252                  }
   1.253                  if (!skipJSONP) {
   1.254                      error(
   1.255 -                        "Name of jsonp attribute ('" + onR.jsonp() + 
   1.256 +                        "Name of jsonp attribute ('" + onR.jsonp() +
   1.257                          "') is not used in url attribute '" + onR.url() + "'", e
   1.258                      );
   1.259                  }
   1.260 @@ -1164,7 +1164,7 @@
   1.261                          error("@OnReceive(method=\"WebSocket\") can only have two arguments", e);
   1.262                          return false;
   1.263                      }
   1.264 -                    
   1.265 +
   1.266                      VariableElement ve = e.getParameters().get(i);
   1.267                      body.append(sep).append(ve.asType().toString()).append(" ").append(ve.getSimpleName());
   1.268                      final String tp = ve.asType().toString();
   1.269 @@ -1270,7 +1270,7 @@
   1.270          method.append(");\n");
   1.271          return false;
   1.272      }
   1.273 -    
   1.274 +
   1.275      private boolean generateWSReceiveBody(int index, StringWriter method, StringBuilder body, OnReceive onR, ExecutableElement e, Element clazz, String className, boolean expectsList, String modelClass, String n, List<String> args, List<String> params, StringBuilder urlBefore, String jsonpVarName, StringBuilder urlAfter, String dataMirror) {
   1.276          body.append(
   1.277              "    case " + index + ": {\n" +
   1.278 @@ -1469,8 +1469,8 @@
   1.279          }
   1.280          return params;
   1.281      }
   1.282 -    
   1.283 -    
   1.284 +
   1.285 +
   1.286      private CharSequence wrapPropName(
   1.287          ExecutableElement ee, String className, String propName, String propValue
   1.288      ) {
   1.289 @@ -1505,7 +1505,7 @@
   1.290          }
   1.291          return params;
   1.292      }
   1.293 -    
   1.294 +
   1.295      private boolean isModel(TypeMirror tm) {
   1.296          if (tm.getKind() == TypeKind.ERROR) {
   1.297              return true;
   1.298 @@ -1524,7 +1524,7 @@
   1.299          }
   1.300          return models.values().contains(e.getSimpleName().toString());
   1.301      }
   1.302 -    
   1.303 +
   1.304      private void writeToString(Prprt[] props, Writer w) throws IOException {
   1.305          w.write("  public String toString() {\n");
   1.306          w.write("    StringBuilder sb = new StringBuilder();\n");
   1.307 @@ -1563,7 +1563,7 @@
   1.308                  w.write("    proto.cloneList(ret.prop_" + p.name() + ", ctx, prop_" + p.name() + ");\n");
   1.309              }
   1.310          }
   1.311 -        
   1.312 +
   1.313          w.write("    return ret;\n");
   1.314          w.write("  }\n");
   1.315      }
   1.316 @@ -1618,7 +1618,7 @@
   1.317              isEnum[0] = false;
   1.318              return e.getSimpleName().toString();
   1.319          }
   1.320 -        
   1.321 +
   1.322          final Model m = e == null ? null : e.getAnnotation(Model.class);
   1.323          String ret;
   1.324          if (m != null) {
   1.325 @@ -1636,7 +1636,7 @@
   1.326          isEnum[0] = processingEnv.getTypeUtils().isSubtype(tm, enm);
   1.327          return ret;
   1.328      }
   1.329 -    
   1.330 +
   1.331      private static boolean findModelForMthd(Element clazz) {
   1.332          if (clazz == null) {
   1.333              return false;
   1.334 @@ -1658,7 +1658,7 @@
   1.335      private Iterable<String> findParamNames(
   1.336          Element e, String url, String jsonParam, StringBuilder... both
   1.337      ) {
   1.338 -        List<String> params = new ArrayList<String>();
   1.339 +        Set<String> params = new LinkedHashSet<String>();
   1.340          int wasJSON = 0;
   1.341  
   1.342          for (int pos = 0; ;) {
   1.343 @@ -1675,16 +1675,17 @@
   1.344                  return params;
   1.345              }
   1.346              final String paramName = url.substring(next + 1, close);
   1.347 -            params.add(paramName);
   1.348 -            if (paramName.equals(jsonParam) && !jsonParam.isEmpty()) {
   1.349 -                both[wasJSON].append('"')
   1.350 -                    .append(url.substring(pos, next))
   1.351 -                    .append('"');
   1.352 -                wasJSON = 1;
   1.353 -            } else {
   1.354 -                both[wasJSON].append('"')
   1.355 -                    .append(url.substring(pos, next))
   1.356 -                    .append("\" + ").append(paramName).append(" + ");
   1.357 +            if (params.add(paramName)) {
   1.358 +                if (paramName.equals(jsonParam) && !jsonParam.isEmpty()) {
   1.359 +                    both[wasJSON].append('"')
   1.360 +                        .append(url.substring(pos, next))
   1.361 +                        .append('"');
   1.362 +                    wasJSON = 1;
   1.363 +                } else {
   1.364 +                    both[wasJSON].append('"')
   1.365 +                        .append(url.substring(pos, next))
   1.366 +                        .append("\" + ").append(paramName).append(" + ");
   1.367 +                }
   1.368              }
   1.369              pos = close + 1;
   1.370          }
   1.371 @@ -1700,7 +1701,7 @@
   1.372      }
   1.373  
   1.374      private boolean isPrimitive(String type) {
   1.375 -        return 
   1.376 +        return
   1.377              "int".equals(type) ||
   1.378              "double".equals(type) ||
   1.379              "long".equals(type) ||
   1.380 @@ -1723,7 +1724,7 @@
   1.381          }
   1.382          return names;
   1.383      }
   1.384 -    
   1.385 +
   1.386      private Prprt[] createProps(Element e, Property[] arr) {
   1.387          Prprt[] ret = Prprt.wrap(processingEnv, e, arr);
   1.388          Prprt[] prev = verify.put(e, ret);
   1.389 @@ -1732,7 +1733,7 @@
   1.390          }
   1.391          return ret;
   1.392      }
   1.393 -    
   1.394 +
   1.395      private static String strip(String s) {
   1.396          int indx = s.indexOf("__");
   1.397          if (indx >= 0) {
   1.398 @@ -1758,7 +1759,7 @@
   1.399          } catch (Exception ex) {
   1.400              // fallback
   1.401          }
   1.402 -        
   1.403 +
   1.404          AnnotationMirror found = null;
   1.405          for (AnnotationMirror am : e.getAnnotationMirrors()) {
   1.406              if (am.getAnnotationType().toString().equals(OnReceive.class.getName())) {
   1.407 @@ -1768,7 +1769,7 @@
   1.408          if (found == null) {
   1.409              return null;
   1.410          }
   1.411 -        
   1.412 +
   1.413          for (Map.Entry<? extends ExecutableElement, ? extends AnnotationValue> entry : found.getElementValues().entrySet()) {
   1.414              ExecutableElement ee = entry.getKey();
   1.415              AnnotationValue av = entry.getValue();
   1.416 @@ -1827,11 +1828,11 @@
   1.417              this.tm = tm;
   1.418              this.p = p;
   1.419          }
   1.420 -        
   1.421 +
   1.422          String name() {
   1.423              return p.name();
   1.424          }
   1.425 -        
   1.426 +
   1.427          boolean array() {
   1.428              return p.array();
   1.429          }
   1.430 @@ -1853,13 +1854,13 @@
   1.431              }
   1.432              throw ex;
   1.433          }
   1.434 -        
   1.435 -        
   1.436 +
   1.437 +
   1.438          static Prprt[] wrap(ProcessingEnvironment pe, Element e, Property[] arr) {
   1.439              if (arr.length == 0) {
   1.440                  return new Prprt[0];
   1.441              }
   1.442 -            
   1.443 +
   1.444              if (e.getKind() != ElementKind.CLASS) {
   1.445                  throw new IllegalStateException("" + e.getKind());
   1.446              }
   1.447 @@ -1881,12 +1882,12 @@
   1.448              BIG: for (int i = 0; i < ret.length; i++) {
   1.449                  AnnotationMirror am = (AnnotationMirror)val.get(i).getValue();
   1.450                  ret[i] = new Prprt(e, am, arr[i]);
   1.451 -                
   1.452 +
   1.453              }
   1.454              return ret;
   1.455          }
   1.456      } // end of Prprt
   1.457 -    
   1.458 +
   1.459      private static final class GetSet {
   1.460          final String name;
   1.461          final String getter;
   1.462 @@ -1922,15 +1923,15 @@
   1.463                  );
   1.464              }
   1.465          }
   1.466 -        
   1.467 +
   1.468          return super.getCompletions(element, annotation, member, userText);
   1.469      }
   1.470 -    
   1.471 +
   1.472      private static final Completion methodOf(String method) {
   1.473          ResourceBundle rb = ResourceBundle.getBundle("org.netbeans.html.json.impl.Bundle");
   1.474          return Completions.of('"' + method + '"', rb.getString("MSG_Completion_" + method));
   1.475      }
   1.476 -    
   1.477 +
   1.478      private boolean findOnError(ExecutableElement errElem, TypeElement te, String name, String className) {
   1.479          String err = null;
   1.480          METHODS:
   1.481 @@ -1978,5 +1979,5 @@
   1.482          error(err, errElem);
   1.483          return false;
   1.484      }
   1.485 -    
   1.486 +
   1.487  }
     2.1 --- a/json/src/test/java/net/java/html/json/ModelTest.java	Wed May 13 10:21:37 2015 +0300
     2.2 +++ b/json/src/test/java/net/java/html/json/ModelTest.java	Wed May 20 06:27:27 2015 +0200
     2.3 @@ -42,17 +42,22 @@
     2.4   */
     2.5  package net.java.html.json;
     2.6  
     2.7 -import net.java.html.BrwsrCtx;
     2.8  import java.util.ArrayList;
     2.9  import java.util.Collections;
    2.10  import java.util.Iterator;
    2.11  import java.util.List;
    2.12  import java.util.ListIterator;
    2.13 +import net.java.html.BrwsrCtx;
    2.14  import org.netbeans.html.context.spi.Contexts;
    2.15  import org.netbeans.html.json.spi.FunctionBinding;
    2.16  import org.netbeans.html.json.spi.PropertyBinding;
    2.17  import org.netbeans.html.json.spi.Technology;
    2.18 -import static org.testng.Assert.*;
    2.19 +import static org.testng.Assert.assertEquals;
    2.20 +import static org.testng.Assert.assertFalse;
    2.21 +import static org.testng.Assert.assertNotNull;
    2.22 +import static org.testng.Assert.assertNull;
    2.23 +import static org.testng.Assert.assertTrue;
    2.24 +import static org.testng.Assert.fail;
    2.25  import org.testng.annotations.BeforeMethod;
    2.26  import org.testng.annotations.Test;
    2.27  
    2.28 @@ -73,37 +78,37 @@
    2.29      private MockTechnology my;
    2.30      private Modelik model;
    2.31      private static Modelik leakedModel;
    2.32 -    
    2.33 +
    2.34      @BeforeMethod
    2.35      public void createModel() {
    2.36          my = new MockTechnology();
    2.37          final BrwsrCtx c = Contexts.newBuilder().register(Technology.class, my, 1).build();
    2.38          model = Models.bind(new Modelik(), c);
    2.39      }
    2.40 -    
    2.41 +
    2.42      @Test public void classGeneratedWithSetterGetter() {
    2.43          model.setValue(10);
    2.44          assertEquals(10, model.getValue(), "Value changed");
    2.45      }
    2.46 -    
    2.47 +
    2.48      @Test public void computedMethod() {
    2.49          model.setValue(4);
    2.50          assertEquals(16, model.getPowerValue());
    2.51      }
    2.52 -    
    2.53 +
    2.54      @Test public void equalsAndHashCode() {
    2.55          Modelik m1 = new Modelik(10, 20, 30, "changed", "firstName");
    2.56          Modelik m2 = new Modelik(10, 20, 30, "changed", "firstName");
    2.57 -        
    2.58 +
    2.59          assertTrue(m1.equals(m2), "They are the same");
    2.60          assertEquals(m1.hashCode(), m2.hashCode(), "Hashcode is the same");
    2.61 -        
    2.62 +
    2.63          m1.setCount(33);
    2.64 -        
    2.65 +
    2.66          assertFalse(m1.equals(m2), "No longer the same");
    2.67          assertFalse(m1.hashCode() == m2.hashCode(), "No longe is hashcode is the same");
    2.68      }
    2.69 -    
    2.70 +
    2.71      @Test public void arrayIsMutable() {
    2.72          assertEquals(model.getNames().size(), 0, "Is empty");
    2.73          model.getNames().add("Jarda");
    2.74 @@ -117,49 +122,49 @@
    2.75          assertTrue(my.mutated.isEmpty(), "No change still " + my.mutated);
    2.76          assertTrue(model.getNames().isEmpty(), "No empty");
    2.77      }
    2.78 -    
    2.79 +
    2.80      @Test public void arrayChangesNotified() {
    2.81          Models.applyBindings(model);
    2.82          model.getNames().add("Hello");
    2.83 -        
    2.84 +
    2.85          assertFalse(my.mutated.isEmpty(), "There was a change" + my.mutated);
    2.86          assertTrue(my.mutated.contains("names"), "Change in names property: " + my.mutated);
    2.87  
    2.88          my.mutated.clear();
    2.89 -        
    2.90 +
    2.91          Iterator<String> it = model.getNames().iterator();
    2.92          assertEquals(it.next(), "Hello");
    2.93          it.remove();
    2.94 -        
    2.95 +
    2.96          assertFalse(my.mutated.isEmpty(), "There was a change" + my.mutated);
    2.97          assertTrue(my.mutated.contains("names"), "Change in names property: " + my.mutated);
    2.98  
    2.99          my.mutated.clear();
   2.100 -        
   2.101 +
   2.102          ListIterator<String> lit = model.getNames().listIterator();
   2.103          lit.add("Jarda");
   2.104 -        
   2.105 +
   2.106          assertFalse(my.mutated.isEmpty(), "There was a change" + my.mutated);
   2.107          assertTrue(my.mutated.contains("names"), "Change in names property: " + my.mutated);
   2.108      }
   2.109  
   2.110      @Test public void autoboxedArray() {
   2.111          model.getValues().add(10);
   2.112 -        
   2.113 +
   2.114          assertEquals(model.getValues().get(0), Integer.valueOf(10), "Really ten");
   2.115      }
   2.116  
   2.117      @Test public void derivedArrayProp() {
   2.118          model.applyBindings();
   2.119          model.setCount(10);
   2.120 -        
   2.121 +
   2.122          List<String> arr = model.getRepeat();
   2.123          assertEquals(arr.size(), 10, "Ten items: " + arr);
   2.124 -        
   2.125 +
   2.126          my.mutated.clear();
   2.127 -        
   2.128 +
   2.129          model.setCount(5);
   2.130 -        
   2.131 +
   2.132          arr = model.getRepeat();
   2.133          assertEquals(arr.size(), 5, "Five items: " + arr);
   2.134  
   2.135 @@ -167,24 +172,24 @@
   2.136          assertTrue(my.mutated.contains("repeat"), "Array is in there: " + my.mutated);
   2.137          assertTrue(my.mutated.contains("count"), "Count is in there: " + my.mutated);
   2.138      }
   2.139 -    
   2.140 +
   2.141      @Test public void derivedPropertiesAreNotified() {
   2.142          model.applyBindings();
   2.143 -        
   2.144 +
   2.145          model.setValue(33);
   2.146 -        
   2.147 +
   2.148          // not interested in change of this property
   2.149          my.mutated.remove("changedProperty");
   2.150 -        
   2.151 +
   2.152          assertEquals(my.mutated.size(), 2, "Two properties changed: " + my.mutated);
   2.153          assertTrue(my.mutated.contains("powerValue"), "Power value is in there: " + my.mutated);
   2.154          assertTrue(my.mutated.contains("value"), "Simple value is in there: " + my.mutated);
   2.155 -        
   2.156 +
   2.157          my.mutated.clear();
   2.158 -        
   2.159 +
   2.160          model.setUnrelated(44);
   2.161 -        
   2.162 -        
   2.163 +
   2.164 +
   2.165          // not interested in change of this property
   2.166          my.mutated.remove("changedProperty");
   2.167          assertEquals(my.mutated.size(), 1, "One property changed: " + my.mutated);
   2.168 @@ -210,16 +215,16 @@
   2.169              // OK, we can't read
   2.170          }
   2.171      }
   2.172 -    
   2.173 +
   2.174      @OnReceive(url = "{protocol}://{host}?query={query}", data = Person.class, onError = "errorState")
   2.175      static void loadPeople(Modelik thiz, People p) {
   2.176          Modelik m = null;
   2.177          m.applyBindings();
   2.178          m.loadPeople("http", "apidesign.org", "query", new Person());
   2.179      }
   2.180 -    
   2.181 +
   2.182      static void errorState(Modelik thiz, Exception ex) {
   2.183 -        
   2.184 +
   2.185      }
   2.186  
   2.187      @OnReceive(url = "{protocol}://{host}?callback={back}&query={query}", jsonp = "back")
   2.188 @@ -228,16 +233,21 @@
   2.189          m.applyBindings();
   2.190          m.loadPeopleViaJSONP("http", "apidesign.org", "query");
   2.191      }
   2.192 -    
   2.193 -    @Function 
   2.194 +
   2.195 +    @OnReceive(url = "{rep}://{rep}")
   2.196 +    static void repeatedTest(Modelik thiz, People p) {
   2.197 +        thiz.repeatedTest("justOneParameterRep");
   2.198 +    }
   2.199 +
   2.200 +    @Function
   2.201      static void doSomething() {
   2.202      }
   2.203 -    
   2.204 +
   2.205      @ComputedProperty
   2.206      static int powerValue(int value) {
   2.207          return value * value;
   2.208      }
   2.209 -    
   2.210 +
   2.211      @OnPropertyChange({ "powerValue", "unrelated" })
   2.212      static void aPropertyChanged(Modelik m, String name) {
   2.213          m.setChangedProperty(name);
   2.214 @@ -247,7 +257,7 @@
   2.215      static void anArrayPropertyChanged(String name, Modelik m) {
   2.216          m.setChangedProperty(name);
   2.217      }
   2.218 -    
   2.219 +
   2.220      @Test public void changeAnything() {
   2.221          model.setCount(44);
   2.222          assertNull(model.getChangedProperty(), "No observed value change");
   2.223 @@ -268,7 +278,7 @@
   2.224          model.getValues().add(10);
   2.225          assertEquals(model.getChangedProperty(), "values", "Something added into the array");
   2.226      }
   2.227 -    
   2.228 +
   2.229      @ComputedProperty
   2.230      static String notAllowedRead() {
   2.231          return "Not allowed callback: " + leakedModel.getUnrelated();
   2.232 @@ -279,12 +289,12 @@
   2.233          leakedModel.setUnrelated(11);
   2.234          return "Not allowed callback!";
   2.235      }
   2.236 -    
   2.237 +
   2.238      @ComputedProperty
   2.239      static List<String> repeat(int count) {
   2.240          return Collections.nCopies(count, "Hello");
   2.241      }
   2.242 -    
   2.243 +
   2.244      public @Test void hasPersonPropertyAndComputedFullName() {
   2.245          List<Person> arr = model.getPeople();
   2.246          assertEquals(arr.size(), 0, "By default empty");
   2.247 @@ -294,7 +304,7 @@
   2.248              assertNotNull(fullNameGenerated);
   2.249          }
   2.250      }
   2.251 -    
   2.252 +
   2.253      public @Test void computedListIsOfTypeString() {
   2.254          Person p = new Person("1st", "2nd", Sex.MALE);
   2.255          String first = p.getBothNames().get(0);
   2.256 @@ -302,7 +312,7 @@
   2.257          assertEquals(first, "1st");
   2.258          assertEquals(last, "2nd");
   2.259      }
   2.260 -    
   2.261 +
   2.262      private static class MockTechnology implements Technology<Object> {
   2.263          private final List<String> mutated = new ArrayList<String>();
   2.264