json/src/test/java/org/netbeans/html/json/impl/DeepChangeTest.java
author Jaroslav Tulach <jtulach@netbeans.org>
Tue, 26 Aug 2014 18:13:30 +0200
changeset 838 bdc3d696dd4a
parent 790 30f20d9c0986
child 920 117b732d42d0
permissions -rw-r--r--
During the API review process (bug 246133) the reviewers decided that in order to include html4j to NetBeans Platform, we need to stop using org.apidesign namespace and switch to NetBeans one. Repackaging all SPI packages into org.netbeans.html.smthng.spi.
     1 /**
     2  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
     3  *
     4  * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
     5  *
     6  * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
     7  * Other names may be trademarks of their respective owners.
     8  *
     9  * The contents of this file are subject to the terms of either the GNU
    10  * General Public License Version 2 only ("GPL") or the Common
    11  * Development and Distribution License("CDDL") (collectively, the
    12  * "License"). You may not use this file except in compliance with the
    13  * License. You can obtain a copy of the License at
    14  * http://www.netbeans.org/cddl-gplv2.html
    15  * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
    16  * specific language governing permissions and limitations under the
    17  * License.  When distributing the software, include this License Header
    18  * Notice in each file and include the License file at
    19  * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
    20  * particular file as subject to the "Classpath" exception as provided
    21  * by Oracle in the GPL Version 2 section of the License file that
    22  * accompanied this code. If applicable, add the following below the
    23  * License Header, with the fields enclosed by brackets [] replaced by
    24  * your own identifying information:
    25  * "Portions Copyrighted [year] [name of copyright owner]"
    26  *
    27  * Contributor(s):
    28  *
    29  * The Original Software is NetBeans. The Initial Developer of the Original
    30  * Software is Oracle. Portions Copyright 2013-2014 Oracle. All Rights Reserved.
    31  *
    32  * If you wish your version of this file to be governed by only the CDDL
    33  * or only the GPL Version 2, indicate your decision by adding
    34  * "[Contributor] elects to include this software in this distribution
    35  * under the [CDDL or GPL Version 2] license." If you do not indicate a
    36  * single choice of license, a recipient has the option to distribute
    37  * your version of this file under either the CDDL, the GPL Version 2 or
    38  * to extend the choice of license to its licensees as provided above.
    39  * However, if you add GPL Version 2 code and therefore, elected the GPL
    40  * Version 2 license, then the option applies only if the new code is
    41  * made subject to such option by the copyright holder.
    42  */
    43 package org.netbeans.html.json.impl;
    44 
    45 import java.io.IOException;
    46 import java.io.InputStream;
    47 import java.lang.reflect.InvocationTargetException;
    48 import java.util.HashMap;
    49 import java.util.List;
    50 import java.util.Map;
    51 import net.java.html.BrwsrCtx;
    52 import net.java.html.json.ComputedProperty;
    53 import net.java.html.json.Model;
    54 import net.java.html.json.Models;
    55 import net.java.html.json.Property;
    56 import org.netbeans.html.context.spi.Contexts;
    57 import org.netbeans.html.json.spi.FunctionBinding;
    58 import org.netbeans.html.json.spi.JSONCall;
    59 import org.netbeans.html.json.spi.PropertyBinding;
    60 import org.netbeans.html.json.spi.Technology;
    61 import org.netbeans.html.json.spi.Transfer;
    62 import static org.testng.Assert.*;
    63 import org.testng.annotations.BeforeMethod;
    64 import org.testng.annotations.Test;
    65 
    66 /**
    67  *
    68  * @author Jaroslav Tulach
    69  */
    70 public class DeepChangeTest {
    71     private MapTechnology t;
    72     private BrwsrCtx c;
    73 
    74     @BeforeMethod public void initTechnology() {
    75         t = new MapTechnology();
    76         c = Contexts.newBuilder().register(Technology.class, t, 1).
    77             register(Transfer.class, t, 1).build();
    78     }
    79     
    80     @Model(className = "MyX", properties = {
    81         @Property(name = "one", type = MyY.class),
    82         @Property(name = "all", type = MyY.class, array = true)
    83     })
    84     static class X {
    85         @ComputedProperty @Transitive(deep = true) 
    86         static String oneName(MyY one) {
    87             return one.getValue();
    88         }
    89         @ComputedProperty
    90         static String sndName(MyY one) {
    91             return one.getValue().toUpperCase();
    92         }
    93         @ComputedProperty @Transitive(deep = false) 
    94         static String noName(MyY one) {
    95             return one.getValue().toUpperCase();
    96         }
    97         @ComputedProperty @Transitive(deep = true) 
    98         static String thrdName(MyY one) {
    99             return "X" + one.getCount();
   100         }
   101         
   102         @ComputedProperty
   103         static String allNames(List<MyY> all) {
   104             StringBuilder sb = new StringBuilder();
   105             for (MyY y : all) {
   106                 sb.append(y.getValue());
   107             }
   108             return sb.toString();
   109         }
   110 
   111         @ComputedProperty @Transitive(deep = true)
   112         static String firstFromNames(List<MyY> all) {
   113             for (MyY y : all) {
   114                 if (y != null && y.getValue() != null) {
   115                     return y.getValue();
   116                 }
   117             }
   118             return null;
   119         }
   120     }
   121     @Model(className = "MyY", properties = {
   122         @Property(name = "value", type = String.class),
   123         @Property(name = "count", type = int.class)
   124     })
   125     static class Y {
   126     }
   127     @Model(className = "MyOverall", properties = {
   128         @Property(name = "x", type = MyX.class)
   129     })
   130     static class Overall {
   131         @ComputedProperty @Transitive(deep = true) 
   132         static String valueAccross(MyX x) {
   133             return x.getFirstFromNames();
   134         }
   135     }
   136     
   137     @Test public void isTransitiveChangeNotifiedProperly() throws Exception {
   138         MyX p = Models.bind(
   139             new MyX(new MyY("Ahoj", 0), new MyY("Hi", 333), new MyY("Hello", 999)
   140         ), c).applyBindings();
   141         
   142         Map m = (Map)Models.toRaw(p);
   143         Object v = m.get("oneName");
   144         assertNotNull(v, "Value should be in the map");
   145         assertEquals(v.getClass(), One.class, "It is instance of One");
   146         One o = (One)v;
   147         assertEquals(o.changes, 0, "No changes so far");
   148         assertTrue(o.pb.isReadOnly(), "Derived property");
   149         assertEquals(o.get(), "Ahoj");
   150 
   151         p.getOne().setValue("Nazdar");
   152         
   153         assertEquals(o.get(), "Nazdar");
   154         assertEquals(o.changes, 1, "One change so far");
   155     }
   156     
   157     @Test public void isTransitiveChangeInArrayNotifiedProperly() throws Exception {
   158         MyX p = Models.bind(
   159             new MyX(new MyY("Ahoj", 0), new MyY("Hi", 333), new MyY("Hello", 999)
   160         ), c).applyBindings();
   161         
   162         Map m = (Map)Models.toRaw(p);
   163         Object v = m.get("allNames");
   164         assertNotNull(v, "Value should be in the map");
   165         assertEquals(v.getClass(), One.class, "It is instance of One");
   166         One o = (One)v;
   167         assertEquals(o.changes, 0, "No changes so far");
   168         assertTrue(o.pb.isReadOnly(), "Derived property");
   169         assertEquals(o.get(), "HiHello");
   170 
   171         p.getAll().get(0).setValue("Nazdar");
   172         
   173         assertEquals(o.get(), "NazdarHello");
   174         assertEquals(o.changes, 1, "One change so far");
   175     }
   176 
   177     @Test public void addingIntoArray() throws Exception {
   178         MyX p = Models.bind(
   179             new MyX(new MyY("Ahoj", 0), new MyY("Hi", 333), new MyY("Hello", 999)
   180         ), c).applyBindings();
   181         
   182         Map m = (Map)Models.toRaw(p);
   183         Object v = m.get("allNames");
   184         assertNotNull(v, "Value should be in the map");
   185         assertEquals(v.getClass(), One.class, "It is instance of One");
   186         One o = (One)v;
   187         assertEquals(o.changes, 0, "No changes so far");
   188         assertTrue(o.pb.isReadOnly(), "Derived property");
   189         assertEquals(o.get(), "HiHello");
   190 
   191         MyY y = new MyY("Cus", 1);
   192         p.getAll().add(y);
   193         
   194         assertEquals(o.changes, 1, "One change so far");
   195         assertEquals(o.get(), "HiHelloCus");
   196         
   197         y.setValue("Nazdar");
   198         
   199         assertEquals(o.changes, 2, "2nd change so far");
   200         assertEquals(o.get(), "HiHelloNazdar");
   201         
   202         y.setValue("Zdravim");
   203 
   204         assertEquals(o.changes, 3, "3rd change so far");
   205         assertEquals(o.get(), "HiHelloZdravim");
   206     }
   207     
   208     @Test public void firstChangeInArrayNotifiedProperly() throws Exception {
   209         MyX p = Models.bind(
   210             new MyX(new MyY("Ahoj", 0), new MyY("Hi", 333), new MyY("Hello", 999)
   211         ), c).applyBindings();
   212         
   213         Map m = (Map)Models.toRaw(p);
   214         Object v = m.get("firstFromNames");
   215         assertNotNull(v, "Value should be in the map");
   216         assertEquals(v.getClass(), One.class, "It is instance of One");
   217         One o = (One)v;
   218         assertEquals(o.changes, 0, "No changes so far");
   219         assertTrue(o.pb.isReadOnly(), "Derived property");
   220         assertEquals(o.get(), "Hi");
   221 
   222         p.getAll().get(0).setValue("Nazdar");
   223         
   224         assertEquals(o.get(), "Nazdar");
   225         assertEquals(o.changes, 1, "One change so far");
   226     }
   227     
   228     @Test public void firstChangeInArrayToNull() throws Exception {
   229         MyX p = Models.bind(
   230             new MyX(new MyY("Ahoj", 0), new MyY("Hi", 333), new MyY("Hello", 999)
   231         ), c).applyBindings();
   232         
   233         Map m = (Map)Models.toRaw(p);
   234         Object v = m.get("firstFromNames");
   235         assertNotNull(v, "Value should be in the map");
   236         assertEquals(v.getClass(), One.class, "It is instance of One");
   237         One o = (One)v;
   238         assertEquals(o.changes, 0, "No changes so far");
   239         assertTrue(o.pb.isReadOnly(), "Derived property");
   240         assertEquals(o.get(), "Hi");
   241 
   242         p.getAll().get(0).setValue(null);
   243         
   244         assertEquals(o.get(), "Hello");
   245         assertEquals(o.changes, 1, "One change so far");
   246         
   247         p.getAll().get(0).setValue("Nazdar");
   248 
   249         assertEquals(o.get(), "Nazdar");
   250         assertEquals(o.changes, 2, "2nd change so far");
   251     }
   252     
   253     @Test public void firstChangeInArrayNotifiedTransitively() throws Exception {
   254         MyOverall p = Models.bind(
   255             new MyOverall(new MyX(new MyY("Ahoj", 0), new MyY("Hi", 333), new MyY("Hello", 999))
   256         ), c).applyBindings();
   257         
   258         Map m = (Map)Models.toRaw(p);
   259         Object v = m.get("valueAccross");
   260         assertNotNull(v, "Value should be in the map");
   261         assertEquals(v.getClass(), One.class, "It is instance of One");
   262         One o = (One)v;
   263         assertEquals(o.changes, 0, "No changes so far");
   264         assertTrue(o.pb.isReadOnly(), "Derived property");
   265         assertEquals(o.get(), "Hi");
   266 
   267         p.getX().getAll().get(0).setValue("Nazdar");
   268         
   269         assertEquals(o.get(), "Nazdar");
   270         assertEquals(o.changes, 1, "One change so far");
   271     }
   272     
   273     @Test public void secondChangeInArrayIgnored() throws Exception {
   274         MyX p = Models.bind(
   275             new MyX(new MyY("Ahoj", 0), new MyY("Hi", 333), new MyY("Hello", 999)
   276         ), c).applyBindings();
   277         
   278         Map m = (Map)Models.toRaw(p);
   279         Object v = m.get("firstFromNames");
   280         assertNotNull(v, "Value should be in the map");
   281         assertEquals(v.getClass(), One.class, "It is instance of One");
   282         One o = (One)v;
   283         assertEquals(o.changes, 0, "No changes so far");
   284         assertTrue(o.pb.isReadOnly(), "Derived property");
   285         assertEquals(o.get(), "Hi");
   286 
   287         p.getAll().get(1).setValue("Nazdar");
   288         
   289         assertEquals(o.get(), "Hi");
   290         assertEquals(o.changes, 0, "No change so far");
   291     }
   292     
   293     @Test public void changeInArraySizeNeedsToBeRecomputed() throws Exception {
   294         MyX p = Models.bind(
   295             new MyX(new MyY("Ahoj", 0), new MyY("Hi", 333), new MyY("Hello", 999)
   296         ), c).applyBindings();
   297         
   298         Map m = (Map)Models.toRaw(p);
   299         Object v = m.get("firstFromNames");
   300         assertNotNull(v, "Value should be in the map");
   301         assertEquals(v.getClass(), One.class, "It is instance of One");
   302         One o = (One)v;
   303         assertEquals(o.changes, 0, "No changes so far");
   304         assertTrue(o.pb.isReadOnly(), "Derived property");
   305         assertEquals(o.get(), "Hi");
   306 
   307         p.getAll().remove(1);
   308         
   309         assertEquals(o.get(), "Hi");
   310         assertEquals(o.changes, 1, "This required a change");
   311     }
   312     
   313     @Test public void doublePropertyChangeNotified() throws Exception {
   314         MyX p = Models.bind(
   315             new MyX(new MyY("Ahoj", 0), new MyY("Hi", 333), new MyY("Hello", 999)
   316         ), c).applyBindings();
   317         
   318         Map m = (Map)Models.toRaw(p);
   319         Object v = m.get("oneName");
   320         assertNotNull(v, "Value should be in the map");
   321         Object v2 = m.get("sndName");
   322         assertNotNull(v2, "Value2 should be in the map");
   323         One o = (One)v;
   324         One o2 = (One)v2;
   325         assertEquals(o.changes, 0, "No changes so far");
   326         assertEquals(o2.changes, 0, "No changes so far");
   327         assertTrue(o.pb.isReadOnly(), "Derived property");
   328         assertEquals(o.get(), "Ahoj");
   329         assertEquals(o2.get(), "AHOJ");
   330 
   331         p.getOne().setValue("Nazdar");
   332         
   333         assertEquals(o.get(), "Nazdar");
   334         assertEquals(o.changes, 1, "One change so far");
   335         assertEquals(o2.changes, 1, "One change so far");
   336         assertEquals(o2.get(), "NAZDAR");
   337     }
   338     
   339     @Test public void onlyAffectedPropertyChangeNotified() throws Exception {
   340         MyX p = Models.bind(
   341             new MyX(new MyY("Ahoj", 0), new MyY("Hi", 333), new MyY("Hello", 999)
   342         ), c).applyBindings();
   343         
   344         Map m = (Map)Models.toRaw(p);
   345         Object v = m.get("oneName");
   346         assertNotNull(v, "Value should be in the map");
   347         Object v2 = m.get("thrdName");
   348         assertNotNull(v2, "Value2 should be in the map");
   349         One o = (One)v;
   350         One o2 = (One)v2;
   351         assertEquals(o.changes, 0, "No changes so far");
   352         assertEquals(o2.changes, 0, "No changes so far");
   353         assertTrue(o.pb.isReadOnly(), "Derived property");
   354         assertEquals(o.get(), "Ahoj");
   355         assertEquals(o2.get(), "X0");
   356 
   357         p.getOne().setCount(10);
   358         
   359         assertEquals(o.get(), "Ahoj");
   360         assertEquals(o.changes, 0, "Still no change");
   361         assertEquals(o2.changes, 1, "One change so far");
   362         assertEquals(o2.get(), "X10");
   363     }
   364     
   365     @Test public void onlyDeepPropsAreNotified() throws Exception {
   366         MyX p = Models.bind(
   367             new MyX(new MyY("Ahoj", 0), new MyY("Hi", 333), new MyY("Hello", 999)
   368         ), c).applyBindings();
   369         
   370         Map m = (Map)Models.toRaw(p);
   371         Object v = m.get("oneName");
   372         assertNotNull(v, "Value should be in the map");
   373         Object v2 = m.get("noName");
   374         assertNotNull(v2, "Value2 should be in the map");
   375         One o = (One)v;
   376         One o2 = (One)v2;
   377         assertEquals(o.changes, 0, "No changes so far");
   378         assertEquals(o2.changes, 0, "No changes so far");
   379         assertTrue(o.pb.isReadOnly(), "Derived property");
   380         assertEquals(o.get(), "Ahoj");
   381         assertEquals(o2.get(), "AHOJ");
   382 
   383         p.getOne().setValue("Nazdar");
   384         
   385         assertEquals(o.get(), "Nazdar");
   386         assertEquals(o.changes, 1, "One change so far");
   387         assertEquals(o2.changes, 0, "This change is not noticed");
   388         assertEquals(o2.get(), "NAZDAR", "but property value changes when computed");
   389     }
   390 
   391     static final class One {
   392 
   393         int changes;
   394         final PropertyBinding pb;
   395         final FunctionBinding fb;
   396 
   397         One(Object m, PropertyBinding pb) throws NoSuchMethodException {
   398             this.pb = pb;
   399             this.fb = null;
   400         }
   401 
   402         One(Object m, FunctionBinding fb) throws NoSuchMethodException {
   403             this.pb = null;
   404             this.fb = fb;
   405         }
   406 
   407         Object get() throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {
   408             return pb.getValue();
   409         }
   410 
   411         void set(Object v) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {
   412             pb.setValue(v);
   413         }
   414     }
   415 
   416     static final class MapTechnology
   417             implements Technology<Map<String, One>>, Transfer {
   418 
   419         @Override
   420         public Map<String, One> wrapModel(Object model) {
   421             return new HashMap<String, One>();
   422         }
   423 
   424         @Override
   425         public void valueHasMutated(Map<String, One> data, String propertyName) {
   426             One p = data.get(propertyName);
   427             if (p != null) {
   428                 p.changes++;
   429             }
   430         }
   431 
   432         @Override
   433         public void bind(PropertyBinding b, Object model, Map<String, One> data) {
   434             try {
   435                 One o = new One(model, b);
   436                 data.put(b.getPropertyName(), o);
   437             } catch (NoSuchMethodException ex) {
   438                 throw new IllegalStateException(ex);
   439             }
   440         }
   441 
   442         @Override
   443         public void expose(FunctionBinding fb, Object model, Map<String, One> data) {
   444             try {
   445                 data.put(fb.getFunctionName(), new One(model, fb));
   446             } catch (NoSuchMethodException ex) {
   447                 throw new IllegalStateException(ex);
   448             }
   449         }
   450 
   451         @Override
   452         public void applyBindings(Map<String, One> data) {
   453         }
   454 
   455         @Override
   456         public Object wrapArray(Object[] arr) {
   457             return arr;
   458         }
   459 
   460         @Override
   461         public void extract(Object obj, String[] props, Object[] values) {
   462             Map<?, ?> map = obj instanceof Map ? (Map<?, ?>) obj : null;
   463             for (int i = 0; i < Math.min(props.length, values.length); i++) {
   464                 if (map == null) {
   465                     values[i] = null;
   466                 } else {
   467                     values[i] = map.get(props[i]);
   468                     if (values[i] instanceof One) {
   469                         values[i] = ((One) values[i]).pb.getValue();
   470                     }
   471                 }
   472             }
   473         }
   474 
   475         @Override
   476         public void loadJSON(JSONCall call) {
   477             call.notifyError(new UnsupportedOperationException());
   478         }
   479 
   480         @Override
   481         public <M> M toModel(Class<M> modelClass, Object data) {
   482             return modelClass.cast(data);
   483         }
   484 
   485         @Override
   486         public Object toJSON(InputStream is) throws IOException {
   487             throw new IOException();
   488         }
   489 
   490         @Override
   491         public void runSafe(Runnable r) {
   492             throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
   493         }
   494     }
   495     
   496 }