Bringing the beans branch up-to-date with release-1.2.3 beans
authorJaroslav Tulach <jtulach@netbeans.org>
Fri, 12 Feb 2016 08:40:12 +0100
branchbeans
changeset 103605139f7b3629
parent 992 6f1a8b251b7d
parent 814 8573ad9106a2
Bringing the beans branch up-to-date with release-1.2.3
json-beans/src/test/java/net/java/html/beans/MapModelGCTest.java
json-beans/src/test/java/net/java/html/beans/MapModelTest.java
json/src/main/java/org/netbeans/html/json/spi/Proto.java
pom.xml
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/json-beans/pom.xml	Fri Feb 12 08:40:12 2016 +0100
     1.3 @@ -0,0 +1,24 @@
     1.4 +<?xml version="1.0" encoding="UTF-8"?>
     1.5 +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
     1.6 +    <modelVersion>4.0.0</modelVersion>
     1.7 +    <parent>
     1.8 +        <groupId>org.netbeans.html</groupId>
     1.9 +        <artifactId>pom</artifactId>
    1.10 +        <version>1.0-SNAPSHOT</version>
    1.11 +    </parent>
    1.12 +    <artifactId>net.java.html.beans</artifactId>
    1.13 +    <name>JSONBeans</name>
    1.14 +    <packaging>jar</packaging>
    1.15 +    <dependencies>
    1.16 +        <dependency>
    1.17 +            <groupId>${project.groupId}</groupId>
    1.18 +            <artifactId>net.java.html.json</artifactId>
    1.19 +            <version>${project.version}</version>
    1.20 +        </dependency>
    1.21 +        <dependency>
    1.22 +            <groupId>org.testng</groupId>
    1.23 +            <artifactId>testng</artifactId>
    1.24 +            <scope>test</scope>
    1.25 +        </dependency>
    1.26 +    </dependencies>
    1.27 +</project>
    1.28 \ No newline at end of file
     2.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     2.2 +++ b/json-beans/src/main/java/net/java/html/beans/JSONBeans.java	Fri Feb 12 08:40:12 2016 +0100
     2.3 @@ -0,0 +1,341 @@
     2.4 +/**
     2.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
     2.6 + *
     2.7 + * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
     2.8 + *
     2.9 + * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
    2.10 + * Other names may be trademarks of their respective owners.
    2.11 + *
    2.12 + * The contents of this file are subject to the terms of either the GNU
    2.13 + * General Public License Version 2 only ("GPL") or the Common
    2.14 + * Development and Distribution License("CDDL") (collectively, the
    2.15 + * "License"). You may not use this file except in compliance with the
    2.16 + * License. You can obtain a copy of the License at
    2.17 + * http://www.netbeans.org/cddl-gplv2.html
    2.18 + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
    2.19 + * specific language governing permissions and limitations under the
    2.20 + * License.  When distributing the software, include this License Header
    2.21 + * Notice in each file and include the License file at
    2.22 + * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
    2.23 + * particular file as subject to the "Classpath" exception as provided
    2.24 + * by Oracle in the GPL Version 2 section of the License file that
    2.25 + * accompanied this code. If applicable, add the following below the
    2.26 + * License Header, with the fields enclosed by brackets [] replaced by
    2.27 + * your own identifying information:
    2.28 + * "Portions Copyrighted [year] [name of copyright owner]"
    2.29 + *
    2.30 + * Contributor(s):
    2.31 + *
    2.32 + * The Original Software is NetBeans. The Initial Developer of the Original
    2.33 + * Software is Oracle. Portions Copyright 2013-2014 Oracle. All Rights Reserved.
    2.34 + *
    2.35 + * If you wish your version of this file to be governed by only the CDDL
    2.36 + * or only the GPL Version 2, indicate your decision by adding
    2.37 + * "[Contributor] elects to include this software in this distribution
    2.38 + * under the [CDDL or GPL Version 2] license." If you do not indicate a
    2.39 + * single choice of license, a recipient has the option to distribute
    2.40 + * your version of this file under either the CDDL, the GPL Version 2 or
    2.41 + * to extend the choice of license to its licensees as provided above.
    2.42 + * However, if you add GPL Version 2 code and therefore, elected the GPL
    2.43 + * Version 2 license, then the option applies only if the new code is
    2.44 + * made subject to such option by the copyright holder.
    2.45 + */
    2.46 +
    2.47 +package net.java.html.beans;
    2.48 +
    2.49 +import java.beans.BeanInfo;
    2.50 +import java.beans.IntrospectionException;
    2.51 +import java.beans.Introspector;
    2.52 +import java.beans.MethodDescriptor;
    2.53 +import java.beans.PropertyChangeEvent;
    2.54 +import java.beans.PropertyChangeListener;
    2.55 +import java.beans.PropertyDescriptor;
    2.56 +import java.lang.ref.Reference;
    2.57 +import java.lang.ref.WeakReference;
    2.58 +import java.lang.reflect.InvocationTargetException;
    2.59 +import java.lang.reflect.Method;
    2.60 +import java.util.HashMap;
    2.61 +import java.util.Map;
    2.62 +import java.util.WeakHashMap;
    2.63 +import java.util.logging.Level;
    2.64 +import java.util.logging.Logger;
    2.65 +import net.java.html.BrwsrCtx;
    2.66 +import net.java.html.json.Function;
    2.67 +import net.java.html.json.Model;
    2.68 +import net.java.html.json.Models;
    2.69 +import net.java.html.json.Property;
    2.70 +import org.apidesign.html.json.spi.Proto;
    2.71 +
    2.72 +/** Plain old JavaBean objects and {@link Model JSON models} finally together.
    2.73 + * In can you own already written JavaBeans, you can use methods in this 
    2.74 + * class to register them for use where ever the classes generated by the
    2.75 + * {@link Model} annotation can be. 
    2.76 + * In general it is expected that one can save a lot of boilerplate code
    2.77 + * by using {@link Model}, but for purposes of compatibility it may be
    2.78 + * suitable to spend time to write JavaBeans and only treat them as
    2.79 + * models.
    2.80 + *
    2.81 + * @author Jaroslav Tulach
    2.82 + * @since 0.9
    2.83 + */
    2.84 +public final class JSONBeans {
    2.85 +    private static final Logger LOG = Logger.getLogger(JSONBeans.class.getName());
    2.86 +
    2.87 +    final Html4JavaType type;
    2.88 +    final Object model;
    2.89 +    final Proto proto;
    2.90 +    Object raw;
    2.91 +
    2.92 +    JSONBeans(Object model, Html4JavaType type, Proto proto) {
    2.93 +        this.model = model;
    2.94 +        this.proto = proto;
    2.95 +        this.type = type;
    2.96 +    }
    2.97 +
    2.98 +    final void initialize(Object object, Method addPCL) {
    2.99 +        this.raw = Models.toRaw(model);
   2.100 +        if (addPCL != null) try {
   2.101 +            addPCL.invoke(object, new JBPCL());
   2.102 +        } catch (Exception ex) {
   2.103 +            throw toRuntime(ex, RuntimeException.class);
   2.104 +        }
   2.105 +    }
   2.106 +    
   2.107 +    /** Registers the given class as a full featured {@link Model model}. 
   2.108 +     * It inspects the class via {@link Introspector} and uses its properties
   2.109 +     * as {@link Property} definitions and its {@link BeanInfo#getMethodDescriptors() methods}
   2.110 +     * as {@link Function} definitions. This method has to be called
   2.111 +     * once and then all classes if this type can be treated as
   2.112 +     * {@link Model instances of models}.
   2.113 +     * 
   2.114 +     * @param javaBeanClass the JavaBean class 
   2.115 +     * @throws IntrospectionException if the JavaBean introspector fails
   2.116 +     */
   2.117 +    public static void register(Class<?> javaBeanClass) throws IntrospectionException {
   2.118 +        BeanInfo bi = Introspector.getBeanInfo(javaBeanClass);
   2.119 +        Method addPCL;
   2.120 +        try {
   2.121 +            addPCL = javaBeanClass.getMethod("addPropertyChangeListener", PropertyChangeListener.class); // NOI18N
   2.122 +        } catch (NoSuchMethodException ex) {
   2.123 +            addPCL = null;
   2.124 +        }
   2.125 +        Html4JavaType html4JavaType = new Html4JavaType(javaBeanClass,
   2.126 +            bi.getPropertyDescriptors(),
   2.127 +            bi.getMethodDescriptors(),
   2.128 +            addPCL
   2.129 +        );
   2.130 +    }
   2.131 +    
   2.132 +    @SuppressWarnings("unchecked")
   2.133 +    static <E extends Throwable> E toRuntime(Throwable ex, Class<E> type) throws E {
   2.134 +        throw (E)ex;
   2.135 +    }
   2.136 +    
   2.137 +    static void clearProto() {
   2.138 +        Html4JavaType.protos.get(null);
   2.139 +    }
   2.140 +    
   2.141 +    private static final class Html4JavaType extends Proto.Type {
   2.142 +        private static final Map<Object,Reference<JSONBeans>> protos = new WeakHashMap<Object, Reference<JSONBeans>>();
   2.143 +        private final PropertyDescriptor[] properties;
   2.144 +        private final MethodDescriptor[] methods;
   2.145 +        private final Method addPCL;
   2.146 +        private final Class<?> javaBeanClass;
   2.147 +        
   2.148 +        Html4JavaType(
   2.149 +            Class<?> javaBeanClass, 
   2.150 +            PropertyDescriptor[] pd,
   2.151 +            MethodDescriptor[] md,
   2.152 +            Method addPCL
   2.153 +        ) {
   2.154 +            super(javaBeanClass, javaBeanClass, pd.length, md.length);
   2.155 +            this.javaBeanClass = javaBeanClass;
   2.156 +            this.properties = pd;
   2.157 +            this.methods = md;
   2.158 +            for (int i = 0; i < pd.length; i++) {
   2.159 +                registerProperty(pd[i].getName(), i, pd[i].getWriteMethod() == null);
   2.160 +            }
   2.161 +            for (int i = 0; i < md.length; i++) {
   2.162 +                registerFunction(md[i].getName(), i);
   2.163 +            }
   2.164 +            this.addPCL = addPCL;
   2.165 +        }
   2.166 +
   2.167 +        @Override
   2.168 +        protected void setValue(Object model, int index, Object value) {
   2.169 +            try {
   2.170 +                try {
   2.171 +                    final Method m = properties[index].getWriteMethod();
   2.172 +                    value = extractValue(toBoxed(m.getParameterTypes()[0]), value);
   2.173 +                    m.invoke(model, value);
   2.174 +                } catch (InvocationTargetException ex) {
   2.175 +                    throw ex.getCause();
   2.176 +                }
   2.177 +            } catch (Throwable t) {
   2.178 +                throw toRuntime(t, RuntimeException.class);
   2.179 +            }
   2.180 +        }
   2.181 +
   2.182 +        @Override
   2.183 +        protected Object getValue(Object model, int index) {
   2.184 +            try {
   2.185 +                Object ret = properties[index].getReadMethod().invoke(model);
   2.186 +                if (ret instanceof Object[]) {
   2.187 +                    Object[] arr = new Object[((Object[])ret).length];
   2.188 +                    for (int i = 0; i < arr.length; i++) {
   2.189 +                        Object ith = ((Object[])ret)[i];
   2.190 +                        if (ith != null) {
   2.191 +                            arr[i] = Models.toRaw(ith);
   2.192 +                        }
   2.193 +                    }
   2.194 +                    return arr;
   2.195 +                }
   2.196 +                return ret;
   2.197 +            } catch (Exception ex) {
   2.198 +                throw toRuntime(ex, RuntimeException.class);
   2.199 +            }
   2.200 +        }
   2.201 +
   2.202 +        @Override
   2.203 +        protected void call(Object model, int index, Object data, Object event) throws Exception {
   2.204 +            Method m = methods[index].getMethod();
   2.205 +            final Class<?>[] types = m.getParameterTypes();
   2.206 +            Object[] args = new Object[types.length];
   2.207 +            if (args.length > 0) {
   2.208 +                args[0] = extractValue(types[0], data);
   2.209 +            }
   2.210 +            if (args.length > 1) {
   2.211 +                args[1] = extractValue(types[1], event);
   2.212 +            }
   2.213 +            m.invoke(model, args);
   2.214 +        }
   2.215 +
   2.216 +        @Override
   2.217 +        protected Object cloneTo(Object model, BrwsrCtx ctx) {
   2.218 +            try {
   2.219 +                Object clone = model.getClass().newInstance();
   2.220 +                for (int i = 0; i < properties.length; i++) {
   2.221 +                    final Method write = properties[i].getWriteMethod();
   2.222 +                    if (write == null) {
   2.223 +                        continue;
   2.224 +                    }
   2.225 +                    Object value = properties[i].getReadMethod().invoke(model);
   2.226 +                    write.invoke(clone, value);
   2.227 +                }
   2.228 +                findProto(clone, this, ctx);
   2.229 +                return clone;
   2.230 +            } catch (Exception ex) {
   2.231 +                throw toRuntime(ex, RuntimeException.class);
   2.232 +            }
   2.233 +        }
   2.234 +
   2.235 +        @Override
   2.236 +        protected Object read(BrwsrCtx c, Object json) {
   2.237 +            try {
   2.238 +                Object bean = javaBeanClass.newInstance();
   2.239 +                Proto proto = findProto(bean, this, c);
   2.240 +                
   2.241 +                String[] names = new String[properties.length];
   2.242 +                for (int i = 0; i < names.length; i++) {
   2.243 +                    if (properties[i].getWriteMethod() != null) {
   2.244 +                        names[i] = properties[i].getName();
   2.245 +                    }
   2.246 +                }
   2.247 +                Object[] values = new Object[properties.length];
   2.248 +                proto.extract(json, names, values);
   2.249 +                for (int i = 0; i < names.length; i++) {
   2.250 +                    if (names[i] != null && values[i] != null) {
   2.251 +                        try {
   2.252 +                            properties[i].getWriteMethod().invoke(bean, values[i]);
   2.253 +                        } catch (IllegalArgumentException ex) {
   2.254 +                            LOG.log(Level.SEVERE, "Cannot set property " + names[i] + " of " + javaBeanClass.getName(), ex);
   2.255 +                        } catch (InvocationTargetException ex) {
   2.256 +                            LOG.log(Level.SEVERE, "Cannot set property " + names[i] + " of " + javaBeanClass.getName(), ex);
   2.257 +                        }
   2.258 +                    }
   2.259 +                }
   2.260 +                return bean;
   2.261 +            } catch (InstantiationException ex) {
   2.262 +                throw toRuntime(ex, RuntimeException.class);
   2.263 +            } catch (IllegalAccessException ex) {
   2.264 +                throw toRuntime(ex, RuntimeException.class);
   2.265 +            }
   2.266 +        }
   2.267 +
   2.268 +        @Override
   2.269 +        protected void onChange(Object model, int index) {
   2.270 +            // no automatic dependencies between properties
   2.271 +        }
   2.272 +
   2.273 +        @Override
   2.274 +        protected Proto protoFor(Object object) {
   2.275 +            return findProto(object, this, null);
   2.276 +        }
   2.277 +        
   2.278 +        private static Proto findProto(Object object, Html4JavaType type, BrwsrCtx ctx) {
   2.279 +            Reference<JSONBeans> ref = protos.get(object);
   2.280 +            JSONBeans jb = ref == null ? null : ref.get();
   2.281 +            if (jb != null) {
   2.282 +                return jb.proto;
   2.283 +            }
   2.284 +            if (ctx == null) {
   2.285 +                ctx = BrwsrCtx.findDefault(object.getClass());
   2.286 +            }
   2.287 +            Proto p = type.createProto(object, ctx);
   2.288 +            jb = new JSONBeans(object, type, p);
   2.289 +            final Reference<JSONBeans> br = new WeakReference<JSONBeans>(jb);
   2.290 +            protos.put(object, br);
   2.291 +            jb.initialize(object, type.addPCL);
   2.292 +            return p;
   2.293 +        }
   2.294 +        private static Class<?> toBoxed(Class<?> c) {
   2.295 +            if (!c.isPrimitive()) {
   2.296 +                return c;
   2.297 +            }
   2.298 +            if (c == Boolean.TYPE) {
   2.299 +                return Boolean.class;
   2.300 +            }
   2.301 +            if (c == Byte.TYPE) {
   2.302 +                return Byte.class;
   2.303 +            }
   2.304 +            if (c == Short.TYPE) {
   2.305 +                return Short.class;
   2.306 +            }
   2.307 +            if (c == Integer.TYPE) {
   2.308 +                return Integer.class;
   2.309 +            }
   2.310 +            if (c == Long.TYPE) {
   2.311 +                return Long.class;
   2.312 +            }
   2.313 +            if (c == Float.TYPE) {
   2.314 +                return Float.class;
   2.315 +            }
   2.316 +            if (c == Double.TYPE) {
   2.317 +                return Double.class;
   2.318 +            }
   2.319 +            if (c == Character.TYPE) {
   2.320 +                return Character.class;
   2.321 +            }
   2.322 +            return c;
   2.323 +        }
   2.324 +    }
   2.325 +    
   2.326 +    private final class JBPCL implements PropertyChangeListener {
   2.327 +        @Override
   2.328 +        public void propertyChange(PropertyChangeEvent evt) {
   2.329 +            String n = evt.getPropertyName();
   2.330 +            JSONBeans jb = JSONBeans.this;
   2.331 +            if (jb == null) {
   2.332 +                return;
   2.333 +            }
   2.334 +            Proto proto = jb.proto;
   2.335 +            Html4JavaType type = jb.type;
   2.336 +            for (int i = 0; i < type.properties.length; i++) {
   2.337 +                PropertyDescriptor p = type.properties[i];
   2.338 +                if (n == null || n.equals(p.getName())) {
   2.339 +                    proto.valueHasMutated(n, evt.getOldValue(), evt.getNewValue());
   2.340 +                }
   2.341 +            }
   2.342 +        }
   2.343 +    }
   2.344 +}
     3.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     3.2 +++ b/json-beans/src/test/java/net/java/html/beans/MapModelGCTest.java	Fri Feb 12 08:40:12 2016 +0100
     3.3 @@ -0,0 +1,418 @@
     3.4 +/**
     3.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
     3.6 + *
     3.7 + * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
     3.8 + *
     3.9 + * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
    3.10 + * Other names may be trademarks of their respective owners.
    3.11 + *
    3.12 + * The contents of this file are subject to the terms of either the GNU
    3.13 + * General Public License Version 2 only ("GPL") or the Common
    3.14 + * Development and Distribution License("CDDL") (collectively, the
    3.15 + * "License"). You may not use this file except in compliance with the
    3.16 + * License. You can obtain a copy of the License at
    3.17 + * http://www.netbeans.org/cddl-gplv2.html
    3.18 + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
    3.19 + * specific language governing permissions and limitations under the
    3.20 + * License.  When distributing the software, include this License Header
    3.21 + * Notice in each file and include the License file at
    3.22 + * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
    3.23 + * particular file as subject to the "Classpath" exception as provided
    3.24 + * by Oracle in the GPL Version 2 section of the License file that
    3.25 + * accompanied this code. If applicable, add the following below the
    3.26 + * License Header, with the fields enclosed by brackets [] replaced by
    3.27 + * your own identifying information:
    3.28 + * "Portions Copyrighted [year] [name of copyright owner]"
    3.29 + *
    3.30 + * Contributor(s):
    3.31 + *
    3.32 + * The Original Software is NetBeans. The Initial Developer of the Original
    3.33 + * Software is Oracle. Portions Copyright 2013-2014 Oracle. All Rights Reserved.
    3.34 + *
    3.35 + * If you wish your version of this file to be governed by only the CDDL
    3.36 + * or only the GPL Version 2, indicate your decision by adding
    3.37 + * "[Contributor] elects to include this software in this distribution
    3.38 + * under the [CDDL or GPL Version 2] license." If you do not indicate a
    3.39 + * single choice of license, a recipient has the option to distribute
    3.40 + * your version of this file under either the CDDL, the GPL Version 2 or
    3.41 + * to extend the choice of license to its licensees as provided above.
    3.42 + * However, if you add GPL Version 2 code and therefore, elected the GPL
    3.43 + * Version 2 license, then the option applies only if the new code is
    3.44 + * made subject to such option by the copyright holder.
    3.45 + */
    3.46 +package net.java.html.json;
    3.47 +
    3.48 +import net.java.html.BrwsrCtx;
    3.49 +import java.io.IOException;
    3.50 +import java.io.InputStream;
    3.51 +import java.lang.reflect.InvocationTargetException;
    3.52 +import java.util.HashMap;
    3.53 +import java.util.Iterator;
    3.54 +import java.util.ListIterator;
    3.55 +import java.util.Map;
    3.56 +import org.netbeans.html.context.spi.Contexts;
    3.57 +import org.netbeans.html.json.spi.FunctionBinding;
    3.58 +import org.netbeans.html.json.spi.JSONCall;
    3.59 +import org.netbeans.html.json.spi.PropertyBinding;
    3.60 +import org.netbeans.html.json.spi.Technology;
    3.61 +import org.netbeans.html.json.spi.Transfer;
    3.62 +import org.testng.annotations.BeforeMethod;
    3.63 +import org.testng.annotations.Test;
    3.64 +import static org.testng.Assert.*;
    3.65 +
    3.66 +/**
    3.67 + *
    3.68 + * @author Jaroslav Tulach
    3.69 + */
    3.70 +public class MapModelTest {
    3.71 +    private MapTechnology t;
    3.72 +    private BrwsrCtx c;
    3.73 +
    3.74 +    @BeforeMethod public void initTechnology() {
    3.75 +        t = new MapTechnology();
    3.76 +        c = Contexts.newBuilder().register(Technology.class, t, 1).
    3.77 +            register(Transfer.class, t, 1).build();
    3.78 +    }
    3.79 +    
    3.80 +    @Test public void isThereNoApplyBinding() throws Exception {
    3.81 +        try {
    3.82 +            Person.class.getMethod("applyBindings");
    3.83 +        } catch (NoSuchMethodException ex) {
    3.84 +            // OK
    3.85 +            return;
    3.86 +        }
    3.87 +        fail("There should be no applyBindings() method");
    3.88 +    }
    3.89 +    
    3.90 +    @Test public void isThereABinding() throws Exception {
    3.91 +        Person p = Models.bind(new Person(), c);
    3.92 +        Models.applyBindings(p);
    3.93 +        assertNull(t.appliedId, "Applied globally");
    3.94 +        p.setFirstName("Jarda");
    3.95 +        
    3.96 +        Map m = (Map)Models.toRaw(p);
    3.97 +        Object v = m.get("firstName");
    3.98 +        assertNotNull(v, "Value should be in the map");
    3.99 +        assertEquals(v.getClass(), One.class, "It is instance of One");
   3.100 +        One o = (One)v;
   3.101 +        assertEquals(o.changes, 1, "One change so far");
   3.102 +        assertFalse(o.pb.isReadOnly(), "Mutable property");
   3.103 +        
   3.104 +        assertEquals(o.get(), "Jarda", "Value should be in the map");
   3.105 +        
   3.106 +        o.set("Karle");
   3.107 +        
   3.108 +        assertEquals(p.getFirstName(), "Karle", "Model value updated");
   3.109 +        assertEquals(o.changes, 2, "Snd change");
   3.110 +    }
   3.111 +    
   3.112 +    @Test public void applyLocally() throws Exception {
   3.113 +        Person p = Models.bind(new Person(), c);
   3.114 +        Models.applyBindings(p, "local");
   3.115 +        assertEquals(t.appliedId, "local", "Applied locally");
   3.116 +    }
   3.117 +    
   3.118 +    @Test public void dontNotifySameProperty() throws Exception {
   3.119 +        Person p = Models.bind(new Person(), c);
   3.120 +        p.setFirstName("Jirka");
   3.121 +        
   3.122 +        Map m = (Map)Models.toRaw(p);
   3.123 +        Object v = m.get("firstName");
   3.124 +        assertNotNull(v, "Value should be in the map");
   3.125 +        assertEquals(v.getClass(), One.class, "It is instance of One");
   3.126 +        One o = (One)v;
   3.127 +        assertEquals(o.changes, 0, "No change so far the only one change happened before we connected");
   3.128 +        
   3.129 +        p.setFirstName(new String("Jirka"));
   3.130 +        assertEquals(o.changes, 0, "No change so far, the value is the same");
   3.131 +        
   3.132 +        p.setFirstName("Jarda");
   3.133 +        assertFalse(o.pb.isReadOnly(), "Mutable property");
   3.134 +        
   3.135 +        assertEquals(o.get(), "Jarda", "Value should be in the map");
   3.136 +        
   3.137 +        o.set("Karle");
   3.138 +        
   3.139 +        assertEquals(p.getFirstName(), "Karle", "Model value updated");
   3.140 +        assertEquals(o.changes, 2, "Snd change");
   3.141 +    }
   3.142 +    
   3.143 +    @Test public void canSetEnumAsString() throws Exception {
   3.144 +        Person p = Models.bind(new Person(), c);
   3.145 +        p.setFirstName("Jirka");
   3.146 +        p.setSex(Sex.MALE);
   3.147 +        
   3.148 +        Map m = (Map)Models.toRaw(p);
   3.149 +        Object v = m.get("sex");
   3.150 +        assertNotNull(v, "Value should be in the map");
   3.151 +        assertEquals(v.getClass(), One.class, "It is instance of One");
   3.152 +        One o = (One)v;
   3.153 +        
   3.154 +        o.set("FEMALE");
   3.155 +
   3.156 +        assertEquals(p.getSex(), Sex.FEMALE, "Changed to female");
   3.157 +    }
   3.158 +    
   3.159 +    @Test public void derivedProperty() throws Exception {
   3.160 +        Person p = Models.bind(new Person(), c);
   3.161 +        
   3.162 +        Map m = (Map)Models.toRaw(p);
   3.163 +        Object v = m.get("fullName");
   3.164 +        assertNotNull(v, "Value should be in the map");
   3.165 +        assertEquals(v.getClass(), One.class, "It is instance of One");
   3.166 +        One o = (One)v;
   3.167 +        assertTrue(o.pb.isReadOnly(), "Mutable property");
   3.168 +    }
   3.169 +    
   3.170 +    @Test public void changeSex() {
   3.171 +        Person p = Models.bind(new Person(), c);
   3.172 +        p.setFirstName("Trans");
   3.173 +        p.setSex(Sex.MALE);
   3.174 +        
   3.175 +        Map m = (Map)Models.toRaw(p);
   3.176 +        Object o = m.get("changeSex");
   3.177 +        assertNotNull(o, "Function registered in the model");
   3.178 +        assertEquals(o.getClass(), One.class);
   3.179 +        
   3.180 +        One one = (One)o;
   3.181 +        assertNotNull(one.fb, "Function binding specified");
   3.182 +        
   3.183 +        one.fb.call(null, null);
   3.184 +        
   3.185 +        assertEquals(p.getSex(), Sex.FEMALE, "Changed");
   3.186 +    }
   3.187 +    
   3.188 +    @Test public void setSex() {
   3.189 +        Person p = Models.bind(new Person(), c);
   3.190 +        p.setFirstName("Trans");
   3.191 +        
   3.192 +        Map m = (Map)Models.toRaw(p);
   3.193 +        Object o = m.get("changeSex");
   3.194 +        assertNotNull(o, "Function registered in the model");
   3.195 +        assertEquals(o.getClass(), One.class);
   3.196 +        
   3.197 +        One one = (One)o;
   3.198 +        assertNotNull(one.fb, "Function binding specified");
   3.199 +        
   3.200 +        one.fb.call("FEMALE", new Object());
   3.201 +        
   3.202 +        assertEquals(p.getSex(), Sex.FEMALE, "Changed");
   3.203 +    }
   3.204 +
   3.205 +    @Test public void changeComputedProperty() {
   3.206 +        Modelik p = Models.bind(new Modelik(), c);
   3.207 +        p.setValue(5);
   3.208 +
   3.209 +        Map m = (Map)Models.toRaw(p);
   3.210 +        Object o = m.get("powerValue");
   3.211 +        assertNotNull(o, "Value is there");
   3.212 +        assertEquals(o.getClass(), One.class);
   3.213 +
   3.214 +        One one = (One)o;
   3.215 +        assertNotNull(one.pb, "Prop binding specified");
   3.216 +
   3.217 +        assertEquals(one.pb.getValue(), 25, "Power of 5");
   3.218 +
   3.219 +        one.pb.setValue(16);
   3.220 +        assertEquals(p.getValue(), 4, "Square root of 16");
   3.221 +    }
   3.222 +    
   3.223 +    @Test public void removeViaIterator() {
   3.224 +        People p = Models.bind(new People(), c);
   3.225 +        p.getNicknames().add("One");
   3.226 +        p.getNicknames().add("Two");
   3.227 +        p.getNicknames().add("Three");
   3.228 +
   3.229 +        Map m = (Map)Models.toRaw(p);
   3.230 +        Object o = m.get("nicknames");
   3.231 +        assertNotNull(o, "List registered in the model");
   3.232 +        assertEquals(o.getClass(), One.class);
   3.233 +        One one = (One)o;
   3.234 +        
   3.235 +        
   3.236 +        assertEquals(one.changes, 0, "No change");
   3.237 +        
   3.238 +        Iterator<String> it = p.getNicknames().iterator();
   3.239 +        assertEquals(it.next(), "One");
   3.240 +        assertEquals(it.next(), "Two");
   3.241 +        it.remove();
   3.242 +        assertEquals(it.next(), "Three");
   3.243 +        assertFalse(it.hasNext());
   3.244 +        
   3.245 +        
   3.246 +        assertEquals(one.changes, 1, "One change");
   3.247 +    }
   3.248 +    
   3.249 +    @Test public void removeViaListIterator() {
   3.250 +        People p = Models.bind(new People(), c);
   3.251 +        p.getNicknames().add("One");
   3.252 +        p.getNicknames().add("Two");
   3.253 +        p.getNicknames().add("Three");
   3.254 +
   3.255 +        Map m = (Map)Models.toRaw(p);
   3.256 +        Object o = m.get("nicknames");
   3.257 +        assertNotNull(o, "List registered in the model");
   3.258 +        assertEquals(o.getClass(), One.class);
   3.259 +        One one = (One)o;
   3.260 +        
   3.261 +        
   3.262 +        assertEquals(one.changes, 0, "No change");
   3.263 +        
   3.264 +        ListIterator<String> it = p.getNicknames().listIterator(1);
   3.265 +        assertEquals(it.next(), "Two");
   3.266 +        it.remove();
   3.267 +        assertEquals(it.next(), "Three");
   3.268 +        assertFalse(it.hasNext());
   3.269 +        
   3.270 +        
   3.271 +        assertEquals(one.changes, 1, "One change");
   3.272 +        
   3.273 +        it.set("3");
   3.274 +        assertEquals(p.getNicknames().get(1), "3");
   3.275 +        
   3.276 +        assertEquals(one.changes, 2, "Snd change");
   3.277 +    }
   3.278 +
   3.279 +    @Test public void functionWithParameters() {
   3.280 +        People p = Models.bind(new People(), c);
   3.281 +        p.getNicknames().add("One");
   3.282 +        p.getNicknames().add("Two");
   3.283 +        p.getNicknames().add("Three");
   3.284 +
   3.285 +        Map m = (Map)Models.toRaw(p);
   3.286 +        Object o = m.get("inInnerClass");
   3.287 +        assertNotNull(o, "functiton is available");
   3.288 +        assertEquals(o.getClass(), One.class);
   3.289 +        One one = (One)o;
   3.290 +        
   3.291 +        Map<String,Object> obj = new HashMap<String, Object>();
   3.292 +        obj.put("nick", "newNick");
   3.293 +        obj.put("x", 42);
   3.294 +        obj.put("y", 7.7f);
   3.295 +        final Person data = new Person("a", "b", Sex.MALE);
   3.296 +        
   3.297 +        one.fb.call(data, obj);
   3.298 +
   3.299 +        assertEquals(p.getInfo().size(), 1, "a+b is there: " + p.getInfo());
   3.300 +        assertEquals(p.getInfo().get(0), data, "Expecting data: " + p.getInfo());
   3.301 +        
   3.302 +        assertEquals(p.getNicknames().size(), 4, "One more nickname: " + p.getNicknames());
   3.303 +        assertEquals(p.getNicknames().get(3), "newNick");
   3.304 +        
   3.305 +        assertEquals(p.getAge().size(), 2, "Two new values: " + p.getAge());
   3.306 +        assertEquals(p.getAge().get(0).intValue(), 42);
   3.307 +        assertEquals(p.getAge().get(1).intValue(), 7);
   3.308 +    }
   3.309 +    
   3.310 +    static final class One {
   3.311 +        int changes;
   3.312 +        final PropertyBinding pb;
   3.313 +        final FunctionBinding fb;
   3.314 +    
   3.315 +        One(Object m, PropertyBinding pb) throws NoSuchMethodException {
   3.316 +            this.pb = pb;
   3.317 +            this.fb = null;
   3.318 +        }
   3.319 +        One(Object m, FunctionBinding fb) throws NoSuchMethodException {
   3.320 +            this.pb = null;
   3.321 +            this.fb = fb;
   3.322 +        }
   3.323 +        
   3.324 +        Object get() throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {
   3.325 +            return pb.getValue();
   3.326 +        }
   3.327 +        
   3.328 +        void set(Object v) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {
   3.329 +            pb.setValue(v);
   3.330 +        }
   3.331 +    }
   3.332 +    
   3.333 +    static final class MapTechnology 
   3.334 +    implements Technology.ApplyId<Map<String,One>>, Transfer {
   3.335 +        private Map<String, One> appliedData;
   3.336 +        private String appliedId;
   3.337 +
   3.338 +        @Override
   3.339 +        public Map<String, One> wrapModel(Object model) {
   3.340 +            return new HashMap<String, One>();
   3.341 +        }
   3.342 +
   3.343 +        @Override
   3.344 +        public void valueHasMutated(Map<String, One> data, String propertyName) {
   3.345 +            One p = data.get(propertyName);
   3.346 +            if (p != null) {
   3.347 +                p.changes++;
   3.348 +            }
   3.349 +        }
   3.350 +
   3.351 +        @Override
   3.352 +        public void bind(PropertyBinding b, Object model, Map<String, One> data) {
   3.353 +            try {
   3.354 +                One o = new One(model, b);
   3.355 +                data.put(b.getPropertyName(), o);
   3.356 +            } catch (NoSuchMethodException ex) {
   3.357 +                throw new IllegalStateException(ex);
   3.358 +            }
   3.359 +        }
   3.360 +
   3.361 +        @Override
   3.362 +        public void expose(FunctionBinding fb, Object model, Map<String, One> data) {
   3.363 +            try {
   3.364 +                data.put(fb.getFunctionName(), new One(model, fb));
   3.365 +            } catch (NoSuchMethodException ex) {
   3.366 +                throw new IllegalStateException(ex);
   3.367 +            }
   3.368 +        }
   3.369 +
   3.370 +        @Override
   3.371 +        public void applyBindings(Map<String, One> data) {
   3.372 +            throw new UnsupportedOperationException("Never called!");
   3.373 +        }
   3.374 +
   3.375 +        @Override
   3.376 +        public Object wrapArray(Object[] arr) {
   3.377 +            return arr;
   3.378 +        }
   3.379 +
   3.380 +        @Override
   3.381 +        public void extract(Object obj, String[] props, Object[] values) {
   3.382 +            Map<?,?> map = obj instanceof Map ? (Map<?,?>)obj : null;
   3.383 +            for (int i = 0; i < Math.min(props.length, values.length); i++) {
   3.384 +                if (map == null) {
   3.385 +                    values[i] = null;
   3.386 +                } else {
   3.387 +                    values[i] = map.get(props[i]);
   3.388 +                    if (values[i] instanceof One) {
   3.389 +                        values[i] = ((One)values[i]).pb.getValue();
   3.390 +                    }
   3.391 +                }
   3.392 +            }
   3.393 +        }
   3.394 +
   3.395 +        @Override
   3.396 +        public void loadJSON(JSONCall call) {
   3.397 +            call.notifyError(new UnsupportedOperationException());
   3.398 +        }
   3.399 +
   3.400 +        @Override
   3.401 +        public <M> M toModel(Class<M> modelClass, Object data) {
   3.402 +            return modelClass.cast(data);
   3.403 +        }
   3.404 +
   3.405 +        @Override
   3.406 +        public Object toJSON(InputStream is) throws IOException {
   3.407 +            throw new IOException();
   3.408 +        }
   3.409 +
   3.410 +        @Override
   3.411 +        public void runSafe(Runnable r) {
   3.412 +            throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
   3.413 +        }
   3.414 +
   3.415 +        @Override
   3.416 +        public void applyBindings(String id, Map<String, One> data) {
   3.417 +            this.appliedId = id;
   3.418 +            this.appliedData = data;
   3.419 +        }
   3.420 +    }
   3.421 +}
     4.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     4.2 +++ b/json-beans/src/test/java/net/java/html/beans/MapModelTest.java	Fri Feb 12 08:40:12 2016 +0100
     4.3 @@ -0,0 +1,418 @@
     4.4 +/**
     4.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
     4.6 + *
     4.7 + * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
     4.8 + *
     4.9 + * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
    4.10 + * Other names may be trademarks of their respective owners.
    4.11 + *
    4.12 + * The contents of this file are subject to the terms of either the GNU
    4.13 + * General Public License Version 2 only ("GPL") or the Common
    4.14 + * Development and Distribution License("CDDL") (collectively, the
    4.15 + * "License"). You may not use this file except in compliance with the
    4.16 + * License. You can obtain a copy of the License at
    4.17 + * http://www.netbeans.org/cddl-gplv2.html
    4.18 + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
    4.19 + * specific language governing permissions and limitations under the
    4.20 + * License.  When distributing the software, include this License Header
    4.21 + * Notice in each file and include the License file at
    4.22 + * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
    4.23 + * particular file as subject to the "Classpath" exception as provided
    4.24 + * by Oracle in the GPL Version 2 section of the License file that
    4.25 + * accompanied this code. If applicable, add the following below the
    4.26 + * License Header, with the fields enclosed by brackets [] replaced by
    4.27 + * your own identifying information:
    4.28 + * "Portions Copyrighted [year] [name of copyright owner]"
    4.29 + *
    4.30 + * Contributor(s):
    4.31 + *
    4.32 + * The Original Software is NetBeans. The Initial Developer of the Original
    4.33 + * Software is Oracle. Portions Copyright 2013-2014 Oracle. All Rights Reserved.
    4.34 + *
    4.35 + * If you wish your version of this file to be governed by only the CDDL
    4.36 + * or only the GPL Version 2, indicate your decision by adding
    4.37 + * "[Contributor] elects to include this software in this distribution
    4.38 + * under the [CDDL or GPL Version 2] license." If you do not indicate a
    4.39 + * single choice of license, a recipient has the option to distribute
    4.40 + * your version of this file under either the CDDL, the GPL Version 2 or
    4.41 + * to extend the choice of license to its licensees as provided above.
    4.42 + * However, if you add GPL Version 2 code and therefore, elected the GPL
    4.43 + * Version 2 license, then the option applies only if the new code is
    4.44 + * made subject to such option by the copyright holder.
    4.45 + */
    4.46 +package net.java.html.json;
    4.47 +
    4.48 +import net.java.html.BrwsrCtx;
    4.49 +import java.io.IOException;
    4.50 +import java.io.InputStream;
    4.51 +import java.lang.reflect.InvocationTargetException;
    4.52 +import java.util.HashMap;
    4.53 +import java.util.Iterator;
    4.54 +import java.util.ListIterator;
    4.55 +import java.util.Map;
    4.56 +import org.netbeans.html.context.spi.Contexts;
    4.57 +import org.netbeans.html.json.spi.FunctionBinding;
    4.58 +import org.netbeans.html.json.spi.JSONCall;
    4.59 +import org.netbeans.html.json.spi.PropertyBinding;
    4.60 +import org.netbeans.html.json.spi.Technology;
    4.61 +import org.netbeans.html.json.spi.Transfer;
    4.62 +import org.testng.annotations.BeforeMethod;
    4.63 +import org.testng.annotations.Test;
    4.64 +import static org.testng.Assert.*;
    4.65 +
    4.66 +/**
    4.67 + *
    4.68 + * @author Jaroslav Tulach
    4.69 + */
    4.70 +public class MapModelTest {
    4.71 +    private MapTechnology t;
    4.72 +    private BrwsrCtx c;
    4.73 +
    4.74 +    @BeforeMethod public void initTechnology() {
    4.75 +        t = new MapTechnology();
    4.76 +        c = Contexts.newBuilder().register(Technology.class, t, 1).
    4.77 +            register(Transfer.class, t, 1).build();
    4.78 +    }
    4.79 +    
    4.80 +    @Test public void isThereNoApplyBinding() throws Exception {
    4.81 +        try {
    4.82 +            Person.class.getMethod("applyBindings");
    4.83 +        } catch (NoSuchMethodException ex) {
    4.84 +            // OK
    4.85 +            return;
    4.86 +        }
    4.87 +        fail("There should be no applyBindings() method");
    4.88 +    }
    4.89 +    
    4.90 +    @Test public void isThereABinding() throws Exception {
    4.91 +        Person p = Models.bind(new Person(), c);
    4.92 +        Models.applyBindings(p);
    4.93 +        assertNull(t.appliedId, "Applied globally");
    4.94 +        p.setFirstName("Jarda");
    4.95 +        
    4.96 +        Map m = (Map)Models.toRaw(p);
    4.97 +        Object v = m.get("firstName");
    4.98 +        assertNotNull(v, "Value should be in the map");
    4.99 +        assertEquals(v.getClass(), One.class, "It is instance of One");
   4.100 +        One o = (One)v;
   4.101 +        assertEquals(o.changes, 1, "One change so far");
   4.102 +        assertFalse(o.pb.isReadOnly(), "Mutable property");
   4.103 +        
   4.104 +        assertEquals(o.get(), "Jarda", "Value should be in the map");
   4.105 +        
   4.106 +        o.set("Karle");
   4.107 +        
   4.108 +        assertEquals(p.getFirstName(), "Karle", "Model value updated");
   4.109 +        assertEquals(o.changes, 2, "Snd change");
   4.110 +    }
   4.111 +    
   4.112 +    @Test public void applyLocally() throws Exception {
   4.113 +        Person p = Models.bind(new Person(), c);
   4.114 +        Models.applyBindings(p, "local");
   4.115 +        assertEquals(t.appliedId, "local", "Applied locally");
   4.116 +    }
   4.117 +    
   4.118 +    @Test public void dontNotifySameProperty() throws Exception {
   4.119 +        Person p = Models.bind(new Person(), c);
   4.120 +        p.setFirstName("Jirka");
   4.121 +        
   4.122 +        Map m = (Map)Models.toRaw(p);
   4.123 +        Object v = m.get("firstName");
   4.124 +        assertNotNull(v, "Value should be in the map");
   4.125 +        assertEquals(v.getClass(), One.class, "It is instance of One");
   4.126 +        One o = (One)v;
   4.127 +        assertEquals(o.changes, 0, "No change so far the only one change happened before we connected");
   4.128 +        
   4.129 +        p.setFirstName(new String("Jirka"));
   4.130 +        assertEquals(o.changes, 0, "No change so far, the value is the same");
   4.131 +        
   4.132 +        p.setFirstName("Jarda");
   4.133 +        assertFalse(o.pb.isReadOnly(), "Mutable property");
   4.134 +        
   4.135 +        assertEquals(o.get(), "Jarda", "Value should be in the map");
   4.136 +        
   4.137 +        o.set("Karle");
   4.138 +        
   4.139 +        assertEquals(p.getFirstName(), "Karle", "Model value updated");
   4.140 +        assertEquals(o.changes, 2, "Snd change");
   4.141 +    }
   4.142 +    
   4.143 +    @Test public void canSetEnumAsString() throws Exception {
   4.144 +        Person p = Models.bind(new Person(), c);
   4.145 +        p.setFirstName("Jirka");
   4.146 +        p.setSex(Sex.MALE);
   4.147 +        
   4.148 +        Map m = (Map)Models.toRaw(p);
   4.149 +        Object v = m.get("sex");
   4.150 +        assertNotNull(v, "Value should be in the map");
   4.151 +        assertEquals(v.getClass(), One.class, "It is instance of One");
   4.152 +        One o = (One)v;
   4.153 +        
   4.154 +        o.set("FEMALE");
   4.155 +
   4.156 +        assertEquals(p.getSex(), Sex.FEMALE, "Changed to female");
   4.157 +    }
   4.158 +    
   4.159 +    @Test public void derivedProperty() throws Exception {
   4.160 +        Person p = Models.bind(new Person(), c);
   4.161 +        
   4.162 +        Map m = (Map)Models.toRaw(p);
   4.163 +        Object v = m.get("fullName");
   4.164 +        assertNotNull(v, "Value should be in the map");
   4.165 +        assertEquals(v.getClass(), One.class, "It is instance of One");
   4.166 +        One o = (One)v;
   4.167 +        assertTrue(o.pb.isReadOnly(), "Mutable property");
   4.168 +    }
   4.169 +    
   4.170 +    @Test public void changeSex() {
   4.171 +        Person p = Models.bind(new Person(), c);
   4.172 +        p.setFirstName("Trans");
   4.173 +        p.setSex(Sex.MALE);
   4.174 +        
   4.175 +        Map m = (Map)Models.toRaw(p);
   4.176 +        Object o = m.get("changeSex");
   4.177 +        assertNotNull(o, "Function registered in the model");
   4.178 +        assertEquals(o.getClass(), One.class);
   4.179 +        
   4.180 +        One one = (One)o;
   4.181 +        assertNotNull(one.fb, "Function binding specified");
   4.182 +        
   4.183 +        one.fb.call(null, null);
   4.184 +        
   4.185 +        assertEquals(p.getSex(), Sex.FEMALE, "Changed");
   4.186 +    }
   4.187 +    
   4.188 +    @Test public void setSex() {
   4.189 +        Person p = Models.bind(new Person(), c);
   4.190 +        p.setFirstName("Trans");
   4.191 +        
   4.192 +        Map m = (Map)Models.toRaw(p);
   4.193 +        Object o = m.get("changeSex");
   4.194 +        assertNotNull(o, "Function registered in the model");
   4.195 +        assertEquals(o.getClass(), One.class);
   4.196 +        
   4.197 +        One one = (One)o;
   4.198 +        assertNotNull(one.fb, "Function binding specified");
   4.199 +        
   4.200 +        one.fb.call("FEMALE", new Object());
   4.201 +        
   4.202 +        assertEquals(p.getSex(), Sex.FEMALE, "Changed");
   4.203 +    }
   4.204 +
   4.205 +    @Test public void changeComputedProperty() {
   4.206 +        Modelik p = Models.bind(new Modelik(), c);
   4.207 +        p.setValue(5);
   4.208 +
   4.209 +        Map m = (Map)Models.toRaw(p);
   4.210 +        Object o = m.get("powerValue");
   4.211 +        assertNotNull(o, "Value is there");
   4.212 +        assertEquals(o.getClass(), One.class);
   4.213 +
   4.214 +        One one = (One)o;
   4.215 +        assertNotNull(one.pb, "Prop binding specified");
   4.216 +
   4.217 +        assertEquals(one.pb.getValue(), 25, "Power of 5");
   4.218 +
   4.219 +        one.pb.setValue(16);
   4.220 +        assertEquals(p.getValue(), 4, "Square root of 16");
   4.221 +    }
   4.222 +    
   4.223 +    @Test public void removeViaIterator() {
   4.224 +        People p = Models.bind(new People(), c);
   4.225 +        p.getNicknames().add("One");
   4.226 +        p.getNicknames().add("Two");
   4.227 +        p.getNicknames().add("Three");
   4.228 +
   4.229 +        Map m = (Map)Models.toRaw(p);
   4.230 +        Object o = m.get("nicknames");
   4.231 +        assertNotNull(o, "List registered in the model");
   4.232 +        assertEquals(o.getClass(), One.class);
   4.233 +        One one = (One)o;
   4.234 +        
   4.235 +        
   4.236 +        assertEquals(one.changes, 0, "No change");
   4.237 +        
   4.238 +        Iterator<String> it = p.getNicknames().iterator();
   4.239 +        assertEquals(it.next(), "One");
   4.240 +        assertEquals(it.next(), "Two");
   4.241 +        it.remove();
   4.242 +        assertEquals(it.next(), "Three");
   4.243 +        assertFalse(it.hasNext());
   4.244 +        
   4.245 +        
   4.246 +        assertEquals(one.changes, 1, "One change");
   4.247 +    }
   4.248 +    
   4.249 +    @Test public void removeViaListIterator() {
   4.250 +        People p = Models.bind(new People(), c);
   4.251 +        p.getNicknames().add("One");
   4.252 +        p.getNicknames().add("Two");
   4.253 +        p.getNicknames().add("Three");
   4.254 +
   4.255 +        Map m = (Map)Models.toRaw(p);
   4.256 +        Object o = m.get("nicknames");
   4.257 +        assertNotNull(o, "List registered in the model");
   4.258 +        assertEquals(o.getClass(), One.class);
   4.259 +        One one = (One)o;
   4.260 +        
   4.261 +        
   4.262 +        assertEquals(one.changes, 0, "No change");
   4.263 +        
   4.264 +        ListIterator<String> it = p.getNicknames().listIterator(1);
   4.265 +        assertEquals(it.next(), "Two");
   4.266 +        it.remove();
   4.267 +        assertEquals(it.next(), "Three");
   4.268 +        assertFalse(it.hasNext());
   4.269 +        
   4.270 +        
   4.271 +        assertEquals(one.changes, 1, "One change");
   4.272 +        
   4.273 +        it.set("3");
   4.274 +        assertEquals(p.getNicknames().get(1), "3");
   4.275 +        
   4.276 +        assertEquals(one.changes, 2, "Snd change");
   4.277 +    }
   4.278 +
   4.279 +    @Test public void functionWithParameters() {
   4.280 +        People p = Models.bind(new People(), c);
   4.281 +        p.getNicknames().add("One");
   4.282 +        p.getNicknames().add("Two");
   4.283 +        p.getNicknames().add("Three");
   4.284 +
   4.285 +        Map m = (Map)Models.toRaw(p);
   4.286 +        Object o = m.get("inInnerClass");
   4.287 +        assertNotNull(o, "functiton is available");
   4.288 +        assertEquals(o.getClass(), One.class);
   4.289 +        One one = (One)o;
   4.290 +        
   4.291 +        Map<String,Object> obj = new HashMap<String, Object>();
   4.292 +        obj.put("nick", "newNick");
   4.293 +        obj.put("x", 42);
   4.294 +        obj.put("y", 7.7f);
   4.295 +        final Person data = new Person("a", "b", Sex.MALE);
   4.296 +        
   4.297 +        one.fb.call(data, obj);
   4.298 +
   4.299 +        assertEquals(p.getInfo().size(), 1, "a+b is there: " + p.getInfo());
   4.300 +        assertEquals(p.getInfo().get(0), data, "Expecting data: " + p.getInfo());
   4.301 +        
   4.302 +        assertEquals(p.getNicknames().size(), 4, "One more nickname: " + p.getNicknames());
   4.303 +        assertEquals(p.getNicknames().get(3), "newNick");
   4.304 +        
   4.305 +        assertEquals(p.getAge().size(), 2, "Two new values: " + p.getAge());
   4.306 +        assertEquals(p.getAge().get(0).intValue(), 42);
   4.307 +        assertEquals(p.getAge().get(1).intValue(), 7);
   4.308 +    }
   4.309 +    
   4.310 +    static final class One {
   4.311 +        int changes;
   4.312 +        final PropertyBinding pb;
   4.313 +        final FunctionBinding fb;
   4.314 +    
   4.315 +        One(Object m, PropertyBinding pb) throws NoSuchMethodException {
   4.316 +            this.pb = pb;
   4.317 +            this.fb = null;
   4.318 +        }
   4.319 +        One(Object m, FunctionBinding fb) throws NoSuchMethodException {
   4.320 +            this.pb = null;
   4.321 +            this.fb = fb;
   4.322 +        }
   4.323 +        
   4.324 +        Object get() throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {
   4.325 +            return pb.getValue();
   4.326 +        }
   4.327 +        
   4.328 +        void set(Object v) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {
   4.329 +            pb.setValue(v);
   4.330 +        }
   4.331 +    }
   4.332 +    
   4.333 +    static final class MapTechnology 
   4.334 +    implements Technology.ApplyId<Map<String,One>>, Transfer {
   4.335 +        private Map<String, One> appliedData;
   4.336 +        private String appliedId;
   4.337 +
   4.338 +        @Override
   4.339 +        public Map<String, One> wrapModel(Object model) {
   4.340 +            return new HashMap<String, One>();
   4.341 +        }
   4.342 +
   4.343 +        @Override
   4.344 +        public void valueHasMutated(Map<String, One> data, String propertyName) {
   4.345 +            One p = data.get(propertyName);
   4.346 +            if (p != null) {
   4.347 +                p.changes++;
   4.348 +            }
   4.349 +        }
   4.350 +
   4.351 +        @Override
   4.352 +        public void bind(PropertyBinding b, Object model, Map<String, One> data) {
   4.353 +            try {
   4.354 +                One o = new One(model, b);
   4.355 +                data.put(b.getPropertyName(), o);
   4.356 +            } catch (NoSuchMethodException ex) {
   4.357 +                throw new IllegalStateException(ex);
   4.358 +            }
   4.359 +        }
   4.360 +
   4.361 +        @Override
   4.362 +        public void expose(FunctionBinding fb, Object model, Map<String, One> data) {
   4.363 +            try {
   4.364 +                data.put(fb.getFunctionName(), new One(model, fb));
   4.365 +            } catch (NoSuchMethodException ex) {
   4.366 +                throw new IllegalStateException(ex);
   4.367 +            }
   4.368 +        }
   4.369 +
   4.370 +        @Override
   4.371 +        public void applyBindings(Map<String, One> data) {
   4.372 +            throw new UnsupportedOperationException("Never called!");
   4.373 +        }
   4.374 +
   4.375 +        @Override
   4.376 +        public Object wrapArray(Object[] arr) {
   4.377 +            return arr;
   4.378 +        }
   4.379 +
   4.380 +        @Override
   4.381 +        public void extract(Object obj, String[] props, Object[] values) {
   4.382 +            Map<?,?> map = obj instanceof Map ? (Map<?,?>)obj : null;
   4.383 +            for (int i = 0; i < Math.min(props.length, values.length); i++) {
   4.384 +                if (map == null) {
   4.385 +                    values[i] = null;
   4.386 +                } else {
   4.387 +                    values[i] = map.get(props[i]);
   4.388 +                    if (values[i] instanceof One) {
   4.389 +                        values[i] = ((One)values[i]).pb.getValue();
   4.390 +                    }
   4.391 +                }
   4.392 +            }
   4.393 +        }
   4.394 +
   4.395 +        @Override
   4.396 +        public void loadJSON(JSONCall call) {
   4.397 +            call.notifyError(new UnsupportedOperationException());
   4.398 +        }
   4.399 +
   4.400 +        @Override
   4.401 +        public <M> M toModel(Class<M> modelClass, Object data) {
   4.402 +            return modelClass.cast(data);
   4.403 +        }
   4.404 +
   4.405 +        @Override
   4.406 +        public Object toJSON(InputStream is) throws IOException {
   4.407 +            throw new IOException();
   4.408 +        }
   4.409 +
   4.410 +        @Override
   4.411 +        public void runSafe(Runnable r) {
   4.412 +            throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
   4.413 +        }
   4.414 +
   4.415 +        @Override
   4.416 +        public void applyBindings(String id, Map<String, One> data) {
   4.417 +            this.appliedId = id;
   4.418 +            this.appliedData = data;
   4.419 +        }
   4.420 +    }
   4.421 +}
     5.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     5.2 +++ b/json-beans/src/test/java/net/java/html/beans/People.java	Fri Feb 12 08:40:12 2016 +0100
     5.3 @@ -0,0 +1,78 @@
     5.4 +/**
     5.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
     5.6 + *
     5.7 + * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
     5.8 + *
     5.9 + * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
    5.10 + * Other names may be trademarks of their respective owners.
    5.11 + *
    5.12 + * The contents of this file are subject to the terms of either the GNU
    5.13 + * General Public License Version 2 only ("GPL") or the Common
    5.14 + * Development and Distribution License("CDDL") (collectively, the
    5.15 + * "License"). You may not use this file except in compliance with the
    5.16 + * License. You can obtain a copy of the License at
    5.17 + * http://www.netbeans.org/cddl-gplv2.html
    5.18 + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
    5.19 + * specific language governing permissions and limitations under the
    5.20 + * License.  When distributing the software, include this License Header
    5.21 + * Notice in each file and include the License file at
    5.22 + * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
    5.23 + * particular file as subject to the "Classpath" exception as provided
    5.24 + * by Oracle in the GPL Version 2 section of the License file that
    5.25 + * accompanied this code. If applicable, add the following below the
    5.26 + * License Header, with the fields enclosed by brackets [] replaced by
    5.27 + * your own identifying information:
    5.28 + * "Portions Copyrighted [year] [name of copyright owner]"
    5.29 + *
    5.30 + * Contributor(s):
    5.31 + *
    5.32 + * The Original Software is NetBeans. The Initial Developer of the Original
    5.33 + * Software is Oracle. Portions Copyright 2013-2014 Oracle. All Rights Reserved.
    5.34 + *
    5.35 + * If you wish your version of this file to be governed by only the CDDL
    5.36 + * or only the GPL Version 2, indicate your decision by adding
    5.37 + * "[Contributor] elects to include this software in this distribution
    5.38 + * under the [CDDL or GPL Version 2] license." If you do not indicate a
    5.39 + * single choice of license, a recipient has the option to distribute
    5.40 + * your version of this file under either the CDDL, the GPL Version 2 or
    5.41 + * to extend the choice of license to its licensees as provided above.
    5.42 + * However, if you add GPL Version 2 code and therefore, elected the GPL
    5.43 + * Version 2 license, then the option applies only if the new code is
    5.44 + * made subject to such option by the copyright holder.
    5.45 + */
    5.46 +package net.java.html.beans;
    5.47 +
    5.48 +import java.beans.PropertyChangeListener;
    5.49 +import java.beans.PropertyChangeSupport;
    5.50 +
    5.51 +/**
    5.52 + *
    5.53 + * @author Jaroslav Tulach
    5.54 + */
    5.55 +public class People {
    5.56 +    
    5.57 +    private Person[] people;
    5.58 +
    5.59 +    public static final String PROP_PEOPLE = "people";
    5.60 +
    5.61 +    public Person[] getPeople() {
    5.62 +        return people;
    5.63 +    }
    5.64 +
    5.65 +    public void setPeople(Person... people) {
    5.66 +        Person[] oldPeople = this.people;
    5.67 +        this.people = people;
    5.68 +        propertyChangeSupport.firePropertyChange(PROP_PEOPLE, oldPeople, people);
    5.69 +    }
    5.70 +
    5.71 +    private transient final PropertyChangeSupport propertyChangeSupport = new PropertyChangeSupport(this);
    5.72 +
    5.73 +    public void addPropertyChangeListener(PropertyChangeListener listener) {
    5.74 +        propertyChangeSupport.addPropertyChangeListener(listener);
    5.75 +    }
    5.76 +
    5.77 +    public void removePropertyChangeListener(PropertyChangeListener listener) {
    5.78 +        propertyChangeSupport.removePropertyChangeListener(listener);
    5.79 +    }
    5.80 +
    5.81 +}
     6.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     6.2 +++ b/json-beans/src/test/java/net/java/html/beans/Person.java	Fri Feb 12 08:40:12 2016 +0100
     6.3 @@ -0,0 +1,142 @@
     6.4 +/**
     6.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
     6.6 + *
     6.7 + * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
     6.8 + *
     6.9 + * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
    6.10 + * Other names may be trademarks of their respective owners.
    6.11 + *
    6.12 + * The contents of this file are subject to the terms of either the GNU
    6.13 + * General Public License Version 2 only ("GPL") or the Common
    6.14 + * Development and Distribution License("CDDL") (collectively, the
    6.15 + * "License"). You may not use this file except in compliance with the
    6.16 + * License. You can obtain a copy of the License at
    6.17 + * http://www.netbeans.org/cddl-gplv2.html
    6.18 + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
    6.19 + * specific language governing permissions and limitations under the
    6.20 + * License.  When distributing the software, include this License Header
    6.21 + * Notice in each file and include the License file at
    6.22 + * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
    6.23 + * particular file as subject to the "Classpath" exception as provided
    6.24 + * by Oracle in the GPL Version 2 section of the License file that
    6.25 + * accompanied this code. If applicable, add the following below the
    6.26 + * License Header, with the fields enclosed by brackets [] replaced by
    6.27 + * your own identifying information:
    6.28 + * "Portions Copyrighted [year] [name of copyright owner]"
    6.29 + *
    6.30 + * Contributor(s):
    6.31 + *
    6.32 + * The Original Software is NetBeans. The Initial Developer of the Original
    6.33 + * Software is Oracle. Portions Copyright 2013-2014 Oracle. All Rights Reserved.
    6.34 + *
    6.35 + * If you wish your version of this file to be governed by only the CDDL
    6.36 + * or only the GPL Version 2, indicate your decision by adding
    6.37 + * "[Contributor] elects to include this software in this distribution
    6.38 + * under the [CDDL or GPL Version 2] license." If you do not indicate a
    6.39 + * single choice of license, a recipient has the option to distribute
    6.40 + * your version of this file under either the CDDL, the GPL Version 2 or
    6.41 + * to extend the choice of license to its licensees as provided above.
    6.42 + * However, if you add GPL Version 2 code and therefore, elected the GPL
    6.43 + * Version 2 license, then the option applies only if the new code is
    6.44 + * made subject to such option by the copyright holder.
    6.45 + */
    6.46 +
    6.47 +package net.java.html.beans;
    6.48 +
    6.49 +import java.beans.PropertyChangeListener;
    6.50 +import java.beans.PropertyChangeSupport;
    6.51 +
    6.52 +/**
    6.53 + *
    6.54 + * @author Jaroslav Tulach
    6.55 + */
    6.56 +public final class Person {
    6.57 +    private String firstName;
    6.58 +    private String lastName;
    6.59 +    private Sex sex;
    6.60 +    private boolean good;
    6.61 +
    6.62 +    public static final String PROP_FIRSTNAME = "firstName";
    6.63 +    public static final String PROP_LASTNAME = "lastName";
    6.64 +    public static final String PROP_FULLNAME = "fullName";
    6.65 +    public static final String PROP_SEX = "sex";
    6.66 +    public static final String PROP_GOOD = "good";
    6.67 +
    6.68 +    public Person() {
    6.69 +    }
    6.70 +
    6.71 +    public Person(String firstName, String lastName, Sex sex) {
    6.72 +        this.firstName = firstName;
    6.73 +        this.lastName = lastName;
    6.74 +        this.sex = sex;
    6.75 +    }
    6.76 +
    6.77 +    public Sex getSex() {
    6.78 +        return sex;
    6.79 +    }
    6.80 +
    6.81 +    public void setSex(Sex sex) {
    6.82 +        Sex oldSex = this.sex;
    6.83 +        this.sex = sex;
    6.84 +        propertyChangeSupport.firePropertyChange(PROP_SEX, oldSex, sex);
    6.85 +    }
    6.86 +    
    6.87 +    public void changeSex(Sex to) {
    6.88 +        if (to != null) {
    6.89 +            setSex(to);
    6.90 +            return;
    6.91 +        }
    6.92 +        if (getSex() == Sex.MALE) {
    6.93 +            setSex(Sex.FEMALE);
    6.94 +        } else {
    6.95 +            setSex(Sex.MALE);
    6.96 +        }
    6.97 +    }
    6.98 +
    6.99 +    public boolean isGood() {
   6.100 +        return good;
   6.101 +    }
   6.102 +
   6.103 +    public void setGood(boolean good) {
   6.104 +        boolean oldGood = this.good;
   6.105 +        this.good = good;
   6.106 +        propertyChangeSupport.firePropertyChange(PROP_GOOD, oldGood, good);
   6.107 +    }
   6.108 +
   6.109 +    public String getFirstName() {
   6.110 +        return firstName;
   6.111 +    }
   6.112 +
   6.113 +    public void setFirstName(String firstName) {
   6.114 +        String oldFirstName = this.firstName;
   6.115 +        this.firstName = firstName;
   6.116 +        propertyChangeSupport.firePropertyChange(PROP_FIRSTNAME, oldFirstName, firstName);
   6.117 +        propertyChangeSupport.firePropertyChange(PROP_FULLNAME, null, null);
   6.118 +    }
   6.119 +
   6.120 +    public String getLastName() {
   6.121 +        return lastName;
   6.122 +    }
   6.123 +    
   6.124 +    public String getFullName() {
   6.125 +        return firstName + " " + lastName;
   6.126 +    }
   6.127 +
   6.128 +    public void setLastName(String lastName) {
   6.129 +        String oldLastName = this.lastName;
   6.130 +        this.lastName = lastName;
   6.131 +        propertyChangeSupport.firePropertyChange(PROP_LASTNAME, oldLastName, lastName);
   6.132 +        propertyChangeSupport.firePropertyChange(PROP_FULLNAME, null, null);
   6.133 +    }
   6.134 +
   6.135 +    private transient final PropertyChangeSupport propertyChangeSupport = new PropertyChangeSupport(this);
   6.136 +
   6.137 +    public void addPropertyChangeListener(PropertyChangeListener listener) {
   6.138 +        propertyChangeSupport.addPropertyChangeListener(listener);
   6.139 +    }
   6.140 +
   6.141 +    public void removePropertyChangeListener(PropertyChangeListener listener) {
   6.142 +        propertyChangeSupport.removePropertyChangeListener(listener);
   6.143 +    }
   6.144 +    
   6.145 +}
     7.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     7.2 +++ b/json-beans/src/test/java/net/java/html/beans/Sex.java	Fri Feb 12 08:40:12 2016 +0100
     7.3 @@ -0,0 +1,51 @@
     7.4 +/**
     7.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
     7.6 + *
     7.7 + * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
     7.8 + *
     7.9 + * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
    7.10 + * Other names may be trademarks of their respective owners.
    7.11 + *
    7.12 + * The contents of this file are subject to the terms of either the GNU
    7.13 + * General Public License Version 2 only ("GPL") or the Common
    7.14 + * Development and Distribution License("CDDL") (collectively, the
    7.15 + * "License"). You may not use this file except in compliance with the
    7.16 + * License. You can obtain a copy of the License at
    7.17 + * http://www.netbeans.org/cddl-gplv2.html
    7.18 + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
    7.19 + * specific language governing permissions and limitations under the
    7.20 + * License.  When distributing the software, include this License Header
    7.21 + * Notice in each file and include the License file at
    7.22 + * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
    7.23 + * particular file as subject to the "Classpath" exception as provided
    7.24 + * by Oracle in the GPL Version 2 section of the License file that
    7.25 + * accompanied this code. If applicable, add the following below the
    7.26 + * License Header, with the fields enclosed by brackets [] replaced by
    7.27 + * your own identifying information:
    7.28 + * "Portions Copyrighted [year] [name of copyright owner]"
    7.29 + *
    7.30 + * Contributor(s):
    7.31 + *
    7.32 + * The Original Software is NetBeans. The Initial Developer of the Original
    7.33 + * Software is Oracle. Portions Copyright 2013-2014 Oracle. All Rights Reserved.
    7.34 + *
    7.35 + * If you wish your version of this file to be governed by only the CDDL
    7.36 + * or only the GPL Version 2, indicate your decision by adding
    7.37 + * "[Contributor] elects to include this software in this distribution
    7.38 + * under the [CDDL or GPL Version 2] license." If you do not indicate a
    7.39 + * single choice of license, a recipient has the option to distribute
    7.40 + * your version of this file under either the CDDL, the GPL Version 2 or
    7.41 + * to extend the choice of license to its licensees as provided above.
    7.42 + * However, if you add GPL Version 2 code and therefore, elected the GPL
    7.43 + * Version 2 license, then the option applies only if the new code is
    7.44 + * made subject to such option by the copyright holder.
    7.45 + */
    7.46 +package net.java.html.beans;
    7.47 +
    7.48 +/**
    7.49 + *
    7.50 + * @author Jaroslav Tulach
    7.51 + */
    7.52 +public enum Sex {
    7.53 +    MALE, FEMALE;
    7.54 +}
     8.1 --- a/json/src/main/java/org/netbeans/html/json/spi/Proto.java	Mon Sep 21 21:19:13 2015 +0200
     8.2 +++ b/json/src/main/java/org/netbeans/html/json/spi/Proto.java	Fri Feb 12 08:40:12 2016 +0100
     8.3 @@ -563,7 +563,7 @@
     8.4          ) {
     8.5              assert getClass().getName().endsWith("$Html4JavaType");
     8.6              try {
     8.7 -                assert getClass().getDeclaringClass() == clazz;
     8.8 +                assert clazz == modelFor || getClass().getDeclaringClass() == clazz;
     8.9              } catch (SecurityException ex) {
    8.10                  // OK, no check
    8.11              }