OSGi version is good enough now - merging to default branch
authorJaroslav Tulach <jaroslav.tulach@netbeans.org>
Mon, 06 Jan 2014 09:44:07 +0100
changeset 418dff5e3d6073e
parent 366 4025de4e97b7
parent 417 5e69676597a7
child 419 5da8f4cfbdcb
OSGi version is good enough now - merging to default branch
json/src/main/java/org/netbeans/html/json/impl/Callback.java
json/src/main/java/org/netbeans/html/json/impl/FromJSON.java
json/src/main/java/org/netbeans/html/json/impl/SetAndGet.java
json/src/main/java/org/netbeans/html/json/impl/WrapperObject.java
     1.1 --- a/boot/pom.xml	Mon Dec 16 18:08:40 2013 +0100
     1.2 +++ b/boot/pom.xml	Mon Jan 06 09:44:07 2014 +0100
     1.3 @@ -24,6 +24,7 @@
     1.4                <configuration>
     1.5                    <instructions>
     1.6                        <Agent-Class>org.netbeans.html.boot.impl.JsAgent</Agent-Class>
     1.7 +                      <Eclipse-BuddyPolicy>dependent</Eclipse-BuddyPolicy>
     1.8                    </instructions>
     1.9                </configuration>
    1.10            </plugin>
     2.1 --- a/boot/src/main/java/org/netbeans/html/boot/impl/FnUtils.java	Mon Dec 16 18:08:40 2013 +0100
     2.2 +++ b/boot/src/main/java/org/netbeans/html/boot/impl/FnUtils.java	Mon Jan 06 09:44:07 2014 +0100
     2.3 @@ -80,7 +80,11 @@
     2.4          }
     2.5          Class<?> clazz;
     2.6          try (Closeable c = Fn.activate(new FnUtils())) {
     2.7 -            clazz = Class.forName(Test.class.getName(), true, l);
     2.8 +            try {
     2.9 +                clazz = Class.forName(Test.class.getName(), true, l);
    2.10 +            } catch (ClassNotFoundException ex) {
    2.11 +                clazz = Test.class;
    2.12 +            }
    2.13              final Object is = ((Callable<?>)clazz.newInstance()).call();
    2.14              return Boolean.TRUE.equals(is);
    2.15          } catch (Exception ex) {
     3.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     3.2 +++ b/equinox-agentclass-hook/pom.xml	Mon Jan 06 09:44:07 2014 +0100
     3.3 @@ -0,0 +1,42 @@
     3.4 +<?xml version="1.0" encoding="UTF-8"?>
     3.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">
     3.6 +    <modelVersion>4.0.0</modelVersion>
     3.7 +    <parent>
     3.8 +        <groupId>org.netbeans.html</groupId>
     3.9 +        <artifactId>pom</artifactId>
    3.10 +        <version>0.7-SNAPSHOT</version>
    3.11 +    </parent>
    3.12 +    <name>AgentClass Hook for Equinox</name>
    3.13 +    <artifactId>equinox-agentclass-hook</artifactId>
    3.14 +    <packaging>bundle</packaging>
    3.15 +    <build>
    3.16 +        <plugins>
    3.17 +            <plugin>
    3.18 +                <groupId>org.apache.felix</groupId>
    3.19 +                <artifactId>maven-bundle-plugin</artifactId>
    3.20 +                <configuration>
    3.21 +                    <instructions>
    3.22 +                        <Fragment-Host>org.eclipse.osgi;bundle-version="[3.8.0,4.0)"</Fragment-Host>
    3.23 +                        <Import-Package></Import-Package>
    3.24 +                    </instructions>
    3.25 +                </configuration>
    3.26 +            </plugin>
    3.27 +            <plugin>
    3.28 +                <groupId>org.apache.maven.plugins</groupId>
    3.29 +                <artifactId>maven-compiler-plugin</artifactId>
    3.30 +                <version>2.3.2</version>
    3.31 +                <configuration>
    3.32 +                    <source>1.5</source>
    3.33 +                    <target>1.5</target>
    3.34 +                </configuration>
    3.35 +            </plugin>
    3.36 +        </plugins>
    3.37 +    </build>
    3.38 +    <dependencies>
    3.39 +        <dependency>
    3.40 +            <groupId>org.eclipse</groupId>
    3.41 +            <artifactId>org.eclipse.osgi</artifactId>
    3.42 +            <version>3.8.0.v20120529-1548</version>
    3.43 +        </dependency>
    3.44 +    </dependencies>
    3.45 +</project>
    3.46 \ No newline at end of file
     4.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     4.2 +++ b/equinox-agentclass-hook/src/main/java/org/netbeans/html/equinox/agentclass/AgentHook.java	Mon Jan 06 09:44:07 2014 +0100
     4.3 @@ -0,0 +1,164 @@
     4.4 +/**
     4.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
     4.6 + *
     4.7 + * Copyright 2013-2013 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-2013 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 org.netbeans.html.equinox.agentclass;
    4.47 +
    4.48 +import java.lang.instrument.IllegalClassFormatException;
    4.49 +import java.security.ProtectionDomain;
    4.50 +import java.util.ArrayList;
    4.51 +import java.util.logging.Logger;
    4.52 +
    4.53 +import org.eclipse.osgi.baseadaptor.BaseData;
    4.54 +import org.eclipse.osgi.baseadaptor.HookConfigurator;
    4.55 +import org.eclipse.osgi.baseadaptor.HookRegistry;
    4.56 +import org.eclipse.osgi.baseadaptor.bundlefile.BundleEntry;
    4.57 +import org.eclipse.osgi.baseadaptor.hooks.ClassLoadingHook;
    4.58 +import org.eclipse.osgi.baseadaptor.loader.BaseClassLoader;
    4.59 +import org.eclipse.osgi.baseadaptor.loader.ClasspathEntry;
    4.60 +import org.eclipse.osgi.baseadaptor.loader.ClasspathManager;
    4.61 +import org.eclipse.osgi.framework.adaptor.BundleProtectionDomain;
    4.62 +import org.eclipse.osgi.framework.adaptor.BundleWatcher;
    4.63 +import org.eclipse.osgi.framework.adaptor.ClassLoaderDelegate;
    4.64 +import org.osgi.framework.Bundle;
    4.65 +import org.osgi.framework.BundleContext;
    4.66 +import org.osgi.framework.wiring.BundleWiring;
    4.67 +
    4.68 +public class AgentHook implements HookConfigurator, BundleWatcher, ClassLoadingHook {
    4.69 +    private static final Logger LOG = Logger.getLogger(AgentHook.class.getName());
    4.70 +	private boolean all;
    4.71 +	
    4.72 +	@Override
    4.73 +	public void addHooks(HookRegistry hookRegistry) {
    4.74 +		LOG.info("Agent hook for Equinox initialized!");
    4.75 +		hookRegistry.addWatcher(this);
    4.76 +		hookRegistry.addClassLoadingHook(this);
    4.77 +	}
    4.78 +
    4.79 +	@Override
    4.80 +	public void watchBundle(Bundle bundle, int type) {
    4.81 +		if (!all) {
    4.82 +			BundleContext c = bundle.getBundleContext();
    4.83 +			if (c != null) {
    4.84 +				Bundle[] arr = bundle.getBundleContext().getBundles();
    4.85 +				for (Bundle b : arr) {
    4.86 +					agentBundle(b);
    4.87 +				}
    4.88 +				all = true;
    4.89 +			}
    4.90 +		}
    4.91 +		if (type == BundleWatcher.END_ACTIVATION) {
    4.92 +			agentBundle(bundle);
    4.93 +		}
    4.94 +	}
    4.95 +
    4.96 +	private void agentBundle(Bundle bundle) {
    4.97 +		String agentClass = (String)bundle.getHeaders().get("Agent-Class");
    4.98 +		if (agentClass != null) {
    4.99 +			Class<?> agent;
   4.100 +			try {
   4.101 +				agent = bundle.loadClass(agentClass);
   4.102 +				NbInstrumentation.registerAgent(agent.getClassLoader(), agent.getName());
   4.103 +			} catch (ClassNotFoundException e) {
   4.104 +				throw new IllegalStateException(e);
   4.105 +			}
   4.106 +		}
   4.107 +	}
   4.108 +
   4.109 +	@Override
   4.110 +	public byte[] processClass(String name, byte[] bytes,
   4.111 +			ClasspathEntry ce, BundleEntry entry,
   4.112 +			ClasspathManager manager) {
   4.113 +        final BaseData bd = ce.getBaseData();
   4.114 +        if (bd == null) {
   4.115 +            return bytes;
   4.116 +        }
   4.117 +        final Bundle b = bd.getBundle();
   4.118 +        if (b == null) {
   4.119 +            return bytes;
   4.120 +        }
   4.121 +        BundleWiring w = (BundleWiring)b.adapt(BundleWiring.class);
   4.122 +        if (w == null) {
   4.123 +            return bytes;
   4.124 +        }
   4.125 +        ClassLoader loader = w.getClassLoader();
   4.126 +		try {
   4.127 +			return NbInstrumentation.patchByteCode(loader, name, ce.getDomain(), bytes);
   4.128 +		} catch (IllegalClassFormatException e) {
   4.129 +			return bytes;
   4.130 +		}
   4.131 +	}
   4.132 +
   4.133 +	@Override
   4.134 +	public boolean addClassPathEntry(ArrayList cpEntries,
   4.135 +			String cp, ClasspathManager hostmanager, BaseData sourcedata,
   4.136 +			ProtectionDomain sourcedomain) {
   4.137 +		// TODO Auto-generated method stub
   4.138 +		return false;
   4.139 +	}
   4.140 +
   4.141 +	@Override
   4.142 +	public String findLibrary(BaseData data, String libName) {
   4.143 +		// TODO Auto-generated method stub
   4.144 +		return null;
   4.145 +	}
   4.146 +
   4.147 +	@Override
   4.148 +	public ClassLoader getBundleClassLoaderParent() {
   4.149 +		// TODO Auto-generated method stub
   4.150 +		return null;
   4.151 +	}
   4.152 +
   4.153 +	@Override
   4.154 +	public BaseClassLoader createClassLoader(ClassLoader parent,
   4.155 +			ClassLoaderDelegate delegate, BundleProtectionDomain domain,
   4.156 +			BaseData data, String[] bundleclasspath) {
   4.157 +		// TODO Auto-generated method stub
   4.158 +		return null;
   4.159 +	}
   4.160 +
   4.161 +	@Override
   4.162 +	public void initializedClassLoader(BaseClassLoader baseClassLoader,
   4.163 +			BaseData data) {
   4.164 +		// TODO Auto-generated method stub
   4.165 +		
   4.166 +	}
   4.167 +}
     5.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     5.2 +++ b/equinox-agentclass-hook/src/main/java/org/netbeans/html/equinox/agentclass/NbInstrumentation.java	Mon Jan 06 09:44:07 2014 +0100
     5.3 @@ -0,0 +1,214 @@
     5.4 +/**
     5.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
     5.6 + *
     5.7 + * Copyright 2013-2013 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-2013 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 +
    5.47 +package org.netbeans.html.equinox.agentclass;
    5.48 +
    5.49 +import java.lang.instrument.ClassDefinition;
    5.50 +import java.lang.instrument.ClassFileTransformer;
    5.51 +import java.lang.instrument.IllegalClassFormatException;
    5.52 +import java.lang.instrument.Instrumentation;
    5.53 +import java.lang.instrument.UnmodifiableClassException;
    5.54 +import java.lang.reflect.InvocationTargetException;
    5.55 +import java.lang.reflect.Method;
    5.56 +import java.security.ProtectionDomain;
    5.57 +import java.util.Collection;
    5.58 +import java.util.HashSet;
    5.59 +import java.util.List;
    5.60 +import java.util.Set;
    5.61 +import java.util.concurrent.CopyOnWriteArrayList;
    5.62 +import java.util.jar.JarFile;
    5.63 +import java.util.logging.Level;
    5.64 +import java.util.logging.Logger;
    5.65 +
    5.66 +/**
    5.67 + *
    5.68 + * @author Jaroslav Tulach <jtulach@netbeans.org>
    5.69 + */
    5.70 +final class NbInstrumentation implements Instrumentation {
    5.71 +    private static final Logger LOG = Logger.getLogger(NbInstrumentation.class.getName());
    5.72 +    private static final Object LOCK = new Object();
    5.73 +    private static volatile Collection<NbInstrumentation> ACTIVE;
    5.74 +
    5.75 +    private final List<ClassFileTransformer> transformers = new CopyOnWriteArrayList<ClassFileTransformer>();
    5.76 +    private static final ThreadLocal<Boolean> IN = new ThreadLocal<Boolean>();
    5.77 +    
    5.78 +    static NbInstrumentation registerAgent(ClassLoader l, String agentClassName) {
    5.79 +        try {
    5.80 +            return registerImpl(agentClassName, l);
    5.81 +        } catch (Throwable ex) {
    5.82 +            LOG.log(Level.WARNING, "Cannot register " + agentClassName, ex);
    5.83 +            return null;
    5.84 +        }
    5.85 +    }
    5.86 +    static void unregisterAgent(NbInstrumentation instr) {
    5.87 +        synchronized (LOCK) {
    5.88 +            if (ACTIVE != null) {
    5.89 +                Collection<NbInstrumentation> clone = new HashSet<NbInstrumentation>();
    5.90 +                clone.addAll(ACTIVE);
    5.91 +                clone.remove(instr);
    5.92 +                ACTIVE = clone;
    5.93 +            }
    5.94 +        }
    5.95 +    }
    5.96 +    private static NbInstrumentation registerImpl(String agentClassName, ClassLoader l) throws ClassNotFoundException, IllegalArgumentException, NoSuchMethodException, SecurityException, IllegalAccessException, InvocationTargetException {
    5.97 +        final NbInstrumentation inst = new NbInstrumentation();
    5.98 +        synchronized (LOCK) {
    5.99 +            if (ACTIVE == null) {
   5.100 +                ACTIVE = new HashSet<NbInstrumentation>();
   5.101 +            } else {
   5.102 +                Set<NbInstrumentation> s = new HashSet<NbInstrumentation>();
   5.103 +                s.addAll(ACTIVE);
   5.104 +                ACTIVE = s;
   5.105 +            }
   5.106 +            ACTIVE.add(inst);
   5.107 +        }
   5.108 +        Class<?> agentClass = Class.forName(agentClassName, true, l);
   5.109 +        try {
   5.110 +            Method m = agentClass.getMethod("agentmain", String.class, Instrumentation.class); // NOI18N
   5.111 +            m.invoke(null, "", inst);
   5.112 +        } catch (NoSuchMethodException ex) {
   5.113 +            Method m = agentClass.getMethod("agentmain", String.class); // NOI18N
   5.114 +            m.invoke(null, "");
   5.115 +        }
   5.116 +        return inst;
   5.117 +    }
   5.118 +    
   5.119 +    public static byte[] patchByteCode(ClassLoader l, String className, ProtectionDomain pd, byte[] arr) throws IllegalClassFormatException {
   5.120 +        if (ACTIVE == null) {
   5.121 +            return arr;
   5.122 +        }
   5.123 +        if (Boolean.TRUE.equals(IN.get())) {
   5.124 +            return arr;
   5.125 +        }
   5.126 +        try {
   5.127 +            IN.set(Boolean.TRUE);
   5.128 +            for (NbInstrumentation inst : ACTIVE) {
   5.129 +                for (ClassFileTransformer t : inst.transformers) {
   5.130 +                    arr = t.transform(l, className, null, pd, arr);
   5.131 +                }
   5.132 +            }
   5.133 +        } finally {
   5.134 +            IN.set(null);
   5.135 +        }
   5.136 +        return arr;
   5.137 +    }
   5.138 +    
   5.139 +    //
   5.140 +    // Instrumentation methods
   5.141 +    //
   5.142 +
   5.143 +    @Override
   5.144 +    public void addTransformer(ClassFileTransformer transformer, boolean canRetransform) {
   5.145 +        transformers.add(transformer);
   5.146 +    }
   5.147 +
   5.148 +    @Override
   5.149 +    public void addTransformer(ClassFileTransformer transformer) {
   5.150 +        transformers.add(transformer);
   5.151 +    }
   5.152 +
   5.153 +    @Override
   5.154 +    public boolean removeTransformer(ClassFileTransformer transformer) {
   5.155 +        return transformers.remove(transformer);
   5.156 +    }
   5.157 +
   5.158 +    @Override
   5.159 +    public boolean isRetransformClassesSupported() {
   5.160 +        return false;
   5.161 +    }
   5.162 +
   5.163 +    @Override
   5.164 +    public void retransformClasses(Class<?>... classes) throws UnmodifiableClassException {
   5.165 +        throw new UnmodifiableClassException();
   5.166 +    }
   5.167 +
   5.168 +    @Override
   5.169 +    public boolean isRedefineClassesSupported() {
   5.170 +        return false;
   5.171 +    }
   5.172 +
   5.173 +    @Override
   5.174 +    public void redefineClasses(ClassDefinition... definitions) throws ClassNotFoundException, UnmodifiableClassException {
   5.175 +        throw new UnmodifiableClassException();
   5.176 +    }
   5.177 +
   5.178 +    @Override
   5.179 +    public boolean isModifiableClass(Class<?> theClass) {
   5.180 +        return false;
   5.181 +    }
   5.182 +
   5.183 +    @Override
   5.184 +    public Class[] getAllLoadedClasses() {
   5.185 +        return new Class[0];
   5.186 +    }
   5.187 +
   5.188 +    @Override
   5.189 +    public Class[] getInitiatedClasses(ClassLoader loader) {
   5.190 +        return new Class[0];
   5.191 +    }
   5.192 +
   5.193 +    @Override
   5.194 +    public long getObjectSize(Object objectToSize) {
   5.195 +        return 42;
   5.196 +    }
   5.197 +
   5.198 +    @Override
   5.199 +    public void appendToBootstrapClassLoaderSearch(JarFile jarfile) {
   5.200 +    }
   5.201 +
   5.202 +    @Override
   5.203 +    public void appendToSystemClassLoaderSearch(JarFile jarfile) {
   5.204 +        throw new UnsupportedOperationException();
   5.205 +    }
   5.206 +
   5.207 +    @Override
   5.208 +    public boolean isNativeMethodPrefixSupported() {
   5.209 +        return false;
   5.210 +    }
   5.211 +
   5.212 +    @Override
   5.213 +    public void setNativeMethodPrefix(ClassFileTransformer transformer, String prefix) {
   5.214 +        throw new UnsupportedOperationException();
   5.215 +    }
   5.216 +    
   5.217 +}
     6.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     6.2 +++ b/equinox-agentclass-hook/src/main/resources/hookconfigurators.properties	Mon Jan 06 09:44:07 2014 +0100
     6.3 @@ -0,0 +1,44 @@
     6.4 +#
     6.5 +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
     6.6 +#
     6.7 +# Copyright 2013-2013 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-2013 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 +hook.configurators=org.netbeans.equinox.agentclass.AgentHook
     7.1 --- a/json-tck/src/main/java/net/java/html/json/tests/ConvertTypesTest.java	Mon Dec 16 18:08:40 2013 +0100
     7.2 +++ b/json-tck/src/main/java/net/java/html/json/tests/ConvertTypesTest.java	Mon Jan 06 09:44:07 2014 +0100
     7.3 @@ -49,7 +49,6 @@
     7.4  import java.util.Map;
     7.5  import net.java.html.BrwsrCtx;
     7.6  import net.java.html.json.Models;
     7.7 -import org.netbeans.html.json.impl.JSON;
     7.8  import org.apidesign.html.json.tck.KOTest;
     7.9  
    7.10  /**
    7.11 @@ -86,7 +85,7 @@
    7.12      public void testConvertToPeople() throws Exception {
    7.13          final Object o = createJSON(true);
    7.14          
    7.15 -        Person p = JSON.read(newContext(), Person.class, o);
    7.16 +        Person p = Models.fromRaw(newContext(), Person.class, o);
    7.17          
    7.18          assert "son".equals(p.getFirstName()) : "First name: " + p.getFirstName();
    7.19          assert "dj".equals(p.getLastName()) : "Last name: " + p.getLastName();
    7.20 @@ -123,7 +122,7 @@
    7.21      public void testConvertToPeopleWithoutSex() throws Exception {
    7.22          final Object o = createJSON(false);
    7.23          
    7.24 -        Person p = JSON.read(newContext(), Person.class, o);
    7.25 +        Person p = Models.fromRaw(newContext(), Person.class, o);
    7.26          
    7.27          assert "son".equals(p.getFirstName()) : "First name: " + p.getFirstName();
    7.28          assert "dj".equals(p.getLastName()) : "Last name: " + p.getLastName();
     8.1 --- a/json-tck/src/main/java/net/java/html/json/tests/JSONTest.java	Mon Dec 16 18:08:40 2013 +0100
     8.2 +++ b/json-tck/src/main/java/net/java/html/json/tests/JSONTest.java	Mon Jan 06 09:44:07 2014 +0100
     8.3 @@ -49,7 +49,6 @@
     8.4  import net.java.html.json.Models;
     8.5  import net.java.html.json.OnReceive;
     8.6  import net.java.html.json.Property;
     8.7 -import org.netbeans.html.json.impl.JSON;
     8.8  import org.apidesign.html.json.tck.KOTest;
     8.9  
    8.10  /** Need to verify that models produce reasonable JSON objects.
    8.11 @@ -84,7 +83,7 @@
    8.12              throw new IllegalStateException("Can't parse " + p).initCause(ex);
    8.13          }
    8.14          
    8.15 -        Person p2 = JSON.read(newContext(), Person.class, json);
    8.16 +        Person p2 = Models.fromRaw(newContext(), Person.class, json);
    8.17          
    8.18          assert p2.getFirstName().equals(p.getFirstName()) : 
    8.19              "Should be the same: " + p.getFirstName() + " != " + p2.getFirstName();
    8.20 @@ -104,7 +103,7 @@
    8.21              throw new IllegalStateException("Can't parse " + txt).initCause(ex);
    8.22          }
    8.23          
    8.24 -        Person p2 = JSON.read(newContext(), Person.class, json);
    8.25 +        Person p2 = Models.fromRaw(newContext(), Person.class, json);
    8.26          
    8.27          assert p2.getFirstName().equals(p.getFirstName()) : 
    8.28              "Should be the same: " + p.getFirstName() + " != " + p2.getFirstName();
    8.29 @@ -124,7 +123,7 @@
    8.30              throw new IllegalStateException("Can't parse " + txt).initCause(ex);
    8.31          }
    8.32          
    8.33 -        Person p2 = JSON.read(newContext(), Person.class, json);
    8.34 +        Person p2 = Models.fromRaw(newContext(), Person.class, json);
    8.35          
    8.36          assert p2.getFirstName().equals(p.getFirstName()) : 
    8.37              "Should be the same: " + p.getFirstName() + " != " + p2.getFirstName();
    8.38 @@ -144,7 +143,7 @@
    8.39              throw new IllegalStateException("Can't parse " + txt).initCause(ex);
    8.40          }
    8.41          
    8.42 -        Person p2 = JSON.read(newContext(), Person.class, json);
    8.43 +        Person p2 = Models.fromRaw(newContext(), Person.class, json);
    8.44          
    8.45          assert p2.getFirstName().equals(p.getFirstName()) : 
    8.46              "Should be the same: " + p.getFirstName() + " != " + p2.getFirstName();
     9.1 --- a/json-tck/src/main/java/net/java/html/json/tests/KnockoutTest.java	Mon Dec 16 18:08:40 2013 +0100
     9.2 +++ b/json-tck/src/main/java/net/java/html/json/tests/KnockoutTest.java	Mon Jan 06 09:44:07 2014 +0100
     9.3 @@ -50,6 +50,7 @@
     9.4  import net.java.html.json.Models;
     9.5  import net.java.html.json.Property;
     9.6  import org.apidesign.html.json.tck.KOTest;
     9.7 +import org.apidesign.html.json.tck.KnockoutTCK;
     9.8  
     9.9  /**
    9.10   *
    10.1 --- a/json-tck/src/main/java/net/java/html/json/tests/Utils.java	Mon Dec 16 18:08:40 2013 +0100
    10.2 +++ b/json-tck/src/main/java/net/java/html/json/tests/Utils.java	Mon Jan 06 09:44:07 2014 +0100
    10.3 @@ -43,6 +43,7 @@
    10.4  package net.java.html.json.tests;
    10.5  
    10.6  import java.net.URI;
    10.7 +import java.util.Collections;
    10.8  import java.util.Map;
    10.9  import java.util.ServiceLoader;
   10.10  import net.java.html.BrwsrCtx;
   10.11 @@ -52,12 +53,18 @@
   10.12   *
   10.13   * @author Jaroslav Tulach <jtulach@netbeans.org>
   10.14   */
   10.15 -final class Utils {
   10.16 +public final class Utils {
   10.17 +    private static KnockoutTCK instantiatedTCK;
   10.18 +    
   10.19      private Utils() {
   10.20      }
   10.21 +    
   10.22 +    public static void registerTCK(KnockoutTCK tck) {
   10.23 +        instantiatedTCK = tck;
   10.24 +    }
   10.25  
   10.26      static  BrwsrCtx newContext(Class<?> clazz) {
   10.27 -        for (KnockoutTCK tck : ServiceLoader.load(KnockoutTCK.class, cl(clazz))) {
   10.28 +        for (KnockoutTCK tck : tcks(clazz)) {
   10.29              BrwsrCtx c = tck.createContext();
   10.30              if (c != null) {
   10.31                  return c;
   10.32 @@ -66,7 +73,7 @@
   10.33          throw new AssertionError("Can't find appropriate Context in ServiceLoader!");
   10.34      }
   10.35      static Object createObject(Map<String,Object> values, Class<?> clazz) {
   10.36 -        for (KnockoutTCK tck : ServiceLoader.load(KnockoutTCK.class, cl(clazz))) {
   10.37 +        for (KnockoutTCK tck : tcks(clazz)) {
   10.38              Object o = tck.createJSON(values);
   10.39              if (o != null) {
   10.40                  return o;
   10.41 @@ -77,11 +84,18 @@
   10.42      static Object executeScript(Class<?> clazz, 
   10.43          String script, Object... arguments
   10.44      ) throws Exception {
   10.45 -        for (KnockoutTCK tck : ServiceLoader.load(KnockoutTCK.class, cl(clazz))) {
   10.46 +        for (KnockoutTCK tck : tcks(clazz)) {
   10.47              return tck.executeScript(script, arguments);
   10.48          }
   10.49          throw new AssertionError("Can't find appropriate Context in ServiceLoader!");
   10.50      }
   10.51 +
   10.52 +    private static Iterable<KnockoutTCK> tcks(Class<?> clazz) {
   10.53 +        if (instantiatedTCK != null) {
   10.54 +            return Collections.singleton(instantiatedTCK);
   10.55 +        }
   10.56 +        return ServiceLoader.load(KnockoutTCK.class, cl(clazz));
   10.57 +    }
   10.58      
   10.59      static Object exposeHTML(Class<?> clazz, String html) throws Exception {
   10.60          String s = 
   10.61 @@ -98,7 +112,7 @@
   10.62  
   10.63      static String prepareURL(
   10.64          Class<?> clazz, String content, String mimeType, String... parameters) {
   10.65 -        for (KnockoutTCK tck : ServiceLoader.load(KnockoutTCK.class, cl(clazz))) {
   10.66 +        for (KnockoutTCK tck : tcks(clazz)) {
   10.67              URI o = tck.prepareURL(content, mimeType, parameters);
   10.68              if (o != null) {
   10.69                  return o.toString();
   10.70 @@ -109,7 +123,7 @@
   10.71  
   10.72      static boolean canFailWebSockets(
   10.73          Class<?> clazz) {
   10.74 -        for (KnockoutTCK tck : ServiceLoader.load(KnockoutTCK.class, cl(clazz))) {
   10.75 +        for (KnockoutTCK tck : tcks(clazz)) {
   10.76              if (tck.canFailWebSocketTest()) {
   10.77                  return true;
   10.78              }
    11.1 --- a/json-tck/src/main/java/org/apidesign/html/json/tck/KnockoutTCK.java	Mon Dec 16 18:08:40 2013 +0100
    11.2 +++ b/json-tck/src/main/java/org/apidesign/html/json/tck/KnockoutTCK.java	Mon Jan 06 09:44:07 2014 +0100
    11.3 @@ -46,9 +46,10 @@
    11.4  import java.util.Map;
    11.5  import net.java.html.BrwsrCtx;
    11.6  import net.java.html.json.tests.ConvertTypesTest;
    11.7 +import net.java.html.json.tests.JSONTest;
    11.8  import net.java.html.json.tests.KnockoutTest;
    11.9 -import net.java.html.json.tests.JSONTest;
   11.10  import net.java.html.json.tests.OperationsTest;
   11.11 +import net.java.html.json.tests.Utils;
   11.12  import net.java.html.json.tests.WebSocketTest;
   11.13  import org.openide.util.lookup.ServiceProvider;
   11.14  
   11.15 @@ -72,6 +73,7 @@
   11.16   */
   11.17  public abstract class KnockoutTCK {
   11.18      protected KnockoutTCK() {
   11.19 +        Utils.registerTCK(this);
   11.20      }
   11.21      
   11.22      /** Implement to create new context for the test. 
    12.1 --- a/json/src/main/java/org/apidesign/html/json/spi/FunctionBinding.java	Mon Dec 16 18:08:40 2013 +0100
    12.2 +++ b/json/src/main/java/org/apidesign/html/json/spi/FunctionBinding.java	Mon Jan 06 09:44:07 2014 +0100
    12.3 @@ -44,35 +44,59 @@
    12.4  
    12.5  import net.java.html.json.Function;
    12.6  import net.java.html.json.Model;
    12.7 -import org.netbeans.html.json.impl.PropertyBindingAccessor.FBData;
    12.8  
    12.9  /** Describes a function provided by the {@link Model} and 
   12.10   * annotated by {@link Function} annotation.
   12.11   *
   12.12   * @author Jaroslav Tulach <jtulach@netbeans.org>
   12.13   */
   12.14 -public final class FunctionBinding {
   12.15 -    private final FBData<?> fb;
   12.16 -    
   12.17 -    FunctionBinding(FBData<?> fb) {
   12.18 -        this.fb = fb;
   12.19 -    }
   12.20 -
   12.21 -    public String getFunctionName() {
   12.22 -        return fb.name;
   12.23 +public abstract class FunctionBinding {
   12.24 +    FunctionBinding() {
   12.25      }
   12.26      
   12.27 -    /** Calls the function provided data associated with current element,
   12.28 -     * as well as information about the event that triggered the event.
   12.29 -     * 
   12.30 +    /** Returns name of the function.
   12.31 +     * @return function name
   12.32 +     */
   12.33 +    public abstract String getFunctionName();
   12.34 +    
   12.35 +    /**
   12.36 +     * Calls the function provided data associated with current element, as well
   12.37 +     * as information about the event that triggered the event.
   12.38 +     *
   12.39       * @param data data associated with selected element
   12.40       * @param ev event (with additional properties) that triggered the event
   12.41       */
   12.42 -    public void call(Object data, Object ev) {
   12.43 -        try {
   12.44 -            fb.call(data, ev);
   12.45 -        } catch (Throwable ex) {
   12.46 -            ex.printStackTrace();
   12.47 +    public abstract void call(Object data, Object ev);
   12.48 +
   12.49 +    static <M> FunctionBinding registerFunction(String name, int index, M model, Proto.Type<M> access) {
   12.50 +        return new Impl<M>(name, index, model, access);
   12.51 +    }
   12.52 +    
   12.53 +    private static final class Impl<M> extends FunctionBinding {
   12.54 +        final String name;
   12.55 +        private final M model;
   12.56 +        private final Proto.Type<M> access;
   12.57 +        private final int index;
   12.58 +
   12.59 +        public Impl(String name, int index, M model, Proto.Type<M> access) {
   12.60 +            this.name = name;
   12.61 +            this.index = index;
   12.62 +            this.model = model;
   12.63 +            this.access = access;
   12.64 +        }
   12.65 +
   12.66 +        @Override
   12.67 +        public String getFunctionName() {
   12.68 +            return name;
   12.69 +        }
   12.70 +
   12.71 +        @Override
   12.72 +        public void call(Object data, Object ev) {
   12.73 +            try {
   12.74 +                access.call(model, index, data, ev);
   12.75 +            } catch (Throwable ex) {
   12.76 +                ex.printStackTrace();
   12.77 +            }
   12.78          }
   12.79      }
   12.80  }
    13.1 --- a/json/src/main/java/org/apidesign/html/json/spi/PropertyBinding.java	Mon Dec 16 18:08:40 2013 +0100
    13.2 +++ b/json/src/main/java/org/apidesign/html/json/spi/PropertyBinding.java	Mon Jan 06 09:44:07 2014 +0100
    13.3 @@ -43,57 +43,126 @@
    13.4  package org.apidesign.html.json.spi;
    13.5  
    13.6  import net.java.html.BrwsrCtx;
    13.7 +import org.netbeans.html.json.impl.Bindings;
    13.8 +import org.netbeans.html.json.impl.JSON;
    13.9  import org.netbeans.html.json.impl.PropertyBindingAccessor;
   13.10 -import org.netbeans.html.json.impl.PropertyBindingAccessor.PBData;
   13.11  import org.netbeans.html.json.impl.RcvrJSON;
   13.12 -import org.netbeans.html.json.impl.WrapperObject;
   13.13  
   13.14  /** Describes a property when one is asked to 
   13.15   * bind it 
   13.16   *
   13.17   * @author Jaroslav Tulach <jtulach@netbeans.org>
   13.18   */
   13.19 -public final class PropertyBinding {
   13.20 -    private final PBData<?> data;
   13.21 -    
   13.22 -    private PropertyBinding(PBData<?> p) {
   13.23 -        this.data = p;
   13.24 +public abstract class PropertyBinding {
   13.25 +    PropertyBinding() {
   13.26      }
   13.27  
   13.28      static {
   13.29          new PropertyBindingAccessor() {
   13.30              @Override
   13.31 -            protected <M> PropertyBinding newBinding(PBData<M> d) {
   13.32 -                return new PropertyBinding(d);
   13.33 +            protected JSONCall newCall(BrwsrCtx ctx, RcvrJSON callback, String urlBefore, String urlAfter, String method, Object data) {
   13.34 +                return new JSONCall(ctx, callback, urlBefore, urlAfter, method, data);
   13.35              }
   13.36  
   13.37              @Override
   13.38 -            protected <M> FunctionBinding newFunction(FBData<M> d) {
   13.39 -                return new FunctionBinding(d);
   13.40 +            protected Bindings bindings(Proto proto, boolean initialize) {
   13.41 +                return initialize ? proto.initBindings() : proto.getBindings();
   13.42              }
   13.43  
   13.44              @Override
   13.45 -            protected JSONCall newCall(BrwsrCtx ctx, RcvrJSON callback, String urlBefore, String urlAfter, String method, Object data) {
   13.46 -                return new JSONCall(ctx, callback, urlBefore, urlAfter, method, data);
   13.47 +            protected void notifyChange(Proto proto, int propIndex) {
   13.48 +                proto.onChange(propIndex);
   13.49 +            }
   13.50 +
   13.51 +            @Override
   13.52 +            protected Proto findProto(Proto.Type<?> type, Object object) {
   13.53 +                return type.protoFor(object);
   13.54 +            }
   13.55 +
   13.56 +            @Override
   13.57 +            protected <Model> Model cloneTo(Proto.Type<Model> type, Model model, BrwsrCtx c) {
   13.58 +                return type.cloneTo(model, c);
   13.59 +            }
   13.60 +
   13.61 +            @Override
   13.62 +            protected Object read(Proto.Type<?> from, BrwsrCtx c, Object data) {
   13.63 +                return from.read(c, data);
   13.64 +            }
   13.65 +
   13.66 +            @Override
   13.67 +            protected <M> PropertyBinding newBinding(
   13.68 +                Proto.Type<M> access, Bindings<?> bindings, String name,
   13.69 +                int index, M model, boolean readOnly
   13.70 +            ) {
   13.71 +                return new Impl(bindings, name, index, model, access, readOnly);
   13.72              }
   13.73          };
   13.74      }
   13.75  
   13.76 -    public String getPropertyName() {
   13.77 -        return data.name;
   13.78 -    }
   13.79 +    /** Name of the property this binding represents.
   13.80 +     * @return name of the property
   13.81 +     */
   13.82 +    public abstract String getPropertyName();
   13.83  
   13.84 -    public void setValue(Object v) {
   13.85 -        data.setValue(v);
   13.86 -    }
   13.87 +    /** Changes value of the property. Can be called only on dedicated
   13.88 +     * thread. See {@link Technology#runSafe(java.lang.Runnable)}.
   13.89 +     * 
   13.90 +     * @param v new value of the property
   13.91 +     */
   13.92 +    public abstract void setValue(Object v);
   13.93      
   13.94 -    public Object getValue() {
   13.95 -        Object v = data.getValue();
   13.96 -        Object r = WrapperObject.find(v, data.getBindings());
   13.97 -        return r == null ? v : r;
   13.98 -    }
   13.99 +    /** Obtains current value of the property this binding represents.
  13.100 +     * Can be called only on dedicated
  13.101 +     * thread. See {@link Technology#runSafe(java.lang.Runnable)}.
  13.102 +     * 
  13.103 +     * @return the value or <code>null</code>
  13.104 +     */
  13.105 +    public abstract Object getValue();
  13.106      
  13.107 -    public boolean isReadOnly() {
  13.108 -        return data.isReadOnly();
  13.109 -    }
  13.110 +    /** Is this property read only? Or can one call {@link #setValue(java.lang.Object)}?
  13.111 +     * 
  13.112 +     * @return true, if this property is read only
  13.113 +     */
  13.114 +    public abstract boolean isReadOnly();
  13.115 +    
  13.116 +    private static final class Impl<M> extends PropertyBinding {
  13.117 +        public final String name;
  13.118 +        public final boolean readOnly;
  13.119 +        private final M model;
  13.120 +        private final Proto.Type<M> access;
  13.121 +        private final Bindings<?> bindings;
  13.122 +        private final int index;
  13.123 +
  13.124 +        public Impl(Bindings<?> bindings, String name, int index, M model, Proto.Type<M> access, boolean readOnly) {
  13.125 +            this.bindings = bindings;
  13.126 +            this.name = name;
  13.127 +            this.index = index;
  13.128 +            this.model = model;
  13.129 +            this.access = access;
  13.130 +            this.readOnly = readOnly;
  13.131 +        }
  13.132 +
  13.133 +        @Override
  13.134 +        public void setValue(Object v) {
  13.135 +            access.setValue(model, index, v);
  13.136 +        }
  13.137 +
  13.138 +        @Override
  13.139 +        public Object getValue() {
  13.140 +            Object v = access.getValue(model, index);
  13.141 +            Object r = JSON.find(v, bindings);
  13.142 +            return r == null ? v : r;
  13.143 +        }
  13.144 +
  13.145 +        @Override
  13.146 +        public boolean isReadOnly() {
  13.147 +            return readOnly;
  13.148 +        }
  13.149 +
  13.150 +        @Override
  13.151 +        public String getPropertyName() {
  13.152 +            return name;
  13.153 +        }
  13.154 +    } // end of PBData
  13.155 +    
  13.156  }
    14.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    14.2 +++ b/json/src/main/java/org/apidesign/html/json/spi/Proto.java	Mon Jan 06 09:44:07 2014 +0100
    14.3 @@ -0,0 +1,656 @@
    14.4 +/**
    14.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
    14.6 + *
    14.7 + * Copyright 2013-2013 Oracle and/or its affiliates. All rights reserved.
    14.8 + *
    14.9 + * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
   14.10 + * Other names may be trademarks of their respective owners.
   14.11 + *
   14.12 + * The contents of this file are subject to the terms of either the GNU
   14.13 + * General Public License Version 2 only ("GPL") or the Common
   14.14 + * Development and Distribution License("CDDL") (collectively, the
   14.15 + * "License"). You may not use this file except in compliance with the
   14.16 + * License. You can obtain a copy of the License at
   14.17 + * http://www.netbeans.org/cddl-gplv2.html
   14.18 + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
   14.19 + * specific language governing permissions and limitations under the
   14.20 + * License.  When distributing the software, include this License Header
   14.21 + * Notice in each file and include the License file at
   14.22 + * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
   14.23 + * particular file as subject to the "Classpath" exception as provided
   14.24 + * by Oracle in the GPL Version 2 section of the License file that
   14.25 + * accompanied this code. If applicable, add the following below the
   14.26 + * License Header, with the fields enclosed by brackets [] replaced by
   14.27 + * your own identifying information:
   14.28 + * "Portions Copyrighted [year] [name of copyright owner]"
   14.29 + *
   14.30 + * Contributor(s):
   14.31 + *
   14.32 + * The Original Software is NetBeans. The Initial Developer of the Original
   14.33 + * Software is Oracle. Portions Copyright 2013-2013 Oracle. All Rights Reserved.
   14.34 + *
   14.35 + * If you wish your version of this file to be governed by only the CDDL
   14.36 + * or only the GPL Version 2, indicate your decision by adding
   14.37 + * "[Contributor] elects to include this software in this distribution
   14.38 + * under the [CDDL or GPL Version 2] license." If you do not indicate a
   14.39 + * single choice of license, a recipient has the option to distribute
   14.40 + * your version of this file under either the CDDL, the GPL Version 2 or
   14.41 + * to extend the choice of license to its licensees as provided above.
   14.42 + * However, if you add GPL Version 2 code and therefore, elected the GPL
   14.43 + * Version 2 license, then the option applies only if the new code is
   14.44 + * made subject to such option by the copyright holder.
   14.45 + */
   14.46 +package org.apidesign.html.json.spi;
   14.47 +
   14.48 +import java.util.Collection;
   14.49 +import java.util.List;
   14.50 +import net.java.html.BrwsrCtx;
   14.51 +import net.java.html.json.ComputedProperty;
   14.52 +import net.java.html.json.Model;
   14.53 +import org.netbeans.html.json.impl.Bindings;
   14.54 +import org.netbeans.html.json.impl.JSON;
   14.55 +import org.netbeans.html.json.impl.JSONList;
   14.56 +import org.netbeans.html.json.impl.RcvrJSON;
   14.57 +import org.netbeans.html.json.impl.RcvrJSON.MsgEvnt;
   14.58 +
   14.59 +/** Object associated with one instance of a model generated by the
   14.60 + * {@link Model} annotation. Contains methods the generated class can
   14.61 + * use to communicate with behind the scene associated {@link Technology}.
   14.62 + * Each {@link Proto} object is associated with <a href="http://wiki.apidesign.org/wiki/Singletonizer">
   14.63 + * singletonizer</a>-like interface {@link Type} which provides the 
   14.64 + * associated {@link Technology} the necessary information about the 
   14.65 + * generated {@link Model} class.
   14.66 + *
   14.67 + * @author Jaroslav Tulach <jtulach@netbeans.org>
   14.68 + * @since 0.7
   14.69 + */
   14.70 +public final class Proto {
   14.71 +    private final Object obj;
   14.72 +    private final Type type;
   14.73 +    private final net.java.html.BrwsrCtx context;
   14.74 +    private boolean locked;
   14.75 +    private org.netbeans.html.json.impl.Bindings ko;
   14.76 +
   14.77 +    Proto(Object obj, Type type, BrwsrCtx context) {
   14.78 +        this.obj = obj;
   14.79 +        this.type = type;
   14.80 +        this.context = context;
   14.81 +    }
   14.82 +
   14.83 +    /** Browser context this proto object and its associated model
   14.84 +     * are operating-in.
   14.85 +     * 
   14.86 +     * @return the associated context 
   14.87 +     */
   14.88 +    public BrwsrCtx getContext() {
   14.89 +        return context;
   14.90 +    }
   14.91 +
   14.92 +    /** Before doing modification of the model properties, the
   14.93 +     * generated code enters write lock by calling this method.
   14.94 +     * @throws IllegalStateException if already locked
   14.95 +     */
   14.96 +    public void acquireLock() throws IllegalStateException {
   14.97 +        if (locked) throw new IllegalStateException();
   14.98 +        locked = true;
   14.99 +    }
  14.100 +    
  14.101 +    /** Verifies the model is not locked otherwise throws an exception.
  14.102 +     * @throws IllegalStateException if the model is locked
  14.103 +     */
  14.104 +    public void verifyUnlocked() throws IllegalStateException {
  14.105 +        if (locked) throw new IllegalStateException();
  14.106 +    }
  14.107 +    
  14.108 +    /** When modifications are over, the model is switched into 
  14.109 +     * unlocked state by calling this method.
  14.110 +     */
  14.111 +    public void releaseLock() {
  14.112 +        locked = false;
  14.113 +    }
  14.114 +    
  14.115 +    /** Whenever model changes a property. It should notify the
  14.116 +     * associated technology by calling this method.
  14.117 +     * 
  14.118 +     *@param propName name of the changed property
  14.119 +     */
  14.120 +    public void valueHasMutated(String propName) {
  14.121 +        if (ko != null) {
  14.122 +            ko.valueHasMutated(propName);
  14.123 +        }
  14.124 +    }
  14.125 +    
  14.126 +    /** Initializes the associated model in the current {@link #getContext() context}.
  14.127 +     * In case of <em>knockout.js</em> technology, applies given bindings 
  14.128 +     * of the current model to the <em>body</em> element of the page.
  14.129 +     */
  14.130 +    public void applyBindings() {
  14.131 +        initBindings().applyBindings();
  14.132 +    }
  14.133 +    
  14.134 +    /** Invokes the provided runnable in the {@link #getContext() context}
  14.135 +     * of the browser. If the caller is already on the right thread, the
  14.136 +     * <code>run.run()</code> is invoked immediately and synchronously. 
  14.137 +     * Otherwise the method returns immediately and the <code>run()</code>
  14.138 +     * method is performed later
  14.139 +     * 
  14.140 +     * @param run the action to execute
  14.141 +     */
  14.142 +    public void runInBrowser(Runnable run) {
  14.143 +        JSON.runInBrowser(context, run);
  14.144 +    }
  14.145 +
  14.146 +    /** Initializes the provided collection with a content of the <code>array</code>.
  14.147 +     * The initialization can only be done soon after the the collection 
  14.148 +     * is created, otherwise an exception is throw
  14.149 +     * 
  14.150 +     * @param to the collection to initialize (assumed to be empty)
  14.151 +     * @param array the array to add to the collection
  14.152 +     * @throws IllegalStateException if the system has already been initialized
  14.153 +     */
  14.154 +    public void initTo(Collection<?> to, Object array) {
  14.155 +        if (ko != null) {
  14.156 +            throw new IllegalStateException();
  14.157 +        }
  14.158 +        if (to instanceof JSONList) {
  14.159 +           ((JSONList)to).init(array);
  14.160 +        } else {
  14.161 +            JSONList.init(to, array);
  14.162 +        }
  14.163 +    }
  14.164 +
  14.165 +    /** Takes an object representing JSON result and extract some of its
  14.166 +     * properties. It is assumed that the <code>props</code> and
  14.167 +     * <code>values</code> arrays have the same length.
  14.168 +     * 
  14.169 +     * @param json the JSON object (actual type depends on the associated
  14.170 +     *   {@link Technology})
  14.171 +     * @param props list of properties to extract
  14.172 +     * @param values array that will be filled with extracted values
  14.173 +     */
  14.174 +    public void extract(Object json, String[] props, Object[] values) {
  14.175 +        JSON.extract(context, json, props, values);
  14.176 +    }
  14.177 +
  14.178 +    /** Converts raw JSON <code>data</code> into a Java {@link Model} class.
  14.179 +     * 
  14.180 +     * @param <T> type of the model class
  14.181 +     * @param modelClass the type of the class to create
  14.182 +     * @param data the raw JSON data
  14.183 +     * @return newly created instance of the model class
  14.184 +     */
  14.185 +    public <T> T read(Class<T> modelClass, Object data) {
  14.186 +        return JSON.read(context, modelClass, data);
  14.187 +    }
  14.188 +
  14.189 +    /** Initializes asynchronous JSON connection to specified URL. The 
  14.190 +     * method returns immediately and later does callback later.
  14.191 +     * 
  14.192 +     * @param index the callback index to be used when a reply is received
  14.193 +     *   to call {@link Type#onMessage(java.lang.Object, int, int, java.lang.Object)}.
  14.194 +     * 
  14.195 +     * @param urlBefore the part of the URL before JSON-P callback parameter
  14.196 +     * @param urlAfter the rest of the URL or <code>null</code> if no JSON-P is used
  14.197 +     * @param method method to use for connection to the server
  14.198 +     * @param data string, number or a {@link Model} generated class to send to
  14.199 +     *    the server when doing a query
  14.200 +     */
  14.201 +    public void loadJSON(final int index, 
  14.202 +        String urlBefore, String urlAfter, String method,
  14.203 +        final Object data
  14.204 +    ) {
  14.205 +        class Rcvr extends RcvrJSON {
  14.206 +            @Override
  14.207 +            protected void onMessage(MsgEvnt msg) {
  14.208 +                type.onMessage(obj, index, 1, msg.getValues());
  14.209 +            }
  14.210 +
  14.211 +            @Override
  14.212 +            protected void onError(MsgEvnt msg) {
  14.213 +                type.onMessage(obj, index, 2, msg.getException());
  14.214 +            }
  14.215 +        }
  14.216 +        JSON.loadJSON(context, new Rcvr(), urlBefore, urlAfter, method, data);
  14.217 +    }
  14.218 +    
  14.219 +    /** Opens new WebSocket connection to the specified URL. 
  14.220 +     * 
  14.221 +     * @param index the index to use later during callbacks to 
  14.222 +     *   {@link Type#onMessage(java.lang.Object, int, int, java.lang.Object)}
  14.223 +     * @param url the <code>ws://</code> or <code>wss://</code> URL to connect to
  14.224 +     * @param data data to send to server (usually <code>null</code>)
  14.225 +     * @return returns a non-null object representing the socket
  14.226 +     *   which can be used when calling {@link #wsSend(java.lang.Object, java.lang.String, java.lang.Object) }
  14.227 +     */
  14.228 +    public Object wsOpen(final int index, String url, Object data) {
  14.229 +        class WSrcvr extends RcvrJSON {
  14.230 +            @Override
  14.231 +            protected void onError(MsgEvnt msg) {
  14.232 +                type.onMessage(obj, index, 2, msg.getException());
  14.233 +            }
  14.234 +            
  14.235 +            @Override
  14.236 +            protected void onMessage(MsgEvnt msg) {
  14.237 +                type.onMessage(obj, index, 1, msg.getValues());
  14.238 +            }
  14.239 +            
  14.240 +            @Override
  14.241 +            protected void onClose(MsgEvnt msg) {
  14.242 +                type.onMessage(obj, index, 3, null);
  14.243 +            }
  14.244 +
  14.245 +            @Override
  14.246 +            protected void onOpen(MsgEvnt msg) {
  14.247 +                type.onMessage(obj, index, 0, null);
  14.248 +            }
  14.249 +        }
  14.250 +        return JSON.openWS(context, new WSrcvr(), url, data);
  14.251 +    }
  14.252 +    
  14.253 +    /** Sends a message to existing socket.
  14.254 +     * 
  14.255 +     * @param webSocket the socket to send message to
  14.256 +     * @param url the <code>ws://</code> or <code>wss://</code> URL to connect to,
  14.257 +     *    preferably the same as the one used when the socket was 
  14.258 +     *    {@link #wsOpen(int, java.lang.String, java.lang.Object) opened}
  14.259 +     * @param data the data to send or <code>null</code> if the socket is
  14.260 +     *    supposed to be closed
  14.261 +     */
  14.262 +    public void wsSend(Object webSocket, String url, Object data) {
  14.263 +        ((JSON.WS)webSocket).send(context, url, data);
  14.264 +    }
  14.265 +
  14.266 +    /** Converts raw data (one of its properties) to string representation.
  14.267 +     * 
  14.268 +     * @param data the object
  14.269 +     * @param propName the name of object property or <code>null</code>
  14.270 +     *   if the whole object should be converted
  14.271 +     * @return the string representation of the object or its property
  14.272 +     */
  14.273 +    public String toString(Object data, String propName) {
  14.274 +        return JSON.toString(context, data, propName);
  14.275 +    }
  14.276 +    
  14.277 +    /** Converts raw data (one of its properties) to a number representation.
  14.278 +     * 
  14.279 +     * @param data the object
  14.280 +     * @param propName the name of object property or <code>null</code>
  14.281 +     *   if the whole object should be converted
  14.282 +     * @return the number representation of the object or its property
  14.283 +     */
  14.284 +    public Number toNumber(Object data, String propName) {
  14.285 +        return JSON.toNumber(context, data, propName);
  14.286 +    }
  14.287 +
  14.288 +    /** Converts raw JSON data into a {@link Model} class representation.
  14.289 +     * 
  14.290 +     * @param <T> type of the model to create
  14.291 +     * @param type class of the model to create
  14.292 +     * @param data raw JSON data (depends on associated {@link Technology})
  14.293 +     * @return new instances of the model class filled with values from the
  14.294 +     *   <code>data</code> object
  14.295 +     */
  14.296 +    public <T> T toModel(Class<T> type, Object data) {
  14.297 +        return JSON.toModel(context, type, data, null);
  14.298 +    }
  14.299 +
  14.300 +    /** Creates new JSON like observable list.
  14.301 +     * 
  14.302 +     * @param <T> the type of the list elements
  14.303 +     * @param propName name of a property this list is associated with
  14.304 +     * @param onChange index of the property to use when the list is modified
  14.305 +     *   during callback to {@link Type#onChange(java.lang.Object, int)}
  14.306 +     * @param dependingProps the array of {@link ComputedProperty derived properties}
  14.307 +     *   that depend on the value of the list
  14.308 +     * @return new, empty list associated with this proto-object and its model
  14.309 +     */
  14.310 +    public <T> List<T> createList(String propName, int onChange, String... dependingProps) {
  14.311 +        return new JSONList<T>(this, propName, onChange, dependingProps);
  14.312 +    }
  14.313 +
  14.314 +    /** Copies content of one collection to another, re-assigning all its
  14.315 +     * elements from their current context to the new <code>ctx</code>.
  14.316 +     * 
  14.317 +     * @param <T> type of the collections
  14.318 +     * @param to the target collection to be filled with cloned values
  14.319 +     * @param ctx context for the new collection
  14.320 +     * @param from original collection with its data
  14.321 +     */
  14.322 +    public <T> void cloneList(Collection<T> to, BrwsrCtx ctx, Collection<T> from) {
  14.323 +        Boolean isModel = null;
  14.324 +        for (T t : from) {
  14.325 +            if (isModel == null) {
  14.326 +                isModel = JSON.isModel(t.getClass());
  14.327 +            }
  14.328 +            if (isModel) {
  14.329 +                to.add(JSON.bindTo(t, ctx));
  14.330 +            } else {
  14.331 +                to.add(t);
  14.332 +            }
  14.333 +        }
  14.334 +    }
  14.335 +    
  14.336 +    //
  14.337 +    // internal state
  14.338 +    //
  14.339 +    
  14.340 +    
  14.341 +    final Bindings initBindings() {
  14.342 +        if (ko == null) {
  14.343 +            Bindings b = Bindings.apply(context, obj);
  14.344 +            PropertyBinding[] pb = new PropertyBinding[type.propertyNames.length];
  14.345 +            for (int i = 0; i < pb.length; i++) {
  14.346 +                pb[i] = b.registerProperty(
  14.347 +                    type.propertyNames[i], i, obj, type, type.propertyReadOnly[i]
  14.348 +                );
  14.349 +            }
  14.350 +            FunctionBinding[] fb = new FunctionBinding[type.functions.length];
  14.351 +            for (int i = 0; i < fb.length; i++) {
  14.352 +                fb[i] = FunctionBinding.registerFunction(
  14.353 +                    type.functions[i], i, obj, type
  14.354 +                );
  14.355 +            }
  14.356 +            ko = b;
  14.357 +            b.finish(obj, pb, fb);
  14.358 +        }
  14.359 +        return ko;
  14.360 +    }
  14.361 +
  14.362 +    final Bindings getBindings() {
  14.363 +        return ko;
  14.364 +    }
  14.365 +
  14.366 +    final void onChange(int index) {
  14.367 +        type.onChange(obj, index);
  14.368 +    }
  14.369 +
  14.370 +    /** Functionality used by the code generated by annotation
  14.371 +     * processor for the {@link net.java.html.json.Model} annotation.
  14.372 +     * 
  14.373 +     * @param <Model> the generated class
  14.374 +     * @since 0.7
  14.375 +     */
  14.376 +    public static abstract class Type<Model> {
  14.377 +        private final Class<Model> clazz;
  14.378 +        private final String[] propertyNames;
  14.379 +        private final boolean[] propertyReadOnly;
  14.380 +        private final String[] functions;
  14.381 +
  14.382 +        /** Constructor for subclasses generated by the annotation processor
  14.383 +         * associated with {@link net.java.html.json.Model} annotation.
  14.384 +         * 
  14.385 +         * @param clazz the generated model class
  14.386 +         * @param modelFor the original class annotated by the {@link net.java.html.json.Model} annotation.
  14.387 +         * @param properties number of properties the class has
  14.388 +         * @param functions  number of functions the class has
  14.389 +         */
  14.390 +        protected Type(
  14.391 +            Class<Model> clazz, Class<?> modelFor, int properties, int functions
  14.392 +        ) {
  14.393 +            assert getClass().getName().endsWith("$Html4JavaType");
  14.394 +            assert getClass().getDeclaringClass() == clazz;
  14.395 +            this.clazz = clazz;
  14.396 +            this.propertyNames = new String[properties];
  14.397 +            this.propertyReadOnly = new boolean[properties];
  14.398 +            this.functions = new String[functions];
  14.399 +            JSON.register(clazz, this);
  14.400 +        }
  14.401 +
  14.402 +        /** Registers property for the type. It is expected each index
  14.403 +         * is initialized only once.
  14.404 +         * 
  14.405 +         * @param name name of the property
  14.406 +         * @param index index of the property
  14.407 +         * @param readOnly is the property read only?
  14.408 +         */
  14.409 +        protected final void registerProperty(String name, int index, boolean readOnly) {
  14.410 +            assert propertyNames[index] == null;
  14.411 +            propertyNames[index] = name;
  14.412 +            propertyReadOnly[index] = readOnly;
  14.413 +        }
  14.414 +
  14.415 +        /** Registers function of given name at given index.
  14.416 +         * 
  14.417 +         * @param name name of the function
  14.418 +         * @param index name of the type
  14.419 +         */
  14.420 +        protected final void registerFunction(String name, int index) {
  14.421 +            assert functions[index] == null;
  14.422 +            functions[index] = name;
  14.423 +        }
  14.424 +        
  14.425 +        /** Creates new proto-object for given {@link Model} class bound to
  14.426 +         * provided context.
  14.427 +         * 
  14.428 +         * @param obj instance of appropriate {@link Model} class
  14.429 +         * @param context the browser context
  14.430 +         * @return new proto-object that the generated class can use for
  14.431 +         *   communication with the infrastructure
  14.432 +         */
  14.433 +        public Proto createProto(Object obj, BrwsrCtx context) {
  14.434 +            return new Proto(obj, this, context);
  14.435 +        }
  14.436 +        
  14.437 +        //
  14.438 +        // Implemented by subclasses
  14.439 +        //
  14.440 +        
  14.441 +        /** Sets value of a {@link #registerProperty(java.lang.String, int, boolean) registered property}
  14.442 +         * to new value.
  14.443 +         * 
  14.444 +         * @param model the instance of {@link Model model class}
  14.445 +         * @param index index of the property used during registration
  14.446 +         * @param value the value to set the property to
  14.447 +         */
  14.448 +        protected abstract void setValue(Model model, int index, Object value);
  14.449 +        
  14.450 +        /** Obtains and returns value of a 
  14.451 +         * {@link #registerProperty(java.lang.String, int, boolean) registered property}.
  14.452 +         * 
  14.453 +         * @param model the instance of {@link Model model class}
  14.454 +         * @param index index of the property used during registration
  14.455 +         * @return current value of the property
  14.456 +         */
  14.457 +        protected abstract Object getValue(Model model, int index);
  14.458 +        
  14.459 +        /** Invokes a {@link #registerFunction(java.lang.String, int) registered function
  14.460 +         * on given object.
  14.461 +         * 
  14.462 +         * @param model the instance of {@link Model model class}
  14.463 +         * @param index index of the property used during registration
  14.464 +         * @param data the currently selected object the function is about to operate on
  14.465 +         * @param event the event that triggered the event
  14.466 +         */
  14.467 +        protected abstract void call(Model model, int index, Object data, Object event);
  14.468 +        
  14.469 +        /** Re-binds the model object to new browser context.
  14.470 +         * 
  14.471 +         * @param model the instance of {@link Model model class}
  14.472 +         * @param ctx browser context to clone the object to
  14.473 +         * @return new instance of the model suitable for new context
  14.474 +         */
  14.475 +        protected abstract Model cloneTo(Model model, BrwsrCtx ctx);
  14.476 +        
  14.477 +        /** Reads raw JSON data and converts them to our model class.
  14.478 +         * 
  14.479 +         * @param c the browser context to work in
  14.480 +         * @param json raw JSON data to get values from
  14.481 +         * @return new instance of model class filled by the data
  14.482 +         */
  14.483 +        protected abstract Model read(BrwsrCtx c, Object json);
  14.484 +        
  14.485 +        /** Called when a {@link #registerProperty(java.lang.String, int, boolean) registered property}
  14.486 +         * changes its value.
  14.487 +         * 
  14.488 +         * @param model the object that has the property
  14.489 +         * @param index the index of the property during registration
  14.490 +         */
  14.491 +        protected abstract void onChange(Model model, int index);
  14.492 +        
  14.493 +        /** Finds out if there is an associated proto-object for given
  14.494 +         * object.
  14.495 +         * 
  14.496 +         * @param object an object, presumably (but not necessarily) instance of Model class
  14.497 +         * @return associated proto-object or <code>null</code>
  14.498 +         */
  14.499 +        protected abstract Proto protoFor(Object object);
  14.500 +
  14.501 +        /** Called to report results of asynchronous over-the-wire 
  14.502 +         * communication. Result of calling {@link Proto#wsOpen(int, java.lang.String, java.lang.Object)}
  14.503 +         * or {@link Proto#loadJSON(int, java.lang.String, java.lang.String, java.lang.String, java.lang.Object)}.
  14.504 +         * 
  14.505 +         * @param model the instance of the model class
  14.506 +         * @param index index used during initiating the communication (via <code>loadJSON</code> or <code>wsOpen</code> calls)
  14.507 +         * @param type type of the message: 0 - onOpen, 1 - onMessage, 2 - onError, 3 - onClose -
  14.508 +         *   not all messages are applicable to all communication protocols (JSON has only 1 and 2).
  14.509 +         * @param data <code>null</code> or string, number or a {@link Model} class
  14.510 +         *   obtained to the server as a response
  14.511 +         */
  14.512 +        protected abstract void onMessage(Model model, int index, int type, Object data);
  14.513 +
  14.514 +        //
  14.515 +        // Various support methods the generated classes use
  14.516 +        //
  14.517 +
  14.518 +        /** Converts and array of raw JSON objects into an array of typed
  14.519 +         * Java {@lin Model} classes.
  14.520 +         * 
  14.521 +         * @param <T> the type of the destination array
  14.522 +         * @param context browser context to use
  14.523 +         * @param src array of raw JSON objects
  14.524 +         * @param destType type of the individual array elements
  14.525 +         * @param dest array to be filled with read type instances
  14.526 +         */
  14.527 +        public <T> void copyJSON(BrwsrCtx context, Object[] src, Class<T> destType, T[] dest) {
  14.528 +            for (int i = 0; i < src.length && i < dest.length; i++) {
  14.529 +                dest[i] = org.netbeans.html.json.impl.JSON.read(context, destType, src[i]);
  14.530 +            }
  14.531 +        }
  14.532 +        
  14.533 +        /** Compares two objects that can be converted to integers.
  14.534 +         * @return true if they are the same
  14.535 +         */
  14.536 +        public final boolean isSame(int a, int b) {
  14.537 +            return a == b;
  14.538 +        }
  14.539 +
  14.540 +        /** Compares two objects that can be converted to (floating point)
  14.541 +         * numbers.
  14.542 +         * @return  true if they are the same
  14.543 +         */
  14.544 +        public final boolean isSame(double a, double b) {
  14.545 +            return a == b;
  14.546 +        }
  14.547 +
  14.548 +        /** Compares two objects for being the same - e.g. either <code>==</code>
  14.549 +         * or <code>equals</code>.
  14.550 +         * @return true if they are equals
  14.551 +         */ 
  14.552 +        public final boolean isSame(Object a, Object b) {
  14.553 +            if (a == b) {
  14.554 +                return true;
  14.555 +            }
  14.556 +            if (a == null || b == null) {
  14.557 +                return false;
  14.558 +            }
  14.559 +            return a.equals(b);
  14.560 +        }
  14.561 +
  14.562 +        /** Cumulative hash function. Adds hashcode of the object to the
  14.563 +         * previous value.
  14.564 +         * @param o the object (or <code>null</code>)
  14.565 +         * @param h the previous value of the hash
  14.566 +         * @return new hash - the old one xor the object's one
  14.567 +         */
  14.568 +        public final int hashPlus(Object o, int h) {
  14.569 +            return o == null ? h : h ^ o.hashCode();
  14.570 +        }
  14.571 +        
  14.572 +        /** Converts an object to its JSON value.
  14.573 +         * 
  14.574 +         * @param obj the object to convert
  14.575 +         * @return JSON representation of the object
  14.576 +         */
  14.577 +        public final String toJSON(Object obj) {
  14.578 +            return JSON.toJSON(obj);
  14.579 +        }
  14.580 +        
  14.581 +        /** Converts the value to string.
  14.582 +         * 
  14.583 +         * @param val the value
  14.584 +         * @return the converted value
  14.585 +         */
  14.586 +        public final String stringValue(Object val) {
  14.587 +            return JSON.stringValue(val);
  14.588 +        }
  14.589 +
  14.590 +        /** Converts the value to number.
  14.591 +         * 
  14.592 +         * @param val the value
  14.593 +         * @return the converted value
  14.594 +         */
  14.595 +        public final Number numberValue(Object val) {
  14.596 +            return JSON.numberValue(val);
  14.597 +        }
  14.598 +
  14.599 +        /** Converts the value to character.
  14.600 +         * 
  14.601 +         * @param val the value
  14.602 +         * @return the converted value
  14.603 +         */
  14.604 +        public final Character charValue(Object val) {
  14.605 +            return JSON.charValue(val);
  14.606 +        }
  14.607 +
  14.608 +        /** Converts the value to boolean.
  14.609 +         * 
  14.610 +         * @param val the value
  14.611 +         * @return the converted value
  14.612 +         */
  14.613 +        public final Boolean boolValue(Object val) {
  14.614 +            return JSON.boolValue(val);
  14.615 +        }
  14.616 +        
  14.617 +        /** Extracts value of specific type from given object.
  14.618 +         * 
  14.619 +         * @param <T> the type of object one is interested in
  14.620 +         * @param type the type
  14.621 +         * @param val the object to convert to type
  14.622 +         * @return the converted value
  14.623 +         */
  14.624 +        public final <T> T extractValue(Class<T> type, Object val) {
  14.625 +            if (Number.class.isAssignableFrom(type)) {
  14.626 +                val = numberValue(val);
  14.627 +            }
  14.628 +            if (Boolean.class == type) {
  14.629 +                val = boolValue(val);
  14.630 +            }
  14.631 +            if (String.class == type) {
  14.632 +                val = stringValue(val);
  14.633 +            }
  14.634 +            if (Character.class == type) {
  14.635 +                val = charValue(val);
  14.636 +            }
  14.637 +            if (Integer.class == type) {
  14.638 +                val = val instanceof Number ? ((Number) val).intValue() : 0;
  14.639 +            }
  14.640 +            if (Long.class == type) {
  14.641 +                val = val instanceof Number ? ((Number) val).longValue() : 0;
  14.642 +            }
  14.643 +            if (Short.class == type) {
  14.644 +                val = val instanceof Number ? ((Number) val).shortValue() : 0;
  14.645 +            }
  14.646 +            if (Byte.class == type) {
  14.647 +                val = val instanceof Number ? ((Number) val).byteValue() : 0;
  14.648 +            }
  14.649 +            if (Double.class == type) {
  14.650 +                val = val instanceof Number ? ((Number) val).doubleValue() : Double.NaN;
  14.651 +            }
  14.652 +            if (Float.class == type) {
  14.653 +                val = val instanceof Number ? ((Number) val).floatValue() : Float.NaN;
  14.654 +            }
  14.655 +            return type.cast(val);
  14.656 +        }
  14.657 +
  14.658 +    }
  14.659 +}
    15.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    15.2 +++ b/json/src/main/java/org/apidesign/html/json/spi/package.html	Mon Jan 06 09:44:07 2014 +0100
    15.3 @@ -0,0 +1,51 @@
    15.4 +<!--
    15.5 +
    15.6 +    DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
    15.7 +
    15.8 +    Copyright 2013-2013 Oracle and/or its affiliates. All rights reserved.
    15.9 +
   15.10 +    Oracle and Java are registered trademarks of Oracle and/or its affiliates.
   15.11 +    Other names may be trademarks of their respective owners.
   15.12 +
   15.13 +    The contents of this file are subject to the terms of either the GNU
   15.14 +    General Public License Version 2 only ("GPL") or the Common
   15.15 +    Development and Distribution License("CDDL") (collectively, the
   15.16 +    "License"). You may not use this file except in compliance with the
   15.17 +    License. You can obtain a copy of the License at
   15.18 +    http://www.netbeans.org/cddl-gplv2.html
   15.19 +    or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
   15.20 +    specific language governing permissions and limitations under the
   15.21 +    License.  When distributing the software, include this License Header
   15.22 +    Notice in each file and include the License file at
   15.23 +    nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
   15.24 +    particular file as subject to the "Classpath" exception as provided
   15.25 +    by Oracle in the GPL Version 2 section of the License file that
   15.26 +    accompanied this code. If applicable, add the following below the
   15.27 +    License Header, with the fields enclosed by brackets [] replaced by
   15.28 +    your own identifying information:
   15.29 +    "Portions Copyrighted [year] [name of copyright owner]"
   15.30 +
   15.31 +    Contributor(s):
   15.32 +
   15.33 +    The Original Software is NetBeans. The Initial Developer of the Original
   15.34 +    Software is Oracle. Portions Copyright 2013-2013 Oracle. All Rights Reserved.
   15.35 +
   15.36 +    If you wish your version of this file to be governed by only the CDDL
   15.37 +    or only the GPL Version 2, indicate your decision by adding
   15.38 +    "[Contributor] elects to include this software in this distribution
   15.39 +    under the [CDDL or GPL Version 2] license." If you do not indicate a
   15.40 +    single choice of license, a recipient has the option to distribute
   15.41 +    your version of this file under either the CDDL, the GPL Version 2 or
   15.42 +    to extend the choice of license to its licensees as provided above.
   15.43 +    However, if you add GPL Version 2 code and therefore, elected the GPL
   15.44 +    Version 2 license, then the option applies only if the new code is
   15.45 +    made subject to such option by the copyright holder.
   15.46 +
   15.47 +-->
   15.48 +<html>
   15.49 +    <body>
   15.50 +        <div>Service Provider Interfaces for those who wish to integrate own
   15.51 +            <a href="Technology.html">technology</a> with the HTML for Java API.
   15.52 +        </div>
   15.53 +    </body>
   15.54 +</html>
    16.1 --- a/json/src/main/java/org/netbeans/html/json/impl/Bindings.java	Mon Dec 16 18:08:40 2013 +0100
    16.2 +++ b/json/src/main/java/org/netbeans/html/json/impl/Bindings.java	Mon Jan 06 09:44:07 2014 +0100
    16.3 @@ -42,11 +42,10 @@
    16.4   */
    16.5  package org.netbeans.html.json.impl;
    16.6  
    16.7 +import net.java.html.BrwsrCtx;
    16.8 +import org.apidesign.html.json.spi.FunctionBinding;
    16.9  import org.apidesign.html.json.spi.PropertyBinding;
   16.10 -import net.java.html.BrwsrCtx;
   16.11 -import org.netbeans.html.json.impl.PropertyBindingAccessor.FBData;
   16.12 -import org.netbeans.html.json.impl.PropertyBindingAccessor.PBData;
   16.13 -import org.apidesign.html.json.spi.FunctionBinding;
   16.14 +import org.apidesign.html.json.spi.Proto;
   16.15  import org.apidesign.html.json.spi.Technology;
   16.16  
   16.17  /**
   16.18 @@ -61,14 +60,10 @@
   16.19          this.bp = bp;
   16.20      }
   16.21      
   16.22 -    public <M> PropertyBinding registerProperty(String propName, M model, SetAndGet<M> access, boolean readOnly) {
   16.23 -        return PropertyBindingAccessor.create(new PBData<M>(this, propName, model, access, readOnly));
   16.24 +    public <M> PropertyBinding registerProperty(String propName, int index, M model, Proto.Type<M> access, boolean readOnly) {
   16.25 +        return PropertyBindingAccessor.create(access, this, propName, index, model, readOnly);
   16.26      }
   16.27  
   16.28 -    public <M> FunctionBinding registerFunction(String name, M model, Callback<M> access) {
   16.29 -        return PropertyBindingAccessor.createFunction(new FBData<M>(name, model, access));
   16.30 -    }
   16.31 -    
   16.32      public static Bindings<?> apply(BrwsrCtx c, Object model) {
   16.33          Technology<?> bp = JSON.findTechnology(c);
   16.34          return apply(bp);
    17.1 --- a/json/src/main/java/org/netbeans/html/json/impl/Callback.java	Mon Dec 16 18:08:40 2013 +0100
    17.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
    17.3 @@ -1,51 +0,0 @@
    17.4 -/**
    17.5 - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
    17.6 - *
    17.7 - * Copyright 2013-2013 Oracle and/or its affiliates. All rights reserved.
    17.8 - *
    17.9 - * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
   17.10 - * Other names may be trademarks of their respective owners.
   17.11 - *
   17.12 - * The contents of this file are subject to the terms of either the GNU
   17.13 - * General Public License Version 2 only ("GPL") or the Common
   17.14 - * Development and Distribution License("CDDL") (collectively, the
   17.15 - * "License"). You may not use this file except in compliance with the
   17.16 - * License. You can obtain a copy of the License at
   17.17 - * http://www.netbeans.org/cddl-gplv2.html
   17.18 - * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
   17.19 - * specific language governing permissions and limitations under the
   17.20 - * License.  When distributing the software, include this License Header
   17.21 - * Notice in each file and include the License file at
   17.22 - * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
   17.23 - * particular file as subject to the "Classpath" exception as provided
   17.24 - * by Oracle in the GPL Version 2 section of the License file that
   17.25 - * accompanied this code. If applicable, add the following below the
   17.26 - * License Header, with the fields enclosed by brackets [] replaced by
   17.27 - * your own identifying information:
   17.28 - * "Portions Copyrighted [year] [name of copyright owner]"
   17.29 - *
   17.30 - * Contributor(s):
   17.31 - *
   17.32 - * The Original Software is NetBeans. The Initial Developer of the Original
   17.33 - * Software is Oracle. Portions Copyright 2013-2013 Oracle. All Rights Reserved.
   17.34 - *
   17.35 - * If you wish your version of this file to be governed by only the CDDL
   17.36 - * or only the GPL Version 2, indicate your decision by adding
   17.37 - * "[Contributor] elects to include this software in this distribution
   17.38 - * under the [CDDL or GPL Version 2] license." If you do not indicate a
   17.39 - * single choice of license, a recipient has the option to distribute
   17.40 - * your version of this file under either the CDDL, the GPL Version 2 or
   17.41 - * to extend the choice of license to its licensees as provided above.
   17.42 - * However, if you add GPL Version 2 code and therefore, elected the GPL
   17.43 - * Version 2 license, then the option applies only if the new code is
   17.44 - * made subject to such option by the copyright holder.
   17.45 - */
   17.46 -package org.netbeans.html.json.impl;
   17.47 -
   17.48 -/**
   17.49 - *
   17.50 - * @author Jaroslav Tulach <jtulach@netbeans.org>
   17.51 - */
   17.52 -public interface Callback<Data> {
   17.53 -    public void call(Data model, Object data, Object ev);
   17.54 -}
    18.1 --- a/json/src/main/java/org/netbeans/html/json/impl/FromJSON.java	Mon Dec 16 18:08:40 2013 +0100
    18.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
    18.3 @@ -1,55 +0,0 @@
    18.4 -/**
    18.5 - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
    18.6 - *
    18.7 - * Copyright 2013-2013 Oracle and/or its affiliates. All rights reserved.
    18.8 - *
    18.9 - * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
   18.10 - * Other names may be trademarks of their respective owners.
   18.11 - *
   18.12 - * The contents of this file are subject to the terms of either the GNU
   18.13 - * General Public License Version 2 only ("GPL") or the Common
   18.14 - * Development and Distribution License("CDDL") (collectively, the
   18.15 - * "License"). You may not use this file except in compliance with the
   18.16 - * License. You can obtain a copy of the License at
   18.17 - * http://www.netbeans.org/cddl-gplv2.html
   18.18 - * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
   18.19 - * specific language governing permissions and limitations under the
   18.20 - * License.  When distributing the software, include this License Header
   18.21 - * Notice in each file and include the License file at
   18.22 - * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
   18.23 - * particular file as subject to the "Classpath" exception as provided
   18.24 - * by Oracle in the GPL Version 2 section of the License file that
   18.25 - * accompanied this code. If applicable, add the following below the
   18.26 - * License Header, with the fields enclosed by brackets [] replaced by
   18.27 - * your own identifying information:
   18.28 - * "Portions Copyrighted [year] [name of copyright owner]"
   18.29 - *
   18.30 - * Contributor(s):
   18.31 - *
   18.32 - * The Original Software is NetBeans. The Initial Developer of the Original
   18.33 - * Software is Oracle. Portions Copyright 2013-2013 Oracle. All Rights Reserved.
   18.34 - *
   18.35 - * If you wish your version of this file to be governed by only the CDDL
   18.36 - * or only the GPL Version 2, indicate your decision by adding
   18.37 - * "[Contributor] elects to include this software in this distribution
   18.38 - * under the [CDDL or GPL Version 2] license." If you do not indicate a
   18.39 - * single choice of license, a recipient has the option to distribute
   18.40 - * your version of this file under either the CDDL, the GPL Version 2 or
   18.41 - * to extend the choice of license to its licensees as provided above.
   18.42 - * However, if you add GPL Version 2 code and therefore, elected the GPL
   18.43 - * Version 2 license, then the option applies only if the new code is
   18.44 - * made subject to such option by the copyright holder.
   18.45 - */
   18.46 -package org.netbeans.html.json.impl;
   18.47 -
   18.48 -import net.java.html.BrwsrCtx;
   18.49 -
   18.50 -/**
   18.51 - *
   18.52 - * @author Jaroslav Tulach <jtulach@netbeans.org>
   18.53 - */
   18.54 -public interface FromJSON<Data> {
   18.55 -    public Class<Data> factoryFor();
   18.56 -    public Data read(BrwsrCtx c, Object d);
   18.57 -    public Data cloneTo(Object d, BrwsrCtx c);
   18.58 -}
    19.1 --- a/json/src/main/java/org/netbeans/html/json/impl/JSON.java	Mon Dec 16 18:08:40 2013 +0100
    19.2 +++ b/json/src/main/java/org/netbeans/html/json/impl/JSON.java	Mon Jan 06 09:44:07 2014 +0100
    19.3 @@ -44,6 +44,7 @@
    19.4  
    19.5  import java.io.IOException;
    19.6  import java.io.InputStream;
    19.7 +import java.util.Collection;
    19.8  import java.util.HashMap;
    19.9  import java.util.Map;
   19.10  import net.java.html.BrwsrCtx;
   19.11 @@ -51,6 +52,7 @@
   19.12  import org.apidesign.html.json.spi.FunctionBinding;
   19.13  import org.apidesign.html.json.spi.JSONCall;
   19.14  import org.apidesign.html.json.spi.PropertyBinding;
   19.15 +import org.apidesign.html.json.spi.Proto;
   19.16  import org.apidesign.html.json.spi.Technology;
   19.17  import org.apidesign.html.json.spi.Transfer;
   19.18  import org.apidesign.html.json.spi.WSTransfer;
   19.19 @@ -96,7 +98,7 @@
   19.20          return val[0];
   19.21      }
   19.22  
   19.23 -    public static Object toJSON(Object value) {
   19.24 +    public static String toJSON(Object value) {
   19.25          if (value == null) {
   19.26              return "null";
   19.27          }
   19.28 @@ -198,7 +200,7 @@
   19.29          return type.cast(val);
   19.30      }
   19.31      
   19.32 -    protected static boolean isNumeric(Object val) {
   19.33 +    static boolean isNumeric(Object val) {
   19.34          return ((val instanceof Integer) || (val instanceof Long) || (val instanceof Short) || (val instanceof Byte));
   19.35      }
   19.36      
   19.37 @@ -217,7 +219,7 @@
   19.38          }
   19.39          return (String)val;
   19.40      }
   19.41 -
   19.42 +    
   19.43      public static Number numberValue(Object val) {
   19.44          if (val instanceof String) {
   19.45              try {
   19.46 @@ -257,6 +259,32 @@
   19.47          return Boolean.TRUE.equals(val);
   19.48      }
   19.49      
   19.50 +    public static Object find(Object object, Bindings model) {
   19.51 +        if (object == null) {
   19.52 +            return null;
   19.53 +        }
   19.54 +        if (object instanceof JSONList) {
   19.55 +            return ((JSONList<?>) object).koData();
   19.56 +        }
   19.57 +        if (object instanceof Collection) {
   19.58 +            return JSONList.koData((Collection<?>) object, model);
   19.59 +        }
   19.60 +        Proto.Type<?> type = JSON.findType(object.getClass());
   19.61 +        if (type == null) {
   19.62 +            return null;
   19.63 +        }
   19.64 +        final Proto proto = PropertyBindingAccessor.protoFor(type, object);
   19.65 +        if (proto == null) {
   19.66 +            return null;
   19.67 +        }
   19.68 +        final Bindings b = PropertyBindingAccessor.getBindings(proto, true);
   19.69 +        return b == null ? null : b.koData();
   19.70 +    }
   19.71 +
   19.72 +    public static Object find(Object object) {
   19.73 +        return find(object, null);
   19.74 +    }
   19.75 +    
   19.76      public static void loadJSON(
   19.77          BrwsrCtx c, RcvrJSON callback,
   19.78          String urlBefore, String urlAfter, String method,
   19.79 @@ -326,22 +354,21 @@
   19.80          
   19.81      }
   19.82      
   19.83 -    private static final Map<Class,FromJSON<?>> froms;
   19.84 +    private static final Map<Class,Proto.Type<?>> modelTypes;
   19.85      static {
   19.86 -        Map<Class,FromJSON<?>> m = new HashMap<Class,FromJSON<?>>();
   19.87 -        froms = m;
   19.88 +        modelTypes = new HashMap<Class, Proto.Type<?>>();
   19.89      }
   19.90 -    public static void register(FromJSON<?> from) {
   19.91 -        froms.put(from.factoryFor(), from);
   19.92 +    public static void register(Class c, Proto.Type<?> type) {
   19.93 +        modelTypes.put(c, type);
   19.94      }
   19.95      
   19.96      public static boolean isModel(Class<?> clazz) {
   19.97 -        return findFrom(clazz) != null; 
   19.98 +        return findType(clazz) != null; 
   19.99      }
  19.100      
  19.101 -    private static FromJSON<?> findFrom(Class<?> clazz) {
  19.102 +    static Proto.Type<?> findType(Class<?> clazz) {
  19.103          for (int i = 0; i < 2; i++) {
  19.104 -            FromJSON<?> from = froms.get(clazz);
  19.105 +            Proto.Type<?> from = modelTypes.get(clazz);
  19.106              if (from == null) {
  19.107                  initClass(clazz);
  19.108              } else {
  19.109 @@ -352,11 +379,11 @@
  19.110      }
  19.111      
  19.112      public static <Model> Model bindTo(Model model, BrwsrCtx c) {
  19.113 -        FromJSON<?> from = findFrom(model.getClass());
  19.114 +        Proto.Type<Model> from = (Proto.Type<Model>) findType(model.getClass());
  19.115          if (from == null) {
  19.116              throw new IllegalArgumentException();
  19.117          }
  19.118 -        return (Model) from.cloneTo(model, c);
  19.119 +        return PropertyBindingAccessor.clone(from, model, c);
  19.120      }
  19.121      
  19.122      public static <T> T readStream(BrwsrCtx c, Class<T> modelClazz, InputStream data) 
  19.123 @@ -372,11 +399,11 @@
  19.124              return modelClazz.cast(data.toString());
  19.125          }
  19.126          for (int i = 0; i < 2; i++) {
  19.127 -            FromJSON<?> from = froms.get(modelClazz);
  19.128 +            Proto.Type<?> from = modelTypes.get(modelClazz);
  19.129              if (from == null) {
  19.130                  initClass(modelClazz);
  19.131              } else {
  19.132 -                return modelClazz.cast(from.read(c, data));
  19.133 +                return modelClazz.cast(PropertyBindingAccessor.readFrom(from, c, data));
  19.134              }
  19.135          }
  19.136          throw new NullPointerException();
    20.1 --- a/json/src/main/java/org/netbeans/html/json/impl/JSONList.java	Mon Dec 16 18:08:40 2013 +0100
    20.2 +++ b/json/src/main/java/org/netbeans/html/json/impl/JSONList.java	Mon Jan 06 09:44:07 2014 +0100
    20.3 @@ -44,37 +44,25 @@
    20.4  
    20.5  import java.lang.reflect.Array;
    20.6  import java.util.ArrayList;
    20.7 -import java.util.Arrays;
    20.8  import java.util.Collection;
    20.9  import java.util.Iterator;
   20.10 -import java.util.List;
   20.11 -import net.java.html.BrwsrCtx;
   20.12 +import org.apidesign.html.json.spi.Proto;
   20.13  
   20.14  /**
   20.15   *
   20.16   * @author Jaroslav Tulach <jtulach@netbeans.org>
   20.17   */
   20.18  public final class JSONList<T> extends ArrayList<T> {
   20.19 +    private final Proto proto;
   20.20      private final String name;
   20.21      private final String[] deps;
   20.22 -    private Bindings[] model;
   20.23 -    private Runnable onchange;
   20.24 +    private final int index;
   20.25  
   20.26 -    public JSONList(Bindings[] model, String name, String... deps) {
   20.27 -        assert model.length == 1;
   20.28 -        this.model = model;
   20.29 +    public JSONList(Proto proto, String name, int changeIndex, String... deps) {
   20.30 +        this.proto = proto;
   20.31          this.name = name;
   20.32          this.deps = deps;
   20.33 -    }
   20.34 -    
   20.35 -    public void init(T... values) {
   20.36 -        if (values == null || values.length == 0) {
   20.37 -            return;
   20.38 -        }
   20.39 -        if (this.model[0] != null || !isEmpty()) {
   20.40 -            throw new IllegalStateException();
   20.41 -        }
   20.42 -        super.addAll(Arrays.asList(values));
   20.43 +        this.index = changeIndex;
   20.44      }
   20.45      
   20.46      public void init(Object values) {
   20.47 @@ -82,23 +70,22 @@
   20.48          if (values == null || (len = Array.getLength(values)) == 0) {
   20.49              return;
   20.50          }
   20.51 -        if (this.model[0] != null || !isEmpty()) {
   20.52 -            throw new IllegalStateException();
   20.53 -        }
   20.54          for (int i = 0; i < len; i++) {
   20.55              Object data = Array.get(values, i);
   20.56              super.add((T)data);
   20.57          }
   20.58      }
   20.59 +    public static <T> void init(Collection<T> to, Object values) {
   20.60 +        int len;
   20.61 +        if (values == null || (len = Array.getLength(values)) == 0) {
   20.62 +            return;
   20.63 +        }
   20.64 +        for (int i = 0; i < len; i++) {
   20.65 +            Object data = Array.get(values, i);
   20.66 +            to.add((T)data);
   20.67 +        }
   20.68 +    }
   20.69      
   20.70 -    public JSONList<T> onChange(Runnable r) {
   20.71 -        if (this.onchange != null) {
   20.72 -            throw new IllegalStateException();
   20.73 -        }
   20.74 -        this.onchange = r;
   20.75 -        return this;
   20.76 -    }
   20.77 -
   20.78      @Override
   20.79      public boolean add(T e) {
   20.80          boolean ret = super.add(e);
   20.81 @@ -187,29 +174,14 @@
   20.82      }
   20.83  
   20.84      private void notifyChange() {
   20.85 -        Bindings m = model[0];
   20.86 +        Bindings m = PropertyBindingAccessor.getBindings(proto, false);
   20.87          if (m != null) {
   20.88              m.valueHasMutated(name);
   20.89              for (String dependant : deps) {
   20.90                  m.valueHasMutated(dependant);
   20.91              }
   20.92 -            Runnable r = onchange;
   20.93 -            if (r != null) {
   20.94 -                r.run();
   20.95 -            }
   20.96 -        }
   20.97 -    }
   20.98 -    
   20.99 -    public void cloneAll(BrwsrCtx c, Collection<T> other) {
  20.100 -        Boolean isModel = null;
  20.101 -        for (T t : other) {
  20.102 -            if (isModel == null) {
  20.103 -                isModel = JSON.isModel(t.getClass());
  20.104 -            }
  20.105 -            if (isModel) {
  20.106 -                add(JSON.bindTo(t, c));
  20.107 -            } else {
  20.108 -                add(t);
  20.109 +            if (index >= 0) {
  20.110 +                PropertyBindingAccessor.notifyProtoChange(proto, index);
  20.111              }
  20.112          }
  20.113      }
  20.114 @@ -222,7 +194,7 @@
  20.115      static final Object koData(Collection<?> c, Bindings m) {
  20.116          Object[] arr = c.toArray(new Object[c.size()]);
  20.117          for (int i = 0; i < arr.length; i++) {
  20.118 -            Object r = WrapperObject.find(arr[i], m);
  20.119 +            Object r = JSON.find(arr[i], m);
  20.120              if (r != null) {
  20.121                  arr[i] = r;
  20.122              }
  20.123 @@ -231,6 +203,6 @@
  20.124      }
  20.125  
  20.126      final Object koData() {
  20.127 -        return koData(this, model[0]);
  20.128 +        return koData(this, PropertyBindingAccessor.getBindings(proto, true));
  20.129      }
  20.130  }
    21.1 --- a/json/src/main/java/org/netbeans/html/json/impl/ModelProcessor.java	Mon Dec 16 18:08:40 2013 +0100
    21.2 +++ b/json/src/main/java/org/netbeans/html/json/impl/ModelProcessor.java	Mon Jan 06 09:44:07 2014 +0100
    21.3 @@ -183,6 +183,7 @@
    21.4          models.put(e, className);
    21.5          try {
    21.6              StringWriter body = new StringWriter();
    21.7 +            StringBuilder onReceiveType = new StringBuilder();
    21.8              List<String> propsGetSet = new ArrayList<String>();
    21.9              List<String> functions = new ArrayList<String>();
   21.10              Map<String, Collection<String>> propsDeps = new HashMap<String, Collection<String>>();
   21.11 @@ -195,13 +196,13 @@
   21.12              if (!generateOnChange(e, propsDeps, props, className, functionDeps)) {
   21.13                  ok = false;
   21.14              }
   21.15 -            if (!generateProperties(e, body, props, propsGetSet, propsDeps, functionDeps)) {
   21.16 +            if (!generateProperties(e, body, className, props, propsGetSet, propsDeps, functionDeps)) {
   21.17                  ok = false;
   21.18              }
   21.19              if (!generateFunctions(e, body, className, e.getEnclosedElements(), functions)) {
   21.20                  ok = false;
   21.21              }
   21.22 -            if (!generateReceive(e, body, className, e.getEnclosedElements(), functions)) {
   21.23 +            if (!generateReceive(e, body, className, e.getEnclosedElements(), onReceiveType)) {
   21.24                  ok = false;
   21.25              }
   21.26              if (!generateOperation(e, body, className, e.getEnclosedElements())) {
   21.27 @@ -213,26 +214,49 @@
   21.28                  w.append("package " + pkg + ";\n");
   21.29                  w.append("import net.java.html.json.*;\n");
   21.30                  w.append("public final class ").append(className).append(" implements Cloneable {\n");
   21.31 -                w.append("  private boolean locked;\n");
   21.32 -                w.append("  private net.java.html.BrwsrCtx context;\n");
   21.33 -                w.append("  private org.netbeans.html.json.impl.Bindings[] ko = { null };\n");
   21.34 +                w.append("  private static final Html4JavaType TYPE = new Html4JavaType();\n");
   21.35 +                w.append("  private final org.apidesign.html.json.spi.Proto proto;\n");
   21.36                  w.append(body.toString());
   21.37                  w.append("  private static Class<" + inPckName(e) + "> modelFor() { return null; }\n");
   21.38                  w.append("  private ").append(className).append("(net.java.html.BrwsrCtx context) {\n");
   21.39 -                w.append("    this.context = context;\n");
   21.40 +                w.append("    this.proto = TYPE.createProto(this, context);\n");
   21.41 +                for (Prprt p : props) {
   21.42 +                    if (p.array()) {
   21.43 +                        final String tn = typeName(e, p);
   21.44 +                        String[] gs = toGetSet(p.name(), tn, p.array());
   21.45 +                        w.write("    this.prop_" + p.name() + " = proto.createList(\""
   21.46 +                            + p.name() + "\"");
   21.47 +                        if (functionDeps.containsKey(p.name())) {
   21.48 +                            int index = Arrays.asList(functionDeps.keySet().toArray()).indexOf(p.name());
   21.49 +                            w.write(", " + index);
   21.50 +                        } else {
   21.51 +                            w.write(", -1");
   21.52 +                        }
   21.53 +                        Collection<String> dependants = propsDeps.get(p.name());
   21.54 +                        if (dependants != null) {
   21.55 +                            for (String depProp : dependants) {
   21.56 +                                w.write(", ");
   21.57 +                                w.write('\"');
   21.58 +                                w.write(depProp);
   21.59 +                                w.write('\"');
   21.60 +                            }
   21.61 +                        }
   21.62 +                        w.write(")");
   21.63 +                        w.write(";\n");
   21.64 +                    }
   21.65 +                }
   21.66                  w.append("  };\n");
   21.67                  w.append("  public ").append(className).append("() {\n");
   21.68                  w.append("    this(net.java.html.BrwsrCtx.findDefault(").append(className).append(".class));\n");
   21.69                  for (Prprt p : props) {
   21.70 -                    if (p.array()) {
   21.71 -                        continue;
   21.72 -                    }
   21.73 -                    boolean[] isModel = {false};
   21.74 -                    boolean[] isEnum = {false};
   21.75 -                    boolean isPrimitive[] = {false};
   21.76 -                    String tn = checkType(p, isModel, isEnum, isPrimitive);
   21.77 -                    if (isModel[0]) {
   21.78 -                        w.write("    prop_" + p.name() + " = new " + tn + "();\n");
   21.79 +                    if (!p.array()) {
   21.80 +                        boolean[] isModel = {false};
   21.81 +                        boolean[] isEnum = {false};
   21.82 +                        boolean isPrimitive[] = {false};
   21.83 +                        String tn = checkType(p, isModel, isEnum, isPrimitive);
   21.84 +                        if (isModel[0]) {
   21.85 +                            w.write("    prop_" + p.name() + " = new " + tn + "();\n");
   21.86 +                        }
   21.87                      }
   21.88                  }
   21.89                  w.append("  };\n");
   21.90 @@ -272,38 +296,28 @@
   21.91                          w.write("    this.prop_" + p.name() + " = " + p.name() + ";\n");
   21.92                      }
   21.93                      if (firstArray != null) {
   21.94 -                        w.write("    this.prop_" + firstArray.name() + ".init(" + firstArray.name() + ");\n");
   21.95 +                        w.write("    proto.initTo(this.prop_" + firstArray.name() + ", " + firstArray.name() + ");\n");
   21.96                      }
   21.97                      w.append("  };\n");
   21.98                  }
   21.99 -                w.append("  private org.netbeans.html.json.impl.Bindings intKnckt() {\n");
  21.100 -                w.append("    if (ko[0] != null) return ko[0];\n");
  21.101 -                w.append("    ko[0] = org.netbeans.html.json.impl.Bindings.apply(context, this);\n");
  21.102 +                w.append("  private static class Html4JavaType extends org.apidesign.html.json.spi.Proto.Type<").append(className).append("> {\n");
  21.103 +                w.append("    private Html4JavaType() {\n      super(").append(className).append(".class, ").
  21.104 +                    append(inPckName(e)).append(".class, " + (propsGetSet.size() / 5) + ", "
  21.105 +                    + (functions.size() / 2) + ");\n");
  21.106                  {
  21.107 -                    w.append("    org.apidesign.html.json.spi.PropertyBinding[] propArr = {\n");
  21.108                      for (int i = 0; i < propsGetSet.size(); i += 5) {
  21.109 -                        w.append("      ko[0].registerProperty(\"").append(propsGetSet.get(i)).append("\", this, new P(");
  21.110 -                        w.append((i / 5) + "), " + (propsGetSet.get(i + 2) == null) + "),\n");
  21.111 +                        w.append("      registerProperty(\"").append(propsGetSet.get(i)).append("\", ");
  21.112 +                        w.append((i / 5) + ", " + (propsGetSet.get(i + 2) == null) + ");\n");
  21.113                      }
  21.114 -                    w.append("    };\n");
  21.115                  }
  21.116                  {
  21.117 -                    w.append("    org.apidesign.html.json.spi.FunctionBinding[] funcArr = {\n");
  21.118                      for (int i = 0; i < functions.size(); i += 2) {
  21.119 -                        w.append("      ko[0].registerFunction(\"").append(functions.get(i)).append("\", this, new P(");
  21.120 -                        w.append((i / 2) + ")),\n");
  21.121 +                        w.append("      registerFunction(\"").append(functions.get(i)).append("\", ");
  21.122 +                        w.append((i / 2) + ");\n");
  21.123                      }
  21.124 -                    w.append("    };\n");
  21.125                  }
  21.126 -                w.append("    ko[0].finish(this, propArr, funcArr);\n");
  21.127 -                w.append("    return ko[0];\n");
  21.128 -                w.append("  };\n");
  21.129 -                w.append("  private static final class P implements org.netbeans.html.json.impl.SetAndGet<" + className + ">,\n");
  21.130 -                w.append("  org.netbeans.html.json.impl.Callback<" + className + ">,\n");
  21.131 -                w.append("  org.netbeans.html.json.impl.FromJSON<" + className + "> {\n");
  21.132 -                w.append("    private final int type;\n");
  21.133 -                w.append("    P(int t) { type = t; };\n");
  21.134 -                w.append("    public void setValue(" + className + " data, Object value) {\n");
  21.135 +                w.append("    }\n");
  21.136 +                w.append("    @Override public void setValue(" + className + " data, int type, Object value) {\n");
  21.137                  w.append("      switch (type) {\n");
  21.138                  for (int i = 0; i < propsGetSet.size(); i += 5) {
  21.139                      final String set = propsGetSet.get(i + 2);
  21.140 @@ -313,12 +327,12 @@
  21.141                          tn = btn;
  21.142                      }
  21.143                      if (set != null) {
  21.144 -                        w.append("        case " + (i / 5) + ": data." + strip(set) + "(org.netbeans.html.json.impl.JSON.extractValue(" + tn + ".class, value)); return;\n");
  21.145 +                        w.append("        case " + (i / 5) + ": data." + strip(set) + "(TYPE.extractValue(" + tn + ".class, value)); return;\n");
  21.146                      }
  21.147                  }
  21.148                  w.append("      }\n");
  21.149                  w.append("    }\n");
  21.150 -                w.append("    public Object getValue(" + className + " data) {\n");
  21.151 +                w.append("    @Override public Object getValue(" + className + " data, int type) {\n");
  21.152                  w.append("      switch (type) {\n");
  21.153                  for (int i = 0; i < propsGetSet.size(); i += 5) {
  21.154                      final String get = propsGetSet.get(i + 1);
  21.155 @@ -329,7 +343,7 @@
  21.156                  w.append("      }\n");
  21.157                  w.append("      throw new UnsupportedOperationException();\n");
  21.158                  w.append("    }\n");
  21.159 -                w.append("    public void call(" + className + " model, Object data, Object ev) {\n");
  21.160 +                w.append("    @Override public void call(" + className + " model, int type, Object data, Object ev) {\n");
  21.161                  w.append("      switch (type) {\n");
  21.162                  for (int i = 0; i < functions.size(); i += 2) {
  21.163                      final String name = functions.get(i);
  21.164 @@ -338,13 +352,33 @@
  21.165                  w.append("      }\n");
  21.166                  w.append("      throw new UnsupportedOperationException();\n");
  21.167                  w.append("    }\n");
  21.168 -                w.append("    public Class<" + className + "> factoryFor() { return " + className + ".class; }\n");
  21.169 -                w.append("    public " + className + " read(net.java.html.BrwsrCtx c, Object json) { return new " + className + "(c, json); }\n");
  21.170 -                w.append("    public " + className + " cloneTo(Object o, net.java.html.BrwsrCtx c) { return ((" + className + ")o).clone(c); }\n");
  21.171 +                w.append("    @Override public org.apidesign.html.json.spi.Proto protoFor(Object obj) {\n");
  21.172 +                w.append("      return ((" + className + ")obj).proto;");
  21.173 +                w.append("    }\n");
  21.174 +                w.append("    @Override public void onChange(" + className + " model, int type) {\n");
  21.175 +                w.append("      switch (type) {\n");
  21.176 +                {
  21.177 +                    String[] arr = functionDeps.keySet().toArray(new String[0]);
  21.178 +                    for (int i = 0; i < arr.length; i++) {
  21.179 +                        Collection<String> onChange = functionDeps.get(arr[i]);
  21.180 +                        if (onChange != null) {
  21.181 +                            w.append("      case " + i + ":\n");
  21.182 +                            for (String call : onChange) {
  21.183 +                                w.append("      ").append(call).append("\n");
  21.184 +                            }
  21.185 +                            w.write("      return;\n");
  21.186 +                        }
  21.187 +                    }
  21.188 +                }
  21.189 +                w.append("    }\n");
  21.190 +                w.append("      throw new UnsupportedOperationException();\n");
  21.191 +                w.append("    }\n");
  21.192 +                w.append(onReceiveType);
  21.193 +                w.append("    @Override public " + className + " read(net.java.html.BrwsrCtx c, Object json) { return new " + className + "(c, json); }\n");
  21.194 +                w.append("    @Override public " + className + " cloneTo(" + className + " o, net.java.html.BrwsrCtx c) { return o.clone(c); }\n");
  21.195                  w.append("  }\n");
  21.196 -                w.append("  static { org.netbeans.html.json.impl.JSON.register(new P(0)); }\n");
  21.197                  w.append("  private ").append(className).append("(net.java.html.BrwsrCtx c, Object json) {\n");
  21.198 -                w.append("    this.context = c;\n");
  21.199 +                w.append("    this(c);\n");
  21.200                  int values = 0;
  21.201                  for (int i = 0; i < propsGetSet.size(); i += 5) {
  21.202                      Prprt p = findPrprt(props, propsGetSet.get(i));
  21.203 @@ -354,7 +388,7 @@
  21.204                      values++;
  21.205                  }
  21.206                  w.append("    Object[] ret = new Object[" + values + "];\n");
  21.207 -                w.append("    org.netbeans.html.json.impl.JSON.extract(context, json, new String[] {\n");
  21.208 +                w.append("    proto.extract(json, new String[] {\n");
  21.209                  for (int i = 0; i < propsGetSet.size(); i += 5) {
  21.210                      Prprt p = findPrprt(props, propsGetSet.get(i));
  21.211                      if (p == null) {
  21.212 @@ -377,15 +411,15 @@
  21.213                          w.append("    if (ret[" + cnt + "] instanceof Object[]) {\n");
  21.214                          w.append("      for (Object e : ((Object[])ret[" + cnt + "])) {\n");
  21.215                          if (isModel[0]) {
  21.216 -                            w.append("        this.prop_").append(pn).append(".add(org.netbeans.html.json.impl.JSON.read");
  21.217 -                            w.append("(c, " + type + ".class, e));\n");
  21.218 +                            w.append("        this.prop_").append(pn).append(".add(proto.read");
  21.219 +                            w.append("(" + type + ".class, e));\n");
  21.220                          } else if (isEnum[0]) {
  21.221                              w.append("        this.prop_").append(pn);
  21.222                              w.append(".add(e == null ? null : ");
  21.223 -                            w.append(type).append(".valueOf(org.netbeans.html.json.impl.JSON.stringValue(e)));\n");
  21.224 +                            w.append(type).append(".valueOf(TYPE.stringValue(e)));\n");
  21.225                          } else {
  21.226                              if (isPrimitive(type)) {
  21.227 -                                w.append("        this.prop_").append(pn).append(".add(org.netbeans.html.json.impl.JSON.numberValue(e).");
  21.228 +                                w.append("        this.prop_").append(pn).append(".add(TYPE.numberValue(e).");
  21.229                                  w.append(type).append("Value());\n");
  21.230                              } else {
  21.231                                  w.append("        this.prop_").append(pn).append(".add((");
  21.232 @@ -399,7 +433,7 @@
  21.233                              w.append("    try {\n");
  21.234                              w.append("    this.prop_").append(pn);
  21.235                              w.append(" = ret[" + cnt + "] == null ? null : ");
  21.236 -                            w.append(type).append(".valueOf(org.netbeans.html.json.impl.JSON.stringValue(ret[" + cnt + "]));\n");
  21.237 +                            w.append(type).append(".valueOf(TYPE.stringValue(ret[" + cnt + "]));\n");
  21.238                              w.append("    } catch (IllegalArgumentException ex) {\n");
  21.239                              w.append("      ex.printStackTrace();\n");
  21.240                              w.append("    }\n");
  21.241 @@ -407,17 +441,17 @@
  21.242                              w.append("    this.prop_").append(pn);
  21.243                              w.append(" = ret[" + cnt + "] == null ? ");
  21.244                              if ("char".equals(type)) {
  21.245 -                                w.append("0 : (org.netbeans.html.json.impl.JSON.charValue(");
  21.246 +                                w.append("0 : (TYPE.charValue(");
  21.247                              } else if ("boolean".equals(type)) {
  21.248 -                                w.append("false : (org.netbeans.html.json.impl.JSON.boolValue(");
  21.249 +                                w.append("false : (TYPE.boolValue(");
  21.250                              } else {
  21.251 -                                w.append("0 : (org.netbeans.html.json.impl.JSON.numberValue(");
  21.252 +                                w.append("0 : (TYPE.numberValue(");
  21.253                              }
  21.254                              w.append("ret[" + cnt + "])).");
  21.255                              w.append(type).append("Value();\n");
  21.256                          } else if (isModel[0]) {
  21.257 -                            w.append("    this.prop_").append(pn).append(" = org.netbeans.html.json.impl.JSON.read");
  21.258 -                            w.append("(c, " + type + ".class, ");
  21.259 +                            w.append("    this.prop_").append(pn).append(" = proto.read");
  21.260 +                            w.append("(" + type + ".class, ");
  21.261                              w.append("ret[" + cnt + "]);\n");
  21.262                          }else {
  21.263                              w.append("    this.prop_").append(pn);
  21.264 @@ -427,7 +461,7 @@
  21.265                      }
  21.266                      cnt++;
  21.267                  }
  21.268 -                w.append("  };\n");
  21.269 +                w.append("  }\n");
  21.270                  writeToString(props, w);
  21.271                  writeClone(className, props, w);
  21.272                  w.write("  /** Activates this model instance in the current {@link \n"
  21.273 @@ -438,26 +472,22 @@
  21.274                      + "*/\n"
  21.275                  );
  21.276                  w.write("  public " + className + " applyBindings() {\n");
  21.277 -                w.write("    intKnckt().applyBindings();\n");
  21.278 +                w.write("    proto.applyBindings();\n");
  21.279                  w.write("    return this;\n");
  21.280                  w.write("  }\n");
  21.281                  w.write("  public boolean equals(Object o) {\n");
  21.282                  w.write("    if (o == this) return true;\n");
  21.283 -                w.write("    if (o instanceof org.netbeans.html.json.impl.WrapperObject) {\n");
  21.284 -                w.write("      ((org.netbeans.html.json.impl.WrapperObject)o).setRealObject(intKnckt().koData());\n");
  21.285 -                w.write("      return false;\n");
  21.286 -                w.write("    }\n");
  21.287                  w.write("    if (!(o instanceof " + className + ")) return false;\n");
  21.288                  w.write("    " + className + " p = (" + className + ")o;\n");
  21.289                  for (Prprt p : props) {
  21.290 -                    w.write("    if (!org.netbeans.html.json.impl.JSON.isSame(prop_" + p.name() + ", p.prop_" + p.name() + ")) return false;\n");
  21.291 +                    w.write("    if (!TYPE.isSame(prop_" + p.name() + ", p.prop_" + p.name() + ")) return false;\n");
  21.292                  }
  21.293                  w.write("    return true;\n");
  21.294                  w.write("  }\n");
  21.295                  w.write("  public int hashCode() {\n");
  21.296                  w.write("    int h = " + className + ".class.getName().hashCode();\n");
  21.297                  for (Prprt p : props) {
  21.298 -                    w.write("    h = org.netbeans.html.json.impl.JSON.hashPlus(prop_" + p.name() + ", h);\n");
  21.299 +                    w.write("    h = TYPE.hashPlus(prop_" + p.name() + ", h);\n");
  21.300                  }
  21.301                  w.write("    return h;\n");
  21.302                  w.write("  }\n");
  21.303 @@ -474,7 +504,7 @@
  21.304      
  21.305      private boolean generateProperties(
  21.306          Element where,
  21.307 -        Writer w, Prprt[] properties,
  21.308 +        Writer w, String className, Prprt[] properties,
  21.309          Collection<String> props, 
  21.310          Map<String,Collection<String>> deps,
  21.311          Map<String,Collection<String>> functionDeps
  21.312 @@ -487,57 +517,34 @@
  21.313              String castTo;
  21.314              
  21.315              if (p.array()) {
  21.316 -                w.write("  private org.netbeans.html.json.impl.JSONList<" + tn + "> prop_" + p.name() + " = new org.netbeans.html.json.impl.JSONList<" + tn + ">(ko, \""
  21.317 -                    + p.name() + "\"");
  21.318 -                Collection<String> dependants = deps.get(p.name());
  21.319 -                if (dependants != null) {
  21.320 -                    for (String depProp : dependants) {
  21.321 -                        w.write(", ");
  21.322 -                        w.write('\"');
  21.323 -                        w.write(depProp);
  21.324 -                        w.write('\"');
  21.325 -                    }
  21.326 -                }
  21.327 -                w.write(")");
  21.328 -                
  21.329 -                dependants = functionDeps.get(p.name());
  21.330 -                if (dependants != null) {
  21.331 -                    w.write(".onChange(new Runnable() { public void run() {\n");
  21.332 -                    for (String call : dependants) {
  21.333 -                        w.append("  ").append(call);
  21.334 -                    }
  21.335 -                    w.write("  }})");
  21.336 -                }
  21.337 -                w.write(";\n");
  21.338 +                w.write("  private final java.util.List<" + tn + "> prop_" + p.name() + ";\n");
  21.339              
  21.340                  castTo = "java.util.List";
  21.341                  w.write("  public java.util.List<" + tn + "> " + gs[0] + "() {\n");
  21.342 -                w.write("    if (locked) throw new IllegalStateException();\n");
  21.343 +                w.write("    proto.verifyUnlocked();\n");
  21.344                  w.write("    return prop_" + p.name() + ";\n");
  21.345                  w.write("  }\n");
  21.346              } else {
  21.347                  castTo = tn;
  21.348                  w.write("  private " + tn + " prop_" + p.name() + ";\n");
  21.349                  w.write("  public " + tn + " " + gs[0] + "() {\n");
  21.350 -                w.write("    if (locked) throw new IllegalStateException();\n");
  21.351 +                w.write("    proto.verifyUnlocked();\n");
  21.352                  w.write("    return prop_" + p.name() + ";\n");
  21.353                  w.write("  }\n");
  21.354                  w.write("  public void " + gs[1] + "(" + tn + " v) {\n");
  21.355 -                w.write("    if (locked) throw new IllegalStateException();\n");
  21.356 -                w.write("    if (org.netbeans.html.json.impl.JSON.isSame(prop_" + p.name() + ", v)) return;\n");
  21.357 +                w.write("    proto.verifyUnlocked();\n");
  21.358 +                w.write("    if (TYPE.isSame(prop_" + p.name() + ", v)) return;\n");
  21.359                  w.write("    prop_" + p.name() + " = v;\n");
  21.360 -                w.write("    org.netbeans.html.json.impl.Bindings b = ko[0];\n");
  21.361 -                w.write("    if (b != null) {\n");
  21.362 -                w.write("      b.valueHasMutated(\"" + p.name() + "\");\n");
  21.363 +                w.write("    proto.valueHasMutated(\"" + p.name() + "\");\n");
  21.364                  Collection<String> dependants = deps.get(p.name());
  21.365                  if (dependants != null) {
  21.366                      for (String depProp : dependants) {
  21.367 -                        w.write("      b.valueHasMutated(\"" + depProp + "\");\n");
  21.368 +                        w.write("    proto.valueHasMutated(\"" + depProp + "\");\n");
  21.369                      }
  21.370                  }
  21.371 -                w.write("    }\n");
  21.372                  dependants = functionDeps.get(p.name());
  21.373                  if (dependants != null) {
  21.374 +                    w.append(className).append(" model = ").append(className).append(".this;\n");
  21.375                      for (String call : dependants) {
  21.376                          w.append("  ").append(call);
  21.377                      }
  21.378 @@ -609,7 +616,6 @@
  21.379              String[] gs = toGetSet(sn, tn, array);
  21.380              
  21.381              w.write("  public " + tn + " " + gs[0] + "() {\n");
  21.382 -            w.write("    if (locked) throw new IllegalStateException();\n");
  21.383              int arg = 0;
  21.384              for (VariableElement pe : ee.getParameters()) {
  21.385                  final String dn = pe.getSimpleName().toString();
  21.386 @@ -631,7 +637,7 @@
  21.387                  depends.add(sn);
  21.388              }
  21.389              w.write("    try {\n");
  21.390 -            w.write("      locked = true;\n");
  21.391 +            w.write("      proto.acquireLock();\n");
  21.392              w.write("      return " + fqn(ee.getEnclosingElement().asType(), ee) + '.' + e.getSimpleName() + "(");
  21.393              String sep = "";
  21.394              for (int i = 1; i <= arg; i++) {
  21.395 @@ -641,7 +647,7 @@
  21.396              }
  21.397              w.write(");\n");
  21.398              w.write("    } finally {\n");
  21.399 -            w.write("      locked = false;\n");
  21.400 +            w.write("      proto.releaseLock();\n");
  21.401              w.write("    }\n");
  21.402              w.write("  }\n");
  21.403  
  21.404 @@ -908,7 +914,7 @@
  21.405                      }
  21.406                  }
  21.407                  body.append(") {\n");
  21.408 -                body.append("    org.netbeans.html.json.impl.JSON.runInBrowser(this.context, new Runnable() { public void run() {\n");
  21.409 +                body.append("    proto.runInBrowser(new Runnable() { public void run() {\n");
  21.410                  body.append("      ").append(clazz.getSimpleName()).append(".").append(m.getSimpleName()).append("(");
  21.411                  body.append(className).append(".this");
  21.412                  for (String s : args) {
  21.413 @@ -926,8 +932,11 @@
  21.414      
  21.415      private boolean generateReceive(
  21.416          Element clazz, StringWriter body, String className, 
  21.417 -        List<? extends Element> enclosedElements, List<String> functions
  21.418 +        List<? extends Element> enclosedElements, StringBuilder inType
  21.419      ) {
  21.420 +        inType.append("  @Override public void onMessage(").append(className).append(" model, int index, int type, Object data) {\n");
  21.421 +        inType.append("    switch (index) {\n");
  21.422 +        int index = 0;
  21.423          for (Element m : enclosedElements) {
  21.424              if (m.getKind() != ElementKind.METHOD) {
  21.425                  continue;
  21.426 @@ -975,7 +984,7 @@
  21.427                          simpleName = type.toString();
  21.428                      }
  21.429                      if (simpleName.toString().equals(className)) {
  21.430 -                        args.add(className + ".this");
  21.431 +                        args.add("model");
  21.432                      } else if (isModel(ve.asType())) {
  21.433                          modelType = ve.asType();
  21.434                      } else if (ve.asType().getKind() == TypeKind.ARRAY) {
  21.435 @@ -1038,47 +1047,49 @@
  21.436              body.append(") {\n");
  21.437              boolean webSocket = onR.method().equals("WebSocket");
  21.438              if (webSocket) {
  21.439 -                if (generateWSReceiveBody(body, onR, e, clazz, className, expectsList, modelClass, n, args, urlBefore, jsonpVarName, urlAfter, dataMirror)) {
  21.440 +                if (generateWSReceiveBody(index++, body, inType, onR, e, clazz, className, expectsList, modelClass, n, args, urlBefore, jsonpVarName, urlAfter, dataMirror)) {
  21.441                      return false;
  21.442                  }
  21.443                  body.append("  }\n");
  21.444 -                body.append("  private org.netbeans.html.json.impl.JSON.WS ws_" + e.getSimpleName() + ";\n");
  21.445 +                body.append("  private Object ws_" + e.getSimpleName() + ";\n");
  21.446              } else {
  21.447 -                if (generateJSONReceiveBody(body, onR, e, clazz, className, expectsList, modelClass, n, args, urlBefore, jsonpVarName, urlAfter, dataMirror)) {
  21.448 +                if (generateJSONReceiveBody(index++, body, inType, onR, e, clazz, className, expectsList, modelClass, n, args, urlBefore, jsonpVarName, urlAfter, dataMirror)) {
  21.449                      return false;
  21.450                  }
  21.451                  body.append("  }\n");
  21.452              }
  21.453          }
  21.454 +        inType.append("    }\n");
  21.455 +        inType.append("    throw new UnsupportedOperationException(\"index: \" + index + \" type: \" + type);\n");
  21.456 +        inType.append("  }\n");
  21.457          return true;
  21.458      }
  21.459  
  21.460 -    private boolean generateJSONReceiveBody(StringWriter body, OnReceive onR, ExecutableElement e, Element clazz, String className, boolean expectsList, String modelClass, String n, List<String> args, StringBuilder urlBefore, String jsonpVarName, StringBuilder urlAfter, String dataMirror) {
  21.461 +    private boolean generateJSONReceiveBody(int index, StringWriter method, StringBuilder body, OnReceive onR, ExecutableElement e, Element clazz, String className, boolean expectsList, String modelClass, String n, List<String> args, StringBuilder urlBefore, String jsonpVarName, StringBuilder urlAfter, String dataMirror) {
  21.462          body.append(
  21.463 -            "    class ProcessResult extends org.netbeans.html.json.impl.RcvrJSON {\n" +
  21.464 -            "      @Override\n" +
  21.465 -            "      public void onError(org.netbeans.html.json.impl.RcvrJSON.MsgEvnt ev) {\n" +
  21.466 -            "        Exception value = ev.getException();\n"
  21.467 +            "    case " + index + ": {\n" +
  21.468 +            "      if (type == 2) { /* on error */\n" +
  21.469 +            "        Exception ex = (Exception)data;\n"
  21.470              );
  21.471          if (onR.onError().isEmpty()) {
  21.472              body.append(
  21.473 -                "        value.printStackTrace();\n"
  21.474 +                "        ex.printStackTrace();\n"
  21.475                  );
  21.476          } else {
  21.477              if (!findOnError(e, ((TypeElement)clazz), onR.onError(), className)) {
  21.478                  return true;
  21.479              }
  21.480              body.append("        ").append(clazz.getSimpleName()).append(".").append(onR.onError()).append("(");
  21.481 -            body.append(className).append(".this, value);\n");
  21.482 +            body.append("model, ex);\n");
  21.483          }
  21.484          body.append(
  21.485 -            "      }\n" +
  21.486 -            "      @Override\n" +
  21.487 -            "      public void onMessage(org.netbeans.html.json.impl.RcvrJSON.MsgEvnt ev) {\n"
  21.488 +            "        return;\n" +
  21.489 +            "      } else if (type == 1) {\n" +
  21.490 +            "        Object[] ev = (Object[])data;\n"
  21.491              );
  21.492          if (expectsList) {
  21.493              body.append(
  21.494 -                "        " + modelClass + "[] arr = new " + modelClass + "[ev.dataSize()];\n"
  21.495 +                "        " + modelClass + "[] arr = new " + modelClass + "[ev.length];\n"
  21.496                  );
  21.497          } else {
  21.498              body.append(
  21.499 @@ -1086,8 +1097,8 @@
  21.500                  );
  21.501          }
  21.502          body.append(
  21.503 -            "        ev.dataRead(context, " + modelClass + ".class, arr);\n"
  21.504 -            );
  21.505 +            "        TYPE.copyJSON(model.proto.getContext(), ev, " + modelClass + ".class, arr);\n"
  21.506 +        );
  21.507          {
  21.508              body.append("        ").append(clazz.getSimpleName()).append(".").append(n).append("(");
  21.509              String sep = "";
  21.510 @@ -1099,38 +1110,36 @@
  21.511              body.append(");\n");
  21.512          }
  21.513          body.append(
  21.514 +            "        return;\n" +
  21.515              "      }\n" +
  21.516              "    }\n"
  21.517              );
  21.518 -        body.append("    ProcessResult pr = new ProcessResult();\n");
  21.519 -        body.append("    org.netbeans.html.json.impl.JSON.loadJSON(context, pr,\n        ");
  21.520 -        body.append(urlBefore).append(", ");
  21.521 +        method.append("    proto.loadJSON(" + index + ",\n        ");
  21.522 +        method.append(urlBefore).append(", ");
  21.523          if (jsonpVarName != null) {
  21.524 -            body.append(urlAfter);
  21.525 +            method.append(urlAfter);
  21.526          } else {
  21.527 -            body.append("null");
  21.528 +            method.append("null");
  21.529          }
  21.530          if (!"GET".equals(onR.method()) || dataMirror != null) {
  21.531 -            body.append(", \"").append(onR.method()).append('"');
  21.532 +            method.append(", \"").append(onR.method()).append('"');
  21.533              if (dataMirror != null) {
  21.534 -                body.append(", data");
  21.535 +                method.append(", data");
  21.536              } else {
  21.537 -                body.append(", null");
  21.538 +                method.append(", null");
  21.539              }
  21.540          } else {
  21.541 -            body.append(", null, null");
  21.542 +            method.append(", null, null");
  21.543          }
  21.544 -        body.append(");\n");
  21.545 +        method.append(");\n");
  21.546          return false;
  21.547      }
  21.548      
  21.549 -    private boolean generateWSReceiveBody(StringWriter body, OnReceive onR, ExecutableElement e, Element clazz, String className, boolean expectsList, String modelClass, String n, List<String> args, StringBuilder urlBefore, String jsonpVarName, StringBuilder urlAfter, String dataMirror) {
  21.550 +    private boolean generateWSReceiveBody(int index, StringWriter method, StringBuilder body, OnReceive onR, ExecutableElement e, Element clazz, String className, boolean expectsList, String modelClass, String n, List<String> args, StringBuilder urlBefore, String jsonpVarName, StringBuilder urlAfter, String dataMirror) {
  21.551          body.append(
  21.552 -            "    class ProcessResult extends org.netbeans.html.json.impl.RcvrJSON {\n" +
  21.553 -            "      @Override\n" +
  21.554 -            "      public void onOpen(org.netbeans.html.json.impl.RcvrJSON.MsgEvnt ev) {\n"
  21.555 -        );
  21.556 -        body.append("        ").append(clazz.getSimpleName()).append(".").append(n).append("(");
  21.557 +            "    case " + index + ": {\n" +
  21.558 +            "      if (type == 0) { /* on open */\n" +
  21.559 +            "        ").append(clazz.getSimpleName()).append(".").append(n).append("(");
  21.560          {
  21.561              String sep = "";
  21.562              for (String arg : args) {
  21.563 @@ -1145,10 +1154,9 @@
  21.564          }
  21.565          body.append(");\n");
  21.566          body.append(
  21.567 -            "      }\n" +
  21.568 -            "      @Override\n" +
  21.569 -            "      public void onError(org.netbeans.html.json.impl.RcvrJSON.MsgEvnt ev) {\n" +
  21.570 -            "        Exception value = ev.getException();\n"
  21.571 +            "        return;\n" +
  21.572 +            "      } else if (type == 2) { /* on error */\n" +
  21.573 +            "        Exception value = (Exception)data;\n"
  21.574              );
  21.575          if (onR.onError().isEmpty()) {
  21.576              body.append(
  21.577 @@ -1159,16 +1167,16 @@
  21.578                  return true;
  21.579              }
  21.580              body.append("        ").append(clazz.getSimpleName()).append(".").append(onR.onError()).append("(");
  21.581 -            body.append(className).append(".this, value);\n");
  21.582 +            body.append("model, value);\n");
  21.583          }
  21.584          body.append(
  21.585 -            "      }\n" +
  21.586 -            "      @Override\n" +
  21.587 -            "      public void onMessage(org.netbeans.html.json.impl.RcvrJSON.MsgEvnt ev) {\n"
  21.588 +            "        return;\n" +
  21.589 +            "      } else if (type == 1) {\n" +
  21.590 +            "        Object[] ev = (Object[])data;\n"
  21.591          );
  21.592          if (expectsList) {
  21.593              body.append(
  21.594 -                "        " + modelClass + "[] arr = new " + modelClass + "[ev.dataSize()];\n"
  21.595 +                "        " + modelClass + "[] arr = new " + modelClass + "[ev.length];\n"
  21.596                  );
  21.597          } else {
  21.598              body.append(
  21.599 @@ -1176,8 +1184,8 @@
  21.600                  );
  21.601          }
  21.602          body.append(
  21.603 -            "        ev.dataRead(context, " + modelClass + ".class, arr);\n"
  21.604 -            );
  21.605 +            "        TYPE.copyJSON(model.proto.getContext(), ev, " + modelClass + ".class, arr);\n"
  21.606 +        );
  21.607          {
  21.608              body.append("        ").append(clazz.getSimpleName()).append(".").append(n).append("(");
  21.609              String sep = "";
  21.610 @@ -1189,28 +1197,27 @@
  21.611              body.append(");\n");
  21.612          }
  21.613          body.append(
  21.614 -            "      }\n"
  21.615 +            "        return;\n" +
  21.616 +            "      }"
  21.617          );
  21.618          if (!onR.onError().isEmpty()) {
  21.619 +            body.append(" else if (type == 3) { /* on close */\n");
  21.620 +            body.append("        ").append(clazz.getSimpleName()).append(".").append(onR.onError()).append("(");
  21.621 +            body.append("model, null);\n");
  21.622              body.append(
  21.623 -                "      @Override\n"
  21.624 -              + "      public void onClose(org.netbeans.html.json.impl.RcvrJSON.MsgEvnt ev) {\n"
  21.625 -            );
  21.626 -            body.append("        ").append(clazz.getSimpleName()).append(".").append(onR.onError()).append("(");
  21.627 -            body.append(className).append(".this, null);\n");
  21.628 -            body.append(
  21.629 -                "      }\n"
  21.630 +                "        return;" +
  21.631 +                "      }"
  21.632              );
  21.633          }
  21.634 +        body.append("\n");
  21.635          body.append("    }\n");
  21.636 -        body.append("    if (this.ws_").append(e.getSimpleName()).append(" == null) {\n");
  21.637 -        body.append("      ProcessResult pr = new ProcessResult();\n");
  21.638 -        body.append("      this.ws_").append(e.getSimpleName());
  21.639 -        body.append("= org.netbeans.html.json.impl.JSON.openWS(context, pr,\n        ");
  21.640 -        body.append(urlBefore).append(", data);\n");
  21.641 -        body.append("    } else {\n");
  21.642 -        body.append("      this.ws_").append(e.getSimpleName()).append(".send(context, ").append(urlBefore).append(", data);\n");
  21.643 -        body.append("    }\n");
  21.644 +        method.append("    if (this.ws_").append(e.getSimpleName()).append(" == null) {\n");
  21.645 +        method.append("      this.ws_").append(e.getSimpleName());
  21.646 +        method.append("= proto.wsOpen(" + index + ", ");
  21.647 +        method.append(urlBefore).append(", data);\n");
  21.648 +        method.append("    } else {\n");
  21.649 +        method.append("      proto.wsSend(this.ws_").append(e.getSimpleName()).append(", ").append(urlBefore).append(", data);\n");
  21.650 +        method.append("    }\n");
  21.651          return false;
  21.652      }
  21.653  
  21.654 @@ -1227,30 +1234,34 @@
  21.655              first = false;
  21.656              String toCall = null;
  21.657              String toFinish = null;
  21.658 +            boolean addNull = true;
  21.659              if (ve.asType() == stringType) {
  21.660                  if (ve.getSimpleName().contentEquals("id")) {
  21.661                      params.append('"').append(id).append('"');
  21.662                      continue;
  21.663                  }
  21.664 -                toCall = "org.netbeans.html.json.impl.JSON.toString(context, ";
  21.665 +                toCall = "proto.toString(";
  21.666              }
  21.667              if (ve.asType().getKind() == TypeKind.DOUBLE) {
  21.668 -                toCall = "org.netbeans.html.json.impl.JSON.toNumber(context, ";
  21.669 +                toCall = "proto.toNumber(";
  21.670                  toFinish = ".doubleValue()";
  21.671              }
  21.672              if (ve.asType().getKind() == TypeKind.INT) {
  21.673 -                toCall = "org.netbeans.html.json.impl.JSON.toNumber(context, ";
  21.674 +                toCall = "proto.toNumber(";
  21.675                  toFinish = ".intValue()";
  21.676              }
  21.677              if (dataName != null && ve.getSimpleName().contentEquals(dataName) && isModel(ve.asType())) {
  21.678 -                toCall = "org.netbeans.html.json.impl.JSON.toModel(context, " + ve.asType() + ".class, ";
  21.679 +                toCall = "proto.toModel(" + ve.asType() + ".class, ";
  21.680 +                addNull = false;
  21.681              }
  21.682  
  21.683              if (toCall != null) {
  21.684                  params.append(toCall);
  21.685                  if (dataName != null && ve.getSimpleName().contentEquals(dataName)) {
  21.686                      params.append(dataName);
  21.687 -                    params.append(", null");
  21.688 +                    if (addNull) {
  21.689 +                        params.append(", null");
  21.690 +                    }
  21.691                  } else {
  21.692                      if (evName == null) {
  21.693                          final StringBuilder sb = new StringBuilder();
  21.694 @@ -1314,7 +1325,7 @@
  21.695                  rn = rn.substring(last + 1);
  21.696              }
  21.697              if (rn.equals(className)) {
  21.698 -                params.append(className).append(".this");
  21.699 +                params.append("model");
  21.700                  continue;
  21.701              }
  21.702              error(
  21.703 @@ -1352,7 +1363,7 @@
  21.704              w.write(sep);
  21.705              w.append("    sb.append('\"').append(\"" + p.name() + "\")");
  21.706                  w.append(".append('\"').append(\":\");\n");
  21.707 -            w.append("    sb.append(org.netbeans.html.json.impl.JSON.toJSON(prop_");
  21.708 +            w.append("    sb.append(TYPE.toJSON(prop_");
  21.709              w.append(p.name()).append("));\n");
  21.710              sep =    "    sb.append(',');\n";
  21.711          }
  21.712 @@ -1362,7 +1373,7 @@
  21.713      }
  21.714      private void writeClone(String className, Prprt[] props, Writer w) throws IOException {
  21.715          w.write("  public " + className + " clone() {\n");
  21.716 -        w.write("    return clone(context);\n");
  21.717 +        w.write("    return clone(proto.getContext());\n");
  21.718          w.write("  }\n");
  21.719          w.write("  private " + className + " clone(net.java.html.BrwsrCtx ctx) {\n");
  21.720          w.write("    " + className + " ret = new " + className + "(ctx);\n");
  21.721 @@ -1378,7 +1389,7 @@
  21.722                  }
  21.723                  w.write("    ret.prop_" + p.name() + " =  prop_" + p.name() + "  == null ? null : prop_" + p.name() + ".clone();\n");
  21.724              } else {
  21.725 -                w.write("    ret.prop_" + p.name() + ".cloneAll(ctx, prop_" + p.name() + ");\n");
  21.726 +                w.write("    proto.cloneList(ret.prop_" + p.name() + ", ctx, prop_" + p.name() + ");\n");
  21.727              }
  21.728          }
  21.729          
    22.1 --- a/json/src/main/java/org/netbeans/html/json/impl/PropertyBindingAccessor.java	Mon Dec 16 18:08:40 2013 +0100
    22.2 +++ b/json/src/main/java/org/netbeans/html/json/impl/PropertyBindingAccessor.java	Mon Jan 06 09:44:07 2014 +0100
    22.3 @@ -46,6 +46,7 @@
    22.4  import org.apidesign.html.json.spi.FunctionBinding;
    22.5  import org.apidesign.html.json.spi.JSONCall;
    22.6  import org.apidesign.html.json.spi.PropertyBinding;
    22.7 +import org.apidesign.html.json.spi.Proto;
    22.8  
    22.9  /**
   22.10   *
   22.11 @@ -63,19 +64,32 @@
   22.12          JSON.initClass(PropertyBinding.class);
   22.13      }
   22.14  
   22.15 -    protected abstract <M> PropertyBinding newBinding(PBData<M> d);
   22.16 -    protected abstract <M> FunctionBinding newFunction(FBData<M> d);
   22.17 +    protected abstract <M> PropertyBinding newBinding(
   22.18 +        Proto.Type<M> access, Bindings<?> bindings, String name, int index, M model, boolean readOnly
   22.19 +    );
   22.20      protected abstract JSONCall newCall(
   22.21          BrwsrCtx ctx, RcvrJSON callback, String urlBefore, String urlAfter,
   22.22          String method, Object data
   22.23      );
   22.24 -
   22.25      
   22.26 -    static <M> PropertyBinding create(PBData<M> d) {
   22.27 -        return DEFAULT.newBinding(d);
   22.28 +    protected abstract Bindings bindings(Proto proto, boolean initialize);
   22.29 +    protected abstract void notifyChange(Proto proto, int propIndex);
   22.30 +    protected abstract Proto findProto(Proto.Type<?> type, Object object);
   22.31 +    protected abstract <Model> Model cloneTo(Proto.Type<Model> type, Model model, BrwsrCtx c);
   22.32 +    protected abstract Object read(Proto.Type<?> from, BrwsrCtx c, Object data);
   22.33 +    
   22.34 +    static Bindings getBindings(Proto proto, boolean initialize) {
   22.35 +        return DEFAULT.bindings(proto, initialize);
   22.36      }
   22.37 -    static <M> FunctionBinding createFunction(FBData<M> d) {
   22.38 -        return DEFAULT.newFunction(d);
   22.39 +    
   22.40 +    static void notifyProtoChange(Proto proto, int propIndex) {
   22.41 +        DEFAULT.notifyChange(proto, propIndex);
   22.42 +    }
   22.43 +    
   22.44 +    static <M> PropertyBinding create(
   22.45 +        Proto.Type<M> access, Bindings<?> bindings, String name, int index, M model , boolean readOnly
   22.46 +    ) {
   22.47 +        return DEFAULT.newBinding(access, bindings, name, index, model, readOnly);
   22.48      }
   22.49      static JSONCall createCall(
   22.50          BrwsrCtx ctx, RcvrJSON callback, String urlBefore, String urlAfter, 
   22.51 @@ -83,53 +97,13 @@
   22.52      ) {
   22.53          return DEFAULT.newCall(ctx, callback, urlBefore, urlAfter, method, data);
   22.54      }
   22.55 -
   22.56 -    public static final class PBData<M> {
   22.57 -        public final String name;
   22.58 -        public final boolean readOnly;
   22.59 -        private final M model;
   22.60 -        private final SetAndGet<M> access;
   22.61 -        private final Bindings<?> bindings;
   22.62 -
   22.63 -        public PBData(Bindings<?> bindings, String name, M model, SetAndGet<M> access, boolean readOnly) {
   22.64 -            this.bindings = bindings;
   22.65 -            this.name = name;
   22.66 -            this.model = model;
   22.67 -            this.access = access;
   22.68 -            this.readOnly = readOnly;
   22.69 -        }
   22.70 -
   22.71 -        public void setValue(Object v) {
   22.72 -            access.setValue(model, v);
   22.73 -        }
   22.74 -
   22.75 -        public Object getValue() {
   22.76 -            return access.getValue(model);
   22.77 -        }
   22.78 -
   22.79 -        public boolean isReadOnly() {
   22.80 -            return readOnly;
   22.81 -        }
   22.82 -
   22.83 -        public Bindings getBindings() {
   22.84 -            return bindings;
   22.85 -        }
   22.86 -    } // end of PBData
   22.87 -    
   22.88 -    public static final class FBData<M> {
   22.89 -        public final String name;
   22.90 -        private final M model;
   22.91 -        private final Callback<M> access;
   22.92 -
   22.93 -        public FBData(String name, M model, Callback<M> access) {
   22.94 -            this.name = name;
   22.95 -            this.model = model;
   22.96 -            this.access = access;
   22.97 -        }
   22.98 -
   22.99 -
  22.100 -        public void call(Object data, Object ev) {
  22.101 -            access.call(model, data, ev);
  22.102 -        }
  22.103 -    } // end of FBData
  22.104 +    static Proto protoFor(Proto.Type<?> type, Object object) {
  22.105 +        return DEFAULT.findProto(type, object);
  22.106 +    }
  22.107 +    static <Model> Model clone(Proto.Type<Model> type, Model model, BrwsrCtx c) {
  22.108 +        return DEFAULT.cloneTo(type, model, c);
  22.109 +    }
  22.110 +    static Object readFrom(Proto.Type<?> from, BrwsrCtx c, Object data) {
  22.111 +        return DEFAULT.read(from, c, data);
  22.112 +    }
  22.113  }
    23.1 --- a/json/src/main/java/org/netbeans/html/json/impl/RcvrJSON.java	Mon Dec 16 18:08:40 2013 +0100
    23.2 +++ b/json/src/main/java/org/netbeans/html/json/impl/RcvrJSON.java	Mon Jan 06 09:44:07 2014 +0100
    23.3 @@ -42,6 +42,7 @@
    23.4   */
    23.5  package org.netbeans.html.json.impl;
    23.6  
    23.7 +import java.util.ArrayList;
    23.8  import net.java.html.BrwsrCtx;
    23.9  
   23.10  /** Super type for those who wish to receive JSON messages.
   23.11 @@ -73,13 +74,10 @@
   23.12              return new Exception(t);
   23.13          }
   23.14          
   23.15 -        public int dataSize() {
   23.16 -            return -1;
   23.17 +        public Object[] getValues() {
   23.18 +            return null;
   23.19          }
   23.20          
   23.21 -        public <Data> void dataRead(BrwsrCtx ctx, Class<? extends Data> type, Data[] fillTheArray) {
   23.22 -        }
   23.23 -
   23.24          public abstract void dispatch(RcvrJSON r);
   23.25          
   23.26          public static MsgEvnt createError(final Throwable t) {
   23.27 @@ -99,26 +97,8 @@
   23.28          public static MsgEvnt createMessage(final Object value) {
   23.29              return new MsgEvnt() {
   23.30                  @Override
   23.31 -                public int dataSize() {
   23.32 -                    if (value instanceof Object[]) {
   23.33 -                        return ((Object[])value).length;
   23.34 -                    } else {
   23.35 -                        return 1;
   23.36 -                    }
   23.37 -                }
   23.38 -                
   23.39 -                @Override
   23.40 -                public <Data> void dataRead(BrwsrCtx context, Class<? extends Data> type, Data[] arr) {
   23.41 -                    if (value instanceof Object[]) {
   23.42 -                        Object[] data = ((Object[]) value);
   23.43 -                        for (int i = 0; i < data.length && i < arr.length; i++) {
   23.44 -                            arr[i] = org.netbeans.html.json.impl.JSON.read(context, type, data[i]);
   23.45 -                        }
   23.46 -                    } else {
   23.47 -                        if (arr.length > 0) {
   23.48 -                            arr[0] = org.netbeans.html.json.impl.JSON.read(context, type, value);
   23.49 -                        }
   23.50 -                    }
   23.51 +                public Object[] getValues() {
   23.52 +                    return value instanceof Object[] ? (Object[])value : new Object[] { value };
   23.53                  }
   23.54                  
   23.55                  @Override
   23.56 @@ -145,5 +125,4 @@
   23.57                  }
   23.58              };
   23.59          }
   23.60 -    } // end MsgEvnt
   23.61 -}
   23.62 +    } }
    24.1 --- a/json/src/main/java/org/netbeans/html/json/impl/SetAndGet.java	Mon Dec 16 18:08:40 2013 +0100
    24.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
    24.3 @@ -1,54 +0,0 @@
    24.4 -/**
    24.5 - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
    24.6 - *
    24.7 - * Copyright 2013-2013 Oracle and/or its affiliates. All rights reserved.
    24.8 - *
    24.9 - * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
   24.10 - * Other names may be trademarks of their respective owners.
   24.11 - *
   24.12 - * The contents of this file are subject to the terms of either the GNU
   24.13 - * General Public License Version 2 only ("GPL") or the Common
   24.14 - * Development and Distribution License("CDDL") (collectively, the
   24.15 - * "License"). You may not use this file except in compliance with the
   24.16 - * License. You can obtain a copy of the License at
   24.17 - * http://www.netbeans.org/cddl-gplv2.html
   24.18 - * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
   24.19 - * specific language governing permissions and limitations under the
   24.20 - * License.  When distributing the software, include this License Header
   24.21 - * Notice in each file and include the License file at
   24.22 - * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
   24.23 - * particular file as subject to the "Classpath" exception as provided
   24.24 - * by Oracle in the GPL Version 2 section of the License file that
   24.25 - * accompanied this code. If applicable, add the following below the
   24.26 - * License Header, with the fields enclosed by brackets [] replaced by
   24.27 - * your own identifying information:
   24.28 - * "Portions Copyrighted [year] [name of copyright owner]"
   24.29 - *
   24.30 - * Contributor(s):
   24.31 - *
   24.32 - * The Original Software is NetBeans. The Initial Developer of the Original
   24.33 - * Software is Oracle. Portions Copyright 2013-2013 Oracle. All Rights Reserved.
   24.34 - *
   24.35 - * If you wish your version of this file to be governed by only the CDDL
   24.36 - * or only the GPL Version 2, indicate your decision by adding
   24.37 - * "[Contributor] elects to include this software in this distribution
   24.38 - * under the [CDDL or GPL Version 2] license." If you do not indicate a
   24.39 - * single choice of license, a recipient has the option to distribute
   24.40 - * your version of this file under either the CDDL, the GPL Version 2 or
   24.41 - * to extend the choice of license to its licensees as provided above.
   24.42 - * However, if you add GPL Version 2 code and therefore, elected the GPL
   24.43 - * Version 2 license, then the option applies only if the new code is
   24.44 - * made subject to such option by the copyright holder.
   24.45 - */
   24.46 -package org.netbeans.html.json.impl;
   24.47 -
   24.48 -import org.apidesign.html.json.spi.PropertyBinding;
   24.49 -
   24.50 -/** A way to implement a {@link PropertyBinding}.
   24.51 - *
   24.52 - * @author Jaroslav Tulach <jtulach@netbeans.org>
   24.53 - */
   24.54 -public interface SetAndGet<Data> {
   24.55 -    public void setValue(Data data, Object value);
   24.56 -    public Object getValue(Data data);
   24.57 -}
    25.1 --- a/json/src/main/java/org/netbeans/html/json/impl/WrapperObject.java	Mon Dec 16 18:08:40 2013 +0100
    25.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
    25.3 @@ -1,82 +0,0 @@
    25.4 -/**
    25.5 - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
    25.6 - *
    25.7 - * Copyright 2013-2013 Oracle and/or its affiliates. All rights reserved.
    25.8 - *
    25.9 - * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
   25.10 - * Other names may be trademarks of their respective owners.
   25.11 - *
   25.12 - * The contents of this file are subject to the terms of either the GNU
   25.13 - * General Public License Version 2 only ("GPL") or the Common
   25.14 - * Development and Distribution License("CDDL") (collectively, the
   25.15 - * "License"). You may not use this file except in compliance with the
   25.16 - * License. You can obtain a copy of the License at
   25.17 - * http://www.netbeans.org/cddl-gplv2.html
   25.18 - * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
   25.19 - * specific language governing permissions and limitations under the
   25.20 - * License.  When distributing the software, include this License Header
   25.21 - * Notice in each file and include the License file at
   25.22 - * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
   25.23 - * particular file as subject to the "Classpath" exception as provided
   25.24 - * by Oracle in the GPL Version 2 section of the License file that
   25.25 - * accompanied this code. If applicable, add the following below the
   25.26 - * License Header, with the fields enclosed by brackets [] replaced by
   25.27 - * your own identifying information:
   25.28 - * "Portions Copyrighted [year] [name of copyright owner]"
   25.29 - *
   25.30 - * Contributor(s):
   25.31 - *
   25.32 - * The Original Software is NetBeans. The Initial Developer of the Original
   25.33 - * Software is Oracle. Portions Copyright 2013-2013 Oracle. All Rights Reserved.
   25.34 - *
   25.35 - * If you wish your version of this file to be governed by only the CDDL
   25.36 - * or only the GPL Version 2, indicate your decision by adding
   25.37 - * "[Contributor] elects to include this software in this distribution
   25.38 - * under the [CDDL or GPL Version 2] license." If you do not indicate a
   25.39 - * single choice of license, a recipient has the option to distribute
   25.40 - * your version of this file under either the CDDL, the GPL Version 2 or
   25.41 - * to extend the choice of license to its licensees as provided above.
   25.42 - * However, if you add GPL Version 2 code and therefore, elected the GPL
   25.43 - * Version 2 license, then the option applies only if the new code is
   25.44 - * made subject to such option by the copyright holder.
   25.45 - */
   25.46 -package org.netbeans.html.json.impl;
   25.47 -
   25.48 -import java.util.Collection;
   25.49 -import org.netbeans.html.json.impl.PropertyBindingAccessor.PBData;
   25.50 -
   25.51 -/** A way to extract real object from a model classes.
   25.52 - *
   25.53 - * @author Jaroslav Tulach <jtulach@netbeans.org>
   25.54 - */
   25.55 -public final class WrapperObject {
   25.56 -    private Object ko;
   25.57 -    
   25.58 -    private WrapperObject() {
   25.59 -    }
   25.60 -    
   25.61 -    public void setRealObject(Object ko) {
   25.62 -        this.ko = ko;
   25.63 -    }
   25.64 -    
   25.65 -    public static Object find(Object object) {
   25.66 -        return find(object, null);
   25.67 -    }
   25.68 -    
   25.69 -    public static Object find(Object object, Bindings model) {
   25.70 -        if (object == null) {
   25.71 -            return null;
   25.72 -        }
   25.73 -        
   25.74 -        if (object instanceof JSONList) {
   25.75 -            return ((JSONList<?>)object).koData();
   25.76 -        }
   25.77 -        if (object instanceof Collection) {
   25.78 -            return JSONList.koData((Collection<?>)object, model);
   25.79 -        }
   25.80 -        
   25.81 -        WrapperObject ro = new WrapperObject();
   25.82 -        object.equals(ro);
   25.83 -        return ro.ko;
   25.84 -    }
   25.85 -}
    26.1 --- a/json/src/test/java/net/java/html/json/MapModelTest.java	Mon Dec 16 18:08:40 2013 +0100
    26.2 +++ b/json/src/test/java/net/java/html/json/MapModelTest.java	Mon Jan 06 09:44:07 2014 +0100
    26.3 @@ -49,12 +49,12 @@
    26.4  import java.util.HashMap;
    26.5  import java.util.Map;
    26.6  import org.apidesign.html.context.spi.Contexts;
    26.7 -import org.netbeans.html.json.impl.WrapperObject;
    26.8  import org.apidesign.html.json.spi.FunctionBinding;
    26.9  import org.apidesign.html.json.spi.JSONCall;
   26.10  import org.apidesign.html.json.spi.PropertyBinding;
   26.11  import org.apidesign.html.json.spi.Technology;
   26.12  import org.apidesign.html.json.spi.Transfer;
   26.13 +import org.netbeans.html.json.impl.JSON;
   26.14  import org.testng.annotations.BeforeMethod;
   26.15  import org.testng.annotations.Test;
   26.16  import static org.testng.Assert.*;
   26.17 @@ -77,7 +77,7 @@
   26.18          Person p = Models.bind(new Person(), c).applyBindings();
   26.19          p.setFirstName("Jarda");
   26.20          
   26.21 -        Map m = (Map)WrapperObject.find(p);
   26.22 +        Map m = (Map)JSON.find(p);
   26.23          Object v = m.get("firstName");
   26.24          assertNotNull(v, "Value should be in the map");
   26.25          assertEquals(v.getClass(), One.class, "It is instance of One");
   26.26 @@ -98,7 +98,7 @@
   26.27          Person p = Models.bind(new Person(), c);
   26.28          p.setFirstName("Jirka");
   26.29          
   26.30 -        Map m = (Map)WrapperObject.find(p);
   26.31 +        Map m = (Map)JSON.find(p);
   26.32          Object v = m.get("firstName");
   26.33          assertNotNull(v, "Value should be in the map");
   26.34          assertEquals(v.getClass(), One.class, "It is instance of One");
   26.35 @@ -122,7 +122,7 @@
   26.36      @Test public void derivedProperty() throws Exception {
   26.37          Person p = Models.bind(new Person(), c);
   26.38          
   26.39 -        Map m = (Map)WrapperObject.find(p);
   26.40 +        Map m = (Map)JSON.find(p);
   26.41          Object v = m.get("fullName");
   26.42          assertNotNull(v, "Value should be in the map");
   26.43          assertEquals(v.getClass(), One.class, "It is instance of One");
   26.44 @@ -135,7 +135,7 @@
   26.45          p.setFirstName("Trans");
   26.46          p.setSex(Sex.MALE);
   26.47          
   26.48 -        Map m = (Map)WrapperObject.find(p);
   26.49 +        Map m = (Map)JSON.find(p);
   26.50          Object o = m.get("changeSex");
   26.51          assertNotNull(o, "Function registered in the model");
   26.52          assertEquals(o.getClass(), One.class);
   26.53 @@ -152,7 +152,7 @@
   26.54          Person p = Models.bind(new Person(), c);
   26.55          p.setFirstName("Trans");
   26.56          
   26.57 -        Map m = (Map)WrapperObject.find(p);
   26.58 +        Map m = (Map)JSON.find(p);
   26.59          Object o = m.get("changeSex");
   26.60          assertNotNull(o, "Function registered in the model");
   26.61          assertEquals(o.getClass(), One.class);
    27.1 --- a/json/src/test/java/net/java/html/json/PrimitiveArrayTest.java	Mon Dec 16 18:08:40 2013 +0100
    27.2 +++ b/json/src/test/java/net/java/html/json/PrimitiveArrayTest.java	Mon Jan 06 09:44:07 2014 +0100
    27.3 @@ -42,6 +42,7 @@
    27.4   */
    27.5  package net.java.html.json;
    27.6  
    27.7 +import java.util.List;
    27.8  import org.testng.Assert;
    27.9  import org.testng.annotations.Test;
   27.10  
   27.11 @@ -53,11 +54,16 @@
   27.12      @Property(name = "array", type = byte.class, array = true)
   27.13  })
   27.14  public class PrimitiveArrayTest {
   27.15 +    @ComputedProperty static int length(List<Byte> array) {
   27.16 +        return array.size();
   27.17 +    }
   27.18 +    
   27.19      @Test public void generatedConstructorWithPrimitiveType() {
   27.20          byte[] arr = new byte[10];
   27.21          arr[3] = 10;
   27.22          ByteArray a = new ByteArray(arr);
   27.23          Assert.assertEquals(a.getArray().size(), 10, "Ten elements");
   27.24          Assert.assertEquals(a.getArray().get(3).byteValue(), 10, "Value ten");
   27.25 +        Assert.assertEquals(a.getLength(), 10, "Derived property is OK too");
   27.26      }
   27.27  }
    28.1 --- a/json/src/test/java/net/java/html/json/TypesTest.java	Mon Dec 16 18:08:40 2013 +0100
    28.2 +++ b/json/src/test/java/net/java/html/json/TypesTest.java	Mon Jan 06 09:44:07 2014 +0100
    28.3 @@ -46,9 +46,9 @@
    28.4  import java.util.Map;
    28.5  import net.java.html.json.MapModelTest.One;
    28.6  import org.apidesign.html.context.spi.Contexts;
    28.7 -import org.netbeans.html.json.impl.WrapperObject;
    28.8  import org.apidesign.html.json.spi.Technology;
    28.9  import org.apidesign.html.json.spi.Transfer;
   28.10 +import org.netbeans.html.json.impl.JSON;
   28.11  import org.testng.annotations.BeforeMethod;
   28.12  import org.testng.annotations.Test;
   28.13  import static org.testng.Assert.*;
   28.14 @@ -111,10 +111,10 @@
   28.15          t.setFloatX(99f);
   28.16          */
   28.17          
   28.18 -        Object json = WrapperObject.find(t);
   28.19 +        Object json = JSON.find(t);
   28.20          
   28.21          Types copy = Models.bind(new Types(), c);
   28.22 -        Map copyMap = (Map) WrapperObject.find(copy);
   28.23 +        Map copyMap = (Map) JSON.find(copy);
   28.24          One o = (One) copyMap.get("readFromEvent");
   28.25          o.fb.call(null, json);
   28.26          
    29.1 --- a/json/src/test/java/org/netbeans/html/json/impl/JSONListTest.java	Mon Dec 16 18:08:40 2013 +0100
    29.2 +++ b/json/src/test/java/org/netbeans/html/json/impl/JSONListTest.java	Mon Jan 06 09:44:07 2014 +0100
    29.3 @@ -80,7 +80,7 @@
    29.4          p.setLastName("2");
    29.5          p.setSex(Sex.MALE);
    29.6  
    29.7 -        Object real = WrapperObject.find(p);
    29.8 +        Object real = JSON.find(p);
    29.9          assertEquals(this, real, "I am the right model");
   29.10      }
   29.11      
   29.12 @@ -135,7 +135,7 @@
   29.13          People people = Models.bind(new People(), c).applyBindings();
   29.14          people.getInfo().add(p);
   29.15          
   29.16 -        Object real = WrapperObject.find(people.getInfo());
   29.17 +        Object real = JSON.find(people.getInfo());
   29.18          assertEquals(real, this, "I am the model of the array");
   29.19      }
   29.20  
    30.1 --- a/json/src/test/java/org/netbeans/html/json/impl/JSONTest.java	Mon Dec 16 18:08:40 2013 +0100
    30.2 +++ b/json/src/test/java/org/netbeans/html/json/impl/JSONTest.java	Mon Jan 06 09:44:07 2014 +0100
    30.3 @@ -42,7 +42,6 @@
    30.4   */
    30.5  package org.netbeans.html.json.impl;
    30.6  
    30.7 -import org.netbeans.html.json.impl.JSON;
    30.8  import static org.testng.Assert.*;
    30.9  import org.testng.annotations.Test;
   30.10  
    31.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    31.2 +++ b/ko-osgi-test/pom.xml	Mon Jan 06 09:44:07 2014 +0100
    31.3 @@ -0,0 +1,136 @@
    31.4 +<?xml version="1.0" encoding="UTF-8"?>
    31.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">
    31.6 +    <modelVersion>4.0.0</modelVersion>
    31.7 +    <parent>
    31.8 +        <groupId>org.netbeans.html</groupId>
    31.9 +        <artifactId>pom</artifactId>
   31.10 +        <version>0.7-SNAPSHOT</version>
   31.11 +    </parent>
   31.12 +    <name>KO Tests in an OSGi Container</name>
   31.13 +    <artifactId>ko-osgi-test</artifactId>
   31.14 +    <packaging>bundle</packaging>
   31.15 +    <description>Runs the TCK for Knockout in an OSGi Container</description>
   31.16 +    <properties>
   31.17 +        <netbeans.compile.on.save>none</netbeans.compile.on.save>
   31.18 +    </properties>
   31.19 +    <build>
   31.20 +        <plugins>
   31.21 +            <plugin>
   31.22 +                <groupId>org.apache.felix</groupId>
   31.23 +                <artifactId>maven-bundle-plugin</artifactId>
   31.24 +            </plugin>
   31.25 +            <plugin>
   31.26 +                <groupId>org.apache.maven.plugins</groupId>
   31.27 +                <artifactId>maven-compiler-plugin</artifactId>
   31.28 +                <version>2.3.2</version>
   31.29 +                <configuration>
   31.30 +                    <source>1.7</source>
   31.31 +                    <target>1.7</target>
   31.32 +                </configuration>
   31.33 +            </plugin>
   31.34 +            <plugin>
   31.35 +                <artifactId>maven-failsafe-plugin</artifactId>
   31.36 +                <version>2.16</version>
   31.37 +                <configuration>
   31.38 +                    <additionalClasspathElements>
   31.39 +                        <additionalClasspathElement>${project.build.directory}/${project.build.finalName}.jar</additionalClasspathElement>
   31.40 +                    </additionalClasspathElements>
   31.41 +                </configuration>
   31.42 +                <executions>
   31.43 +                    <execution>
   31.44 +                        <goals>
   31.45 +                            <goal>integration-test</goal>
   31.46 +                            <goal>verify</goal>
   31.47 +                        </goals>
   31.48 +                    </execution>
   31.49 +                </executions>
   31.50 +            </plugin>
   31.51 +        </plugins>
   31.52 +    </build>
   31.53 +    <dependencies>
   31.54 +        <dependency>
   31.55 +            <groupId>com.oracle</groupId>
   31.56 +            <artifactId>javafx</artifactId>
   31.57 +            <version>2.2</version>
   31.58 +            <scope>system</scope>
   31.59 +            <systemPath>${jfxrt.jar}</systemPath>
   31.60 +        </dependency>
   31.61 +        <dependency>
   31.62 +            <groupId>de.twentyeleven.skysail</groupId>
   31.63 +            <artifactId>org.json-osgi</artifactId>
   31.64 +        </dependency>
   31.65 +        <dependency>
   31.66 +            <groupId>org.netbeans.html</groupId>
   31.67 +            <artifactId>net.java.html.json</artifactId>
   31.68 +            <version>${project.version}</version>
   31.69 +        </dependency>
   31.70 +        <dependency>
   31.71 +            <groupId>org.testng</groupId>
   31.72 +            <artifactId>testng</artifactId>
   31.73 +            <scope>test</scope>
   31.74 +        </dependency>
   31.75 +        <dependency>
   31.76 +            <groupId>${project.groupId}</groupId>
   31.77 +            <artifactId>net.java.html.json.tck</artifactId>
   31.78 +            <version>${project.version}</version>
   31.79 +        </dependency>
   31.80 +        <dependency>
   31.81 +            <groupId>org.netbeans.api</groupId>
   31.82 +            <artifactId>org-openide-util-lookup</artifactId>
   31.83 +            <scope>provided</scope>
   31.84 +        </dependency>
   31.85 +        <dependency>
   31.86 +            <groupId>org.netbeans.html</groupId>
   31.87 +            <artifactId>net.java.html.boot</artifactId>
   31.88 +            <version>${project.version}</version>
   31.89 +            <type>jar</type>
   31.90 +        </dependency>
   31.91 +        <dependency>
   31.92 +            <groupId>${project.groupId}</groupId>
   31.93 +            <artifactId>ko-fx</artifactId>
   31.94 +            <version>${project.version}</version>
   31.95 +        </dependency>
   31.96 +        <dependency>
   31.97 +            <groupId>${project.groupId}</groupId>
   31.98 +            <artifactId>net.java.html.boot.fx</artifactId>
   31.99 +            <version>${project.version}</version>
  31.100 +            <scope>test</scope>
  31.101 +        </dependency>
  31.102 +        <dependency>
  31.103 +            <groupId>org.glassfish.grizzly</groupId>
  31.104 +            <artifactId>grizzly-http-server</artifactId>
  31.105 +            <version>2.3.3</version>
  31.106 +            <scope>test</scope>
  31.107 +        </dependency>
  31.108 +        <dependency>
  31.109 +            <groupId>org.glassfish.grizzly</groupId>
  31.110 +            <artifactId>grizzly-websockets-server</artifactId>
  31.111 +            <version>2.3.3</version>
  31.112 +            <scope>test</scope>
  31.113 +            <type>jar</type>
  31.114 +        </dependency>
  31.115 +        <dependency>
  31.116 +            <groupId>org.glassfish.grizzly</groupId>
  31.117 +            <artifactId>grizzly-http-servlet</artifactId>
  31.118 +            <version>2.3.3</version>
  31.119 +            <scope>test</scope>
  31.120 +        </dependency>    
  31.121 +        <dependency>
  31.122 +            <groupId>javax.servlet</groupId>
  31.123 +            <artifactId>javax.servlet-api</artifactId>
  31.124 +            <scope>test</scope>
  31.125 +            <version>3.1.0</version>
  31.126 +        </dependency>
  31.127 +        <dependency>
  31.128 +            <groupId>org.eclipse</groupId>
  31.129 +            <artifactId>org.eclipse.osgi</artifactId>
  31.130 +            <version>3.8.0.v20120529-1548</version>
  31.131 +        </dependency>
  31.132 +        <dependency>
  31.133 +            <groupId>${project.groupId}</groupId>
  31.134 +            <artifactId>equinox-agentclass-hook</artifactId>
  31.135 +            <version>${project.version}</version>
  31.136 +            <scope>test</scope>
  31.137 +        </dependency>
  31.138 +    </dependencies>
  31.139 +</project>
  31.140 \ No newline at end of file
    32.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    32.2 +++ b/ko-osgi-test/src/main/java/org/netbeans/html/ko/osgi/test/KnockoutEquinoxTCKImpl.java	Mon Jan 06 09:44:07 2014 +0100
    32.3 @@ -0,0 +1,219 @@
    32.4 +/**
    32.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
    32.6 + *
    32.7 + * Copyright 2013-2013 Oracle and/or its affiliates. All rights reserved.
    32.8 + *
    32.9 + * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
   32.10 + * Other names may be trademarks of their respective owners.
   32.11 + *
   32.12 + * The contents of this file are subject to the terms of either the GNU
   32.13 + * General Public License Version 2 only ("GPL") or the Common
   32.14 + * Development and Distribution License("CDDL") (collectively, the
   32.15 + * "License"). You may not use this file except in compliance with the
   32.16 + * License. You can obtain a copy of the License at
   32.17 + * http://www.netbeans.org/cddl-gplv2.html
   32.18 + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
   32.19 + * specific language governing permissions and limitations under the
   32.20 + * License.  When distributing the software, include this License Header
   32.21 + * Notice in each file and include the License file at
   32.22 + * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
   32.23 + * particular file as subject to the "Classpath" exception as provided
   32.24 + * by Oracle in the GPL Version 2 section of the License file that
   32.25 + * accompanied this code. If applicable, add the following below the
   32.26 + * License Header, with the fields enclosed by brackets [] replaced by
   32.27 + * your own identifying information:
   32.28 + * "Portions Copyrighted [year] [name of copyright owner]"
   32.29 + *
   32.30 + * Contributor(s):
   32.31 + *
   32.32 + * The Original Software is NetBeans. The Initial Developer of the Original
   32.33 + * Software is Oracle. Portions Copyright 2013-2013 Oracle. All Rights Reserved.
   32.34 + *
   32.35 + * If you wish your version of this file to be governed by only the CDDL
   32.36 + * or only the GPL Version 2, indicate your decision by adding
   32.37 + * "[Contributor] elects to include this software in this distribution
   32.38 + * under the [CDDL or GPL Version 2] license." If you do not indicate a
   32.39 + * single choice of license, a recipient has the option to distribute
   32.40 + * your version of this file under either the CDDL, the GPL Version 2 or
   32.41 + * to extend the choice of license to its licensees as provided above.
   32.42 + * However, if you add GPL Version 2 code and therefore, elected the GPL
   32.43 + * Version 2 license, then the option applies only if the new code is
   32.44 + * made subject to such option by the copyright holder.
   32.45 + */
   32.46 +package org.netbeans.html.ko.osgi.test;
   32.47 +
   32.48 +import java.io.BufferedReader;
   32.49 +import java.io.IOException;
   32.50 +import java.io.InputStreamReader;
   32.51 +import java.lang.reflect.Method;
   32.52 +import java.net.URI;
   32.53 +import java.net.URISyntaxException;
   32.54 +import java.net.URL;
   32.55 +import java.net.URLConnection;
   32.56 +import java.util.Map;
   32.57 +import java.util.concurrent.Callable;
   32.58 +import java.util.concurrent.Executors;
   32.59 +import net.java.html.BrwsrCtx;
   32.60 +import net.java.html.boot.BrowserBuilder;
   32.61 +import net.java.html.js.JavaScriptBody;
   32.62 +import org.apidesign.html.boot.spi.Fn;
   32.63 +import org.apidesign.html.context.spi.Contexts;
   32.64 +import org.apidesign.html.json.spi.Technology;
   32.65 +import org.apidesign.html.json.spi.Transfer;
   32.66 +import org.apidesign.html.json.tck.KnockoutTCK;
   32.67 +import org.json.JSONException;
   32.68 +import org.json.JSONObject;
   32.69 +import org.openide.util.lookup.ServiceProvider;
   32.70 +import org.osgi.framework.Bundle;
   32.71 +import org.osgi.framework.BundleContext;
   32.72 +import org.osgi.framework.FrameworkUtil;
   32.73 +
   32.74 +/**
   32.75 + *
   32.76 + * @author Jaroslav Tulach <jtulach@netbeans.org>
   32.77 + */
   32.78 +@ServiceProvider(service = KnockoutTCK.class)
   32.79 +public class KnockoutEquinoxTCKImpl extends KnockoutTCK implements Callable<Class[]> {
   32.80 +    
   32.81 +    private static Fn.Presenter browserContext;
   32.82 +
   32.83 +    public static Class loadOSGiClass(String name, BundleContext ctx) throws Exception {
   32.84 +        for (Bundle b : ctx.getBundles()) {
   32.85 +            try {
   32.86 +                Class<?> osgiClass = b.loadClass(name);
   32.87 +                if (osgiClass != null && osgiClass.getClassLoader() != ClassLoader.getSystemClassLoader()) {
   32.88 +                    return osgiClass;
   32.89 +                }
   32.90 +            } catch (ClassNotFoundException cnfe) {
   32.91 +                // go on
   32.92 +            }
   32.93 +        }
   32.94 +        throw new IllegalStateException("Cannot load " + name + " from the OSGi container!");
   32.95 +    }
   32.96 +
   32.97 +    @Override
   32.98 +    public Class[] call() throws Exception {
   32.99 +        return testClasses();
  32.100 +    }
  32.101 +    
  32.102 +    public static void start(URI server) throws Exception {
  32.103 +        final BrowserBuilder bb = BrowserBuilder.newBrowser().loadClass(KnockoutEquinoxTCKImpl.class).
  32.104 +            loadPage(server.toString()).
  32.105 +            invoke("initialized");
  32.106 +
  32.107 +        Executors.newSingleThreadExecutor().submit(new Runnable() {
  32.108 +            @Override
  32.109 +            public void run() {
  32.110 +                try {
  32.111 +                    final ClassLoader osgiClassLoader = BrowserBuilder.class.getClassLoader();
  32.112 +                    Thread.currentThread().setContextClassLoader(osgiClassLoader);
  32.113 +                    bb.showAndWait();
  32.114 +                } catch (Throwable t) {
  32.115 +                    t.printStackTrace();
  32.116 +                }
  32.117 +            }
  32.118 +        });
  32.119 +    }
  32.120 +
  32.121 +    public static void initialized() throws Exception {
  32.122 +        Bundle bundle = FrameworkUtil.getBundle(KnockoutEquinoxTCKImpl.class);
  32.123 +        if (bundle == null) {
  32.124 +            throw new IllegalStateException(
  32.125 +                "Should be loaded from a bundle. But was: " + KnockoutEquinoxTCKImpl.class.getClassLoader()
  32.126 +            );
  32.127 +        }
  32.128 +        Class<?> classpathClass = ClassLoader.getSystemClassLoader().loadClass(
  32.129 +            "org.netbeans.html.ko.osgi.test.KnockoutEquinoxIT"
  32.130 +        );
  32.131 +        Method m = classpathClass.getMethod("initialized", Class.class, Object.class);
  32.132 +        browserContext = Fn.activePresenter();
  32.133 +        m.invoke(null, KnockoutEquinoxTCKImpl.class, browserContext);
  32.134 +    }
  32.135 +    
  32.136 +    @Override
  32.137 +    public BrwsrCtx createContext() {
  32.138 +        try {
  32.139 +            Class<?> fxCls = loadOSGiClass(
  32.140 +                "org.netbeans.html.kofx.FXContext",
  32.141 +                FrameworkUtil.getBundle(KnockoutEquinoxTCKImpl.class).getBundleContext()
  32.142 +            );
  32.143 +            Object fx = fxCls.getConstructor(Fn.Presenter.class).newInstance(browserContext);
  32.144 +            Contexts.Builder cb = Contexts.newBuilder().
  32.145 +                register(Technology.class, (Technology)fx, 10).
  32.146 +                register(Transfer.class, (Transfer)fx, 10);
  32.147 +//        if (fx.areWebSocketsSupported()) {
  32.148 +//            cb.register(WSTransfer.class, fx, 10);
  32.149 +//        }
  32.150 +            return cb.build();
  32.151 +        } catch (Exception ex) {
  32.152 +            throw new IllegalStateException(ex);
  32.153 +        }
  32.154 +    }
  32.155 +
  32.156 +    @Override
  32.157 +    public Object createJSON(Map<String, Object> values) {
  32.158 +        JSONObject json = new JSONObject();
  32.159 +        for (Map.Entry<String, Object> entry : values.entrySet()) {
  32.160 +            try {
  32.161 +                json.put(entry.getKey(), entry.getValue());
  32.162 +            } catch (JSONException ex) {
  32.163 +                throw new IllegalStateException(ex);
  32.164 +            }
  32.165 +        }
  32.166 +        return json;
  32.167 +    }
  32.168 +
  32.169 +    @Override
  32.170 +    @JavaScriptBody(args = { "s", "args" }, body = ""
  32.171 +        + "var f = new Function(s); "
  32.172 +        + "return f.apply(null, args);"
  32.173 +    )
  32.174 +    public native Object executeScript(String script, Object[] arguments);
  32.175 +
  32.176 +    @JavaScriptBody(args = {  }, body = 
  32.177 +          "var h;"
  32.178 +        + "if (!!window && !!window.location && !!window.location.href)\n"
  32.179 +        + "  h = window.location.href;\n"
  32.180 +        + "else "
  32.181 +        + "  h = null;"
  32.182 +        + "return h;\n"
  32.183 +    )
  32.184 +    private static native String findBaseURL();
  32.185 +    
  32.186 +    @Override
  32.187 +    public URI prepareURL(String content, String mimeType, String[] parameters) {
  32.188 +        try {
  32.189 +            final URL baseURL = new URL(findBaseURL());
  32.190 +            StringBuilder sb = new StringBuilder();
  32.191 +            sb.append("/dynamic?mimeType=").append(mimeType);
  32.192 +            for (int i = 0; i < parameters.length; i++) {
  32.193 +                sb.append("&param" + i).append("=").append(parameters[i]);
  32.194 +            }
  32.195 +            String mangle = content.replace("\n", "%0a")
  32.196 +                .replace("\"", "\\\"").replace(" ", "%20");
  32.197 +            sb.append("&content=").append(mangle);
  32.198 +
  32.199 +            URL query = new URL(baseURL, sb.toString());
  32.200 +            URLConnection c = query.openConnection();
  32.201 +            BufferedReader br = new BufferedReader(new InputStreamReader(c.getInputStream()));
  32.202 +            URI connectTo = new URI(br.readLine());
  32.203 +            return connectTo;
  32.204 +        } catch (IOException ex) {
  32.205 +            throw new IllegalStateException(ex);
  32.206 +        } catch (URISyntaxException ex) {
  32.207 +            throw new IllegalStateException(ex);
  32.208 +        }
  32.209 +    }
  32.210 +
  32.211 +    @Override
  32.212 +    public boolean canFailWebSocketTest() {
  32.213 +        try {
  32.214 +            Class.forName("java.util.function.Function");
  32.215 +            return false;
  32.216 +        } catch (ClassNotFoundException ex) {
  32.217 +            // running on JDK7, FX WebView WebSocket impl does not work
  32.218 +            return true;
  32.219 +        }
  32.220 +    }
  32.221 +
  32.222 +}
    33.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    33.2 +++ b/ko-osgi-test/src/test/java/org/netbeans/html/ko/osgi/test/DynamicHTTP.java	Mon Jan 06 09:44:07 2014 +0100
    33.3 @@ -0,0 +1,259 @@
    33.4 +/**
    33.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
    33.6 + *
    33.7 + * Copyright 2013-2013 Oracle and/or its affiliates. All rights reserved.
    33.8 + *
    33.9 + * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
   33.10 + * Other names may be trademarks of their respective owners.
   33.11 + *
   33.12 + * The contents of this file are subject to the terms of either the GNU
   33.13 + * General Public License Version 2 only ("GPL") or the Common
   33.14 + * Development and Distribution License("CDDL") (collectively, the
   33.15 + * "License"). You may not use this file except in compliance with the
   33.16 + * License. You can obtain a copy of the License at
   33.17 + * http://www.netbeans.org/cddl-gplv2.html
   33.18 + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
   33.19 + * specific language governing permissions and limitations under the
   33.20 + * License.  When distributing the software, include this License Header
   33.21 + * Notice in each file and include the License file at
   33.22 + * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
   33.23 + * particular file as subject to the "Classpath" exception as provided
   33.24 + * by Oracle in the GPL Version 2 section of the License file that
   33.25 + * accompanied this code. If applicable, add the following below the
   33.26 + * License Header, with the fields enclosed by brackets [] replaced by
   33.27 + * your own identifying information:
   33.28 + * "Portions Copyrighted [year] [name of copyright owner]"
   33.29 + *
   33.30 + * Contributor(s):
   33.31 + *
   33.32 + * The Original Software is NetBeans. The Initial Developer of the Original
   33.33 + * Software is Oracle. Portions Copyright 2013-2013 Oracle. All Rights Reserved.
   33.34 + *
   33.35 + * If you wish your version of this file to be governed by only the CDDL
   33.36 + * or only the GPL Version 2, indicate your decision by adding
   33.37 + * "[Contributor] elects to include this software in this distribution
   33.38 + * under the [CDDL or GPL Version 2] license." If you do not indicate a
   33.39 + * single choice of license, a recipient has the option to distribute
   33.40 + * your version of this file under either the CDDL, the GPL Version 2 or
   33.41 + * to extend the choice of license to its licensees as provided above.
   33.42 + * However, if you add GPL Version 2 code and therefore, elected the GPL
   33.43 + * Version 2 license, then the option applies only if the new code is
   33.44 + * made subject to such option by the copyright holder.
   33.45 + */
   33.46 +package org.netbeans.html.ko.osgi.test;
   33.47 +
   33.48 +import java.io.ByteArrayInputStream;
   33.49 +import java.io.ByteArrayOutputStream;
   33.50 +import java.io.IOException;
   33.51 +import java.io.InputStream;
   33.52 +import java.io.OutputStream;
   33.53 +import java.io.Reader;
   33.54 +import java.net.URI;
   33.55 +import java.net.URISyntaxException;
   33.56 +import java.util.ArrayList;
   33.57 +import java.util.List;
   33.58 +import java.util.logging.Level;
   33.59 +import java.util.logging.Logger;
   33.60 +import org.glassfish.grizzly.PortRange;
   33.61 +import org.glassfish.grizzly.http.server.HttpHandler;
   33.62 +import org.glassfish.grizzly.http.server.HttpServer;
   33.63 +import org.glassfish.grizzly.http.server.NetworkListener;
   33.64 +import org.glassfish.grizzly.http.server.Request;
   33.65 +import org.glassfish.grizzly.http.server.Response;
   33.66 +import org.glassfish.grizzly.http.server.ServerConfiguration;
   33.67 +import org.glassfish.grizzly.websockets.WebSocket;
   33.68 +import org.glassfish.grizzly.websockets.WebSocketAddOn;
   33.69 +import org.glassfish.grizzly.websockets.WebSocketApplication;
   33.70 +import org.glassfish.grizzly.websockets.WebSocketEngine;
   33.71 +
   33.72 +/**
   33.73 + *
   33.74 + * @author Jaroslav Tulach <jtulach@netbeans.org>
   33.75 + */
   33.76 +final class DynamicHTTP extends HttpHandler {
   33.77 +    private static final Logger LOG = Logger.getLogger(DynamicHTTP.class.getName());
   33.78 +    private static int resourcesCount;
   33.79 +    private static List<Resource> resources;
   33.80 +    private static ServerConfiguration conf;
   33.81 +    private static HttpServer server;
   33.82 +    
   33.83 +    private DynamicHTTP() {
   33.84 +    }
   33.85 +    
   33.86 +    static URI initServer() throws Exception {
   33.87 +        server = HttpServer.createSimpleServer(null, new PortRange(8080, 65535));
   33.88 +        final WebSocketAddOn addon = new WebSocketAddOn();
   33.89 +        for (NetworkListener listener : server.getListeners()) {
   33.90 +            listener.registerAddOn(addon);
   33.91 +        }        
   33.92 +        resources = new ArrayList<Resource>();
   33.93 +
   33.94 +        conf = server.getServerConfiguration();
   33.95 +        final DynamicHTTP dh = new DynamicHTTP();
   33.96 +
   33.97 +        conf.addHttpHandler(dh, "/");
   33.98 +        
   33.99 +        server.start();
  33.100 +
  33.101 +        return pageURL("http", server, "/test.html");
  33.102 +    }
  33.103 +    
  33.104 +    @Override
  33.105 +    public void service(Request request, Response response) throws Exception {
  33.106 +        if ("/test.html".equals(request.getRequestURI())) {
  33.107 +            response.setContentType("text/html");
  33.108 +            final InputStream is = DynamicHTTP.class.getResourceAsStream("test.html");
  33.109 +            copyStream(is, response.getOutputStream(), null);
  33.110 +            return;
  33.111 +        }
  33.112 +        if ("/dynamic".equals(request.getRequestURI())) {
  33.113 +            String mimeType = request.getParameter("mimeType");
  33.114 +            List<String> params = new ArrayList<String>();
  33.115 +            boolean webSocket = false;
  33.116 +            for (int i = 0;; i++) {
  33.117 +                String p = request.getParameter("param" + i);
  33.118 +                if (p == null) {
  33.119 +                    break;
  33.120 +                }
  33.121 +                if ("protocol:ws".equals(p)) {
  33.122 +                    webSocket = true;
  33.123 +                    continue;
  33.124 +                }
  33.125 +                params.add(p);
  33.126 +            }
  33.127 +            final String cnt = request.getParameter("content");
  33.128 +            String mangle = cnt.replace("%20", " ").replace("%0A", "\n");
  33.129 +            ByteArrayInputStream is = new ByteArrayInputStream(mangle.getBytes("UTF-8"));
  33.130 +            URI url;
  33.131 +            final Resource res = new Resource(is, mimeType, "/dynamic/res" + ++resourcesCount, params.toArray(new String[params.size()]));
  33.132 +            if (webSocket) {
  33.133 +                url = registerWebSocket(res);
  33.134 +            } else {
  33.135 +                url = registerResource(res);
  33.136 +            }
  33.137 +            response.getWriter().write(url.toString());
  33.138 +            response.getWriter().write("\n");
  33.139 +            return;
  33.140 +        }
  33.141 +
  33.142 +        for (Resource r : resources) {
  33.143 +            if (r.httpPath.equals(request.getRequestURI())) {
  33.144 +                response.setContentType(r.httpType);
  33.145 +                r.httpContent.reset();
  33.146 +                String[] params = null;
  33.147 +                if (r.parameters.length != 0) {
  33.148 +                    params = new String[r.parameters.length];
  33.149 +                    for (int i = 0; i < r.parameters.length; i++) {
  33.150 +                        params[i] = request.getParameter(r.parameters[i]);
  33.151 +                        if (params[i] == null) {
  33.152 +                            if ("http.method".equals(r.parameters[i])) {
  33.153 +                                params[i] = request.getMethod().toString();
  33.154 +                            } else if ("http.requestBody".equals(r.parameters[i])) {
  33.155 +                                Reader rdr = request.getReader();
  33.156 +                                StringBuilder sb = new StringBuilder();
  33.157 +                                for (;;) {
  33.158 +                                    int ch = rdr.read();
  33.159 +                                    if (ch == -1) {
  33.160 +                                        break;
  33.161 +                                    }
  33.162 +                                    sb.append((char) ch);
  33.163 +                                }
  33.164 +                                params[i] = sb.toString();
  33.165 +                            }
  33.166 +                        }
  33.167 +                        if (params[i] == null) {
  33.168 +                            params[i] = "null";
  33.169 +                        }
  33.170 +                    }
  33.171 +                }
  33.172 +
  33.173 +                copyStream(r.httpContent, response.getOutputStream(), null, params);
  33.174 +            }
  33.175 +        }
  33.176 +    }
  33.177 +    
  33.178 +    private URI registerWebSocket(Resource r) {
  33.179 +        WebSocketEngine.getEngine().register("", r.httpPath, new WS(r));
  33.180 +        return pageURL("ws", server, r.httpPath);
  33.181 +    }
  33.182 +
  33.183 +    private URI registerResource(Resource r) {
  33.184 +        if (!resources.contains(r)) {
  33.185 +            resources.add(r);
  33.186 +            conf.addHttpHandler(this, r.httpPath);
  33.187 +        }
  33.188 +        return pageURL("http", server, r.httpPath);
  33.189 +    }
  33.190 +    
  33.191 +    private static URI pageURL(String proto, HttpServer server, final String page) {
  33.192 +        NetworkListener listener = server.getListeners().iterator().next();
  33.193 +        int port = listener.getPort();
  33.194 +        try {
  33.195 +            return new URI(proto + "://localhost:" + port + page);
  33.196 +        } catch (URISyntaxException ex) {
  33.197 +            throw new IllegalStateException(ex);
  33.198 +        }
  33.199 +    }
  33.200 +    
  33.201 +    static final class Resource {
  33.202 +
  33.203 +        final InputStream httpContent;
  33.204 +        final String httpType;
  33.205 +        final String httpPath;
  33.206 +        final String[] parameters;
  33.207 +
  33.208 +        Resource(InputStream httpContent, String httpType, String httpPath,
  33.209 +            String[] parameters) {
  33.210 +            httpContent.mark(Integer.MAX_VALUE);
  33.211 +            this.httpContent = httpContent;
  33.212 +            this.httpType = httpType;
  33.213 +            this.httpPath = httpPath;
  33.214 +            this.parameters = parameters;
  33.215 +        }
  33.216 +    }
  33.217 +
  33.218 +    static void copyStream(InputStream is, OutputStream os, String baseURL, String... params) throws IOException {
  33.219 +        for (;;) {
  33.220 +            int ch = is.read();
  33.221 +            if (ch == -1) {
  33.222 +                break;
  33.223 +            }
  33.224 +            if (ch == '$' && params.length > 0) {
  33.225 +                int cnt = is.read() - '0';
  33.226 +                if (baseURL != null && cnt == 'U' - '0') {
  33.227 +                    os.write(baseURL.getBytes("UTF-8"));
  33.228 +                } else {
  33.229 +                    if (cnt >= 0 && cnt < params.length) {
  33.230 +                        os.write(params[cnt].getBytes("UTF-8"));
  33.231 +                    } else {
  33.232 +                        os.write('$');
  33.233 +                        os.write(cnt + '0');
  33.234 +                    }
  33.235 +                }
  33.236 +            } else {
  33.237 +                os.write(ch);
  33.238 +            }
  33.239 +        }
  33.240 +    }
  33.241 +    
  33.242 +    private static class WS extends WebSocketApplication {
  33.243 +        private final Resource r;
  33.244 +
  33.245 +        private WS(Resource r) {
  33.246 +            this.r = r;
  33.247 +        }
  33.248 +
  33.249 +        @Override
  33.250 +        public void onMessage(WebSocket socket, String text) {
  33.251 +            try {
  33.252 +                r.httpContent.reset();
  33.253 +                ByteArrayOutputStream out = new ByteArrayOutputStream();
  33.254 +                copyStream(r.httpContent, out, null, text);
  33.255 +                String s = new String(out.toByteArray(), "UTF-8");
  33.256 +                socket.send(s);
  33.257 +            } catch (IOException ex) {
  33.258 +                LOG.log(Level.WARNING, "Error processing message " + text, ex);
  33.259 +            }
  33.260 +        }
  33.261 +    }
  33.262 +}
    34.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    34.2 +++ b/ko-osgi-test/src/test/java/org/netbeans/html/ko/osgi/test/KOFx.java	Mon Jan 06 09:44:07 2014 +0100
    34.3 @@ -0,0 +1,118 @@
    34.4 +/**
    34.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
    34.6 + *
    34.7 + * Copyright 2013-2013 Oracle and/or its affiliates. All rights reserved.
    34.8 + *
    34.9 + * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
   34.10 + * Other names may be trademarks of their respective owners.
   34.11 + *
   34.12 + * The contents of this file are subject to the terms of either the GNU
   34.13 + * General Public License Version 2 only ("GPL") or the Common
   34.14 + * Development and Distribution License("CDDL") (collectively, the
   34.15 + * "License"). You may not use this file except in compliance with the
   34.16 + * License. You can obtain a copy of the License at
   34.17 + * http://www.netbeans.org/cddl-gplv2.html
   34.18 + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
   34.19 + * specific language governing permissions and limitations under the
   34.20 + * License.  When distributing the software, include this License Header
   34.21 + * Notice in each file and include the License file at
   34.22 + * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
   34.23 + * particular file as subject to the "Classpath" exception as provided
   34.24 + * by Oracle in the GPL Version 2 section of the License file that
   34.25 + * accompanied this code. If applicable, add the following below the
   34.26 + * License Header, with the fields enclosed by brackets [] replaced by
   34.27 + * your own identifying information:
   34.28 + * "Portions Copyrighted [year] [name of copyright owner]"
   34.29 + *
   34.30 + * Contributor(s):
   34.31 + *
   34.32 + * The Original Software is NetBeans. The Initial Developer of the Original
   34.33 + * Software is Oracle. Portions Copyright 2013-2013 Oracle. All Rights Reserved.
   34.34 + *
   34.35 + * If you wish your version of this file to be governed by only the CDDL
   34.36 + * or only the GPL Version 2, indicate your decision by adding
   34.37 + * "[Contributor] elects to include this software in this distribution
   34.38 + * under the [CDDL or GPL Version 2] license." If you do not indicate a
   34.39 + * single choice of license, a recipient has the option to distribute
   34.40 + * your version of this file under either the CDDL, the GPL Version 2 or
   34.41 + * to extend the choice of license to its licensees as provided above.
   34.42 + * However, if you add GPL Version 2 code and therefore, elected the GPL
   34.43 + * Version 2 license, then the option applies only if the new code is
   34.44 + * made subject to such option by the copyright holder.
   34.45 + */
   34.46 +package org.netbeans.html.ko.osgi.test;
   34.47 +
   34.48 +import java.io.Closeable;
   34.49 +import java.lang.reflect.InvocationTargetException;
   34.50 +import java.lang.reflect.Method;
   34.51 +import javafx.application.Platform;
   34.52 +import org.apidesign.html.boot.spi.Fn;
   34.53 +import org.testng.ITest;
   34.54 +import org.testng.annotations.Test;
   34.55 +
   34.56 +/**
   34.57 + *
   34.58 + * @author Jaroslav Tulach <jtulach@netbeans.org>
   34.59 + */
   34.60 +public final class KOFx implements ITest, Runnable {
   34.61 +    private final Object p;
   34.62 +    private final Method m;
   34.63 +    private Object result;
   34.64 +    private Object inst;
   34.65 +    private int count;
   34.66 +
   34.67 +    KOFx(Object p, Method m) {
   34.68 +        this.p = p;
   34.69 +        this.m = m;
   34.70 +    }
   34.71 +
   34.72 +    @Override
   34.73 +    public String getTestName() {
   34.74 +        return m.getName();
   34.75 +    }
   34.76 +
   34.77 +    @Test
   34.78 +    public synchronized void executeTest() throws Exception {
   34.79 +        if (result == null) {
   34.80 +            Platform.runLater(this);
   34.81 +            wait();
   34.82 +        }
   34.83 +        if (result instanceof Exception) {
   34.84 +            throw (Exception)result;
   34.85 +        }
   34.86 +        if (result instanceof Error) {
   34.87 +            throw (Error)result;
   34.88 +        }
   34.89 +    }
   34.90 +
   34.91 +    @Override
   34.92 +    public synchronized void run() {
   34.93 +        boolean notify = true;
   34.94 +        try (Closeable a = KnockoutEquinoxIT.activateInOSGi(p)) {
   34.95 +            if (inst == null) {
   34.96 +                inst = m.getDeclaringClass().newInstance();
   34.97 +            }
   34.98 +            result = m.invoke(inst);
   34.99 +            if (result == null) {
  34.100 +                result = this;
  34.101 +            }
  34.102 +        } catch (InvocationTargetException ex) {
  34.103 +            Throwable r = ex.getTargetException();
  34.104 +            if (r instanceof InterruptedException) {
  34.105 +                if (count++ < 10000) {
  34.106 +                    notify = false;
  34.107 +                    Platform.runLater(this);
  34.108 +                    return;
  34.109 +                }
  34.110 +            }
  34.111 +            result = r;
  34.112 +        } catch (Exception ex) {
  34.113 +            result = ex;
  34.114 +        } finally {
  34.115 +            if (notify) {
  34.116 +                notifyAll();
  34.117 +            }
  34.118 +        }
  34.119 +    }
  34.120 +    
  34.121 +}
    35.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    35.2 +++ b/ko-osgi-test/src/test/java/org/netbeans/html/ko/osgi/test/KnockoutEquinoxIT.java	Mon Jan 06 09:44:07 2014 +0100
    35.3 @@ -0,0 +1,243 @@
    35.4 +/**
    35.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
    35.6 + *
    35.7 + * Copyright 2013-2013 Oracle and/or its affiliates. All rights reserved.
    35.8 + *
    35.9 + * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
   35.10 + * Other names may be trademarks of their respective owners.
   35.11 + *
   35.12 + * The contents of this file are subject to the terms of either the GNU
   35.13 + * General Public License Version 2 only ("GPL") or the Common
   35.14 + * Development and Distribution License("CDDL") (collectively, the
   35.15 + * "License"). You may not use this file except in compliance with the
   35.16 + * License. You can obtain a copy of the License at
   35.17 + * http://www.netbeans.org/cddl-gplv2.html
   35.18 + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
   35.19 + * specific language governing permissions and limitations under the
   35.20 + * License.  When distributing the software, include this License Header
   35.21 + * Notice in each file and include the License file at
   35.22 + * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
   35.23 + * particular file as subject to the "Classpath" exception as provided
   35.24 + * by Oracle in the GPL Version 2 section of the License file that
   35.25 + * accompanied this code. If applicable, add the following below the
   35.26 + * License Header, with the fields enclosed by brackets [] replaced by
   35.27 + * your own identifying information:
   35.28 + * "Portions Copyrighted [year] [name of copyright owner]"
   35.29 + *
   35.30 + * Contributor(s):
   35.31 + *
   35.32 + * The Original Software is NetBeans. The Initial Developer of the Original
   35.33 + * Software is Oracle. Portions Copyright 2013-2013 Oracle. All Rights Reserved.
   35.34 + *
   35.35 + * If you wish your version of this file to be governed by only the CDDL
   35.36 + * or only the GPL Version 2, indicate your decision by adding
   35.37 + * "[Contributor] elects to include this software in this distribution
   35.38 + * under the [CDDL or GPL Version 2] license." If you do not indicate a
   35.39 + * single choice of license, a recipient has the option to distribute
   35.40 + * your version of this file under either the CDDL, the GPL Version 2 or
   35.41 + * to extend the choice of license to its licensees as provided above.
   35.42 + * However, if you add GPL Version 2 code and therefore, elected the GPL
   35.43 + * Version 2 license, then the option applies only if the new code is
   35.44 + * made subject to such option by the copyright holder.
   35.45 + */
   35.46 +package org.netbeans.html.ko.osgi.test;
   35.47 +
   35.48 +import java.io.Closeable;
   35.49 +import java.io.File;
   35.50 +import java.io.IOException;
   35.51 +import java.lang.annotation.Annotation;
   35.52 +import java.lang.reflect.Method;
   35.53 +import java.net.URI;
   35.54 +import java.util.ArrayList;
   35.55 +import java.util.HashMap;
   35.56 +import java.util.List;
   35.57 +import java.util.Map;
   35.58 +import java.util.ServiceLoader;
   35.59 +import java.util.concurrent.Callable;
   35.60 +import java.util.jar.JarFile;
   35.61 +import java.util.logging.Level;
   35.62 +import java.util.logging.Logger;
   35.63 +import org.apidesign.html.boot.spi.Fn;
   35.64 +import org.apidesign.html.json.tck.KOTest;
   35.65 +import org.apidesign.html.json.tck.KnockoutTCK;
   35.66 +import org.osgi.framework.Bundle;
   35.67 +import org.osgi.framework.BundleException;
   35.68 +import org.osgi.framework.Constants;
   35.69 +import org.osgi.framework.launch.Framework;
   35.70 +import org.osgi.framework.launch.FrameworkFactory;
   35.71 +import static org.testng.Assert.assertNotNull;
   35.72 +import static org.testng.Assert.fail;
   35.73 +import org.testng.annotations.AfterClass;
   35.74 +import org.testng.annotations.Factory;
   35.75 +
   35.76 +/**
   35.77 + *
   35.78 + * @author Jaroslav Tulach <jtulach@netbeans.org>
   35.79 + */
   35.80 +public class KnockoutEquinoxIT {
   35.81 +    private static final Logger LOG = Logger.getLogger(KnockoutEquinoxIT.class.getName());
   35.82 +    private static Framework framework;
   35.83 +    private static File dir;
   35.84 +    static Framework framework() throws Exception {
   35.85 +        if (framework != null) {
   35.86 +            return framework;
   35.87 +        }
   35.88 +        for (FrameworkFactory ff : ServiceLoader.load(FrameworkFactory.class)) {
   35.89 +            
   35.90 +            String basedir = System.getProperty("basedir");
   35.91 +            assertNotNull("basedir preperty provided", basedir);
   35.92 +            File target = new File(basedir, "target");
   35.93 +            dir = new File(target, "osgi");
   35.94 +            dir.mkdirs();
   35.95 +            
   35.96 +            Map<String,String> config = new HashMap<>();
   35.97 +            config.put(Constants.FRAMEWORK_STORAGE, dir.getPath());
   35.98 +            config.put(Constants.FRAMEWORK_STORAGE_CLEAN, "true");
   35.99 +            config.put(Constants.FRAMEWORK_SYSTEMPACKAGES_EXTRA, "sun.misc,"
  35.100 +                + "javafx.application,"
  35.101 +                + "javafx.beans.property,"
  35.102 +                + "javafx.beans.value,"
  35.103 +                + "javafx.collections,"
  35.104 +                + "javafx.concurrent,"
  35.105 +                + "javafx.event,"
  35.106 +                + "javafx.geometry,"
  35.107 +                + "javafx.scene,"
  35.108 +                + "javafx.scene.control,"
  35.109 +                + "javafx.scene.layout,"
  35.110 +                + "javafx.scene.text,"
  35.111 +                + "javafx.scene.web,"
  35.112 +                + "javafx.stage,"
  35.113 +                + "javafx.util,"
  35.114 +                + "netscape.javascript"
  35.115 +            );
  35.116 +            config.put("osgi.hook.configurators.include", "org.netbeans.html.equinox.agentclass.AgentHook");
  35.117 +            framework = ff.newFramework(config);
  35.118 +            framework.init();
  35.119 +            loadClassPathBundles(framework);
  35.120 +            framework.start();
  35.121 +            for (Bundle b : framework.getBundleContext().getBundles()) {
  35.122 +                try {
  35.123 +                    if (b.getSymbolicName().contains("equinox-agentclass-hook")) {
  35.124 +                        continue;
  35.125 +                    }
  35.126 +                    if (b.getSymbolicName().contains("grizzly.websockets-server")) {
  35.127 +                        continue;
  35.128 +                    }
  35.129 +                    b.start();
  35.130 +                    LOG.log(Level.INFO, "Started {0}", b.getSymbolicName());
  35.131 +                } catch (BundleException ex) {
  35.132 +                    LOG.log(Level.WARNING, "Cannot start bundle " + b.getSymbolicName(), ex);
  35.133 +                }
  35.134 +            }
  35.135 +            return framework;
  35.136 +        }
  35.137 +        fail("No OSGi framework in the path");
  35.138 +        return null;
  35.139 +    }
  35.140 +    
  35.141 +    @AfterClass public static void cleanUp() throws Exception {
  35.142 +        if (framework != null) framework.stop();
  35.143 +        clearUpDir(dir);
  35.144 +    }
  35.145 +    private static void clearUpDir(File dir) {
  35.146 +        if (dir.isDirectory()) {
  35.147 +            for (File f : dir.listFiles()) {
  35.148 +                clearUpDir(f);
  35.149 +            }
  35.150 +        }
  35.151 +        dir.delete();
  35.152 +    }
  35.153 +    
  35.154 +    
  35.155 +
  35.156 +    private static void loadClassPathBundles(Framework f) throws IOException, BundleException {
  35.157 +        for (String jar : System.getProperty("java.class.path").split(File.pathSeparator)) {
  35.158 +            File file = new File(jar);
  35.159 +            if (!file.isFile()) {
  35.160 +                LOG.info("Not loading " + file);
  35.161 +                continue;
  35.162 +            }
  35.163 +            JarFile jf = new JarFile(file);
  35.164 +            final String name = jf.getManifest().getMainAttributes().getValue("Bundle-SymbolicName");
  35.165 +            jf.close();
  35.166 +            if (name != null) {
  35.167 +                if (name.contains("org.eclipse.osgi")) {
  35.168 +                    continue;
  35.169 +                }
  35.170 +                if (name.contains("testng")) {
  35.171 +                    continue;
  35.172 +                }
  35.173 +                final String path = "reference:" + file.toURI().toString();
  35.174 +                try {
  35.175 +                    Bundle b = f.getBundleContext().installBundle(path);
  35.176 +                } catch (BundleException ex) {
  35.177 +                    LOG.log(Level.WARNING, "Cannot install " + file, ex);
  35.178 +                }
  35.179 +            }
  35.180 +        }
  35.181 +    }
  35.182 +    
  35.183 +    private static Class<?> loadOSGiClass(Class<?> c) throws Exception {
  35.184 +        return KnockoutEquinoxTCKImpl.loadOSGiClass(c.getName(), KnockoutEquinoxIT.framework().getBundleContext());
  35.185 +    }
  35.186 +    
  35.187 +    private static Class<?> browserClass;
  35.188 +    private static Object browserContext;
  35.189 +    
  35.190 +    @Factory public static Object[] compatibilityTests() throws Exception {
  35.191 +        Class<?> tck = loadOSGiClass(KnockoutTCK.class);
  35.192 +        Class<?> peer = loadOSGiClass(KnockoutEquinoxTCKImpl.class);
  35.193 +        // initialize the TCK
  35.194 +        Callable<Class[]> inst = (Callable<Class[]>) peer.newInstance();
  35.195 +        
  35.196 +        Class[] arr = inst.call();
  35.197 +        for (int i = 0; i < arr.length; i++) {
  35.198 +            if (arr[i].getClassLoader() == ClassLoader.getSystemClassLoader()) {
  35.199 +                fail("Should be an OSGi class: " + arr[i]);
  35.200 +            }
  35.201 +        }
  35.202 +        
  35.203 +        URI uri = DynamicHTTP.initServer();
  35.204 +
  35.205 +        Method start = peer.getMethod("start", URI.class);
  35.206 +        start.invoke(null, uri);
  35.207 +        
  35.208 +        ClassLoader l = getClassLoader();
  35.209 +        List<Object> res = new ArrayList<Object>();
  35.210 +        for (int i = 0; i < arr.length; i++) {
  35.211 +            seekKOTests(arr[i], res);
  35.212 +        }
  35.213 +        return res.toArray();
  35.214 +    }
  35.215 +
  35.216 +    private static void seekKOTests(Class<?> c, List<Object> res) throws SecurityException, ClassNotFoundException {
  35.217 +        Class<? extends Annotation> koTest =
  35.218 +            c.getClassLoader().loadClass(KOTest.class.getName()).
  35.219 +            asSubclass(Annotation.class);
  35.220 +        for (Method m : c.getMethods()) {
  35.221 +            if (m.getAnnotation(koTest) != null) {
  35.222 +                res.add(new KOFx(browserContext, m));
  35.223 +            }
  35.224 +        }
  35.225 +    }
  35.226 +
  35.227 +    static synchronized ClassLoader getClassLoader() throws InterruptedException {
  35.228 +        while (browserClass == null) {
  35.229 +            KnockoutEquinoxIT.class.wait();
  35.230 +        }
  35.231 +        return browserClass.getClassLoader();
  35.232 +    }
  35.233 +    
  35.234 +    public static synchronized void initialized(Class<?> browserCls, Object presenter) throws Exception {
  35.235 +        browserClass = browserCls;
  35.236 +        browserContext = presenter;
  35.237 +        KnockoutEquinoxIT.class.notifyAll();
  35.238 +    }
  35.239 +
  35.240 +    static Closeable activateInOSGi(Object presenter) throws Exception {
  35.241 +        Class<?> presenterClass = loadOSGiClass(Fn.Presenter.class);
  35.242 +        Class<?> fnClass = loadOSGiClass(Fn.class);
  35.243 +        Method m = fnClass.getMethod("activate", presenterClass);
  35.244 +        return (Closeable) m.invoke(null, presenter);
  35.245 +    }
  35.246 +}
    36.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    36.2 +++ b/ko-osgi-test/src/test/resources/org/netbeans/html/ko/osgi/test/test.html	Mon Jan 06 09:44:07 2014 +0100
    36.3 @@ -0,0 +1,56 @@
    36.4 +<!--
    36.5 +
    36.6 +    DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
    36.7 +
    36.8 +    Copyright 2013-2013 Oracle and/or its affiliates. All rights reserved.
    36.9 +
   36.10 +    Oracle and Java are registered trademarks of Oracle and/or its affiliates.
   36.11 +    Other names may be trademarks of their respective owners.
   36.12 +
   36.13 +    The contents of this file are subject to the terms of either the GNU
   36.14 +    General Public License Version 2 only ("GPL") or the Common
   36.15 +    Development and Distribution License("CDDL") (collectively, the
   36.16 +    "License"). You may not use this file except in compliance with the
   36.17 +    License. You can obtain a copy of the License at
   36.18 +    http://www.netbeans.org/cddl-gplv2.html
   36.19 +    or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
   36.20 +    specific language governing permissions and limitations under the
   36.21 +    License.  When distributing the software, include this License Header
   36.22 +    Notice in each file and include the License file at
   36.23 +    nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
   36.24 +    particular file as subject to the "Classpath" exception as provided
   36.25 +    by Oracle in the GPL Version 2 section of the License file that
   36.26 +    accompanied this code. If applicable, add the following below the
   36.27 +    License Header, with the fields enclosed by brackets [] replaced by
   36.28 +    your own identifying information:
   36.29 +    "Portions Copyrighted [year] [name of copyright owner]"
   36.30 +
   36.31 +    Contributor(s):
   36.32 +
   36.33 +    The Original Software is NetBeans. The Initial Developer of the Original
   36.34 +    Software is Oracle. Portions Copyright 2013-2013 Oracle. All Rights Reserved.
   36.35 +
   36.36 +    If you wish your version of this file to be governed by only the CDDL
   36.37 +    or only the GPL Version 2, indicate your decision by adding
   36.38 +    "[Contributor] elects to include this software in this distribution
   36.39 +    under the [CDDL or GPL Version 2] license." If you do not indicate a
   36.40 +    single choice of license, a recipient has the option to distribute
   36.41 +    your version of this file under either the CDDL, the GPL Version 2 or
   36.42 +    to extend the choice of license to its licensees as provided above.
   36.43 +    However, if you add GPL Version 2 code and therefore, elected the GPL
   36.44 +    Version 2 license, then the option applies only if the new code is
   36.45 +    made subject to such option by the copyright holder.
   36.46 +
   36.47 +-->
   36.48 +<!DOCTYPE html>
   36.49 +<html>
   36.50 +    <head>
   36.51 +        <title>Knockout.fx Execution Harness</title>
   36.52 +        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
   36.53 +        <meta name="viewport" content="width=device-width">
   36.54 +    </head>
   36.55 +    <body>
   36.56 +        <h1>Knockout.fx Execution Harness</h1>
   36.57 +    </body>
   36.58 +    <script></script>
   36.59 +</html>
    37.1 --- a/pom.xml	Mon Dec 16 18:08:40 2013 +0100
    37.2 +++ b/pom.xml	Mon Jan 06 09:44:07 2014 +0100
    37.3 @@ -30,6 +30,8 @@
    37.4      <module>boot-fx</module>
    37.5      <module>geo</module>
    37.6      <module>ko-ws-tyrus</module>
    37.7 +    <module>ko-osgi-test</module>
    37.8 +    <module>equinox-agentclass-hook</module>
    37.9    </modules>
   37.10    <licenses>
   37.11        <license>