/* * Copyright (C) 2014 Servoy BV * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.servoy.j2db.server.ngclient.property; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import java.io.InputStream; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.json.JSONException; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.mozilla.javascript.NativeArray; import org.mozilla.javascript.NativeObject; import org.mozilla.javascript.Scriptable; import org.mozilla.javascript.ScriptableObject; import org.sablo.InMemPackageReader; import org.sablo.WebComponent; import org.sablo.specification.PropertyDescription; import org.sablo.specification.NGPackage.IPackageReader; import org.sablo.specification.WebComponentSpecProvider; import org.sablo.specification.WebObjectSpecification.PushToServerEnum; import org.sablo.specification.property.BrowserConverterContext; import org.sablo.specification.property.ChangeAwareList; import org.sablo.specification.property.ChangeAwareMap; import org.sablo.specification.property.CustomJSONArrayType; import org.sablo.specification.property.CustomJSONObjectType; import org.sablo.specification.property.CustomJSONPropertyType; import org.sablo.websocket.TypedData; import org.sablo.websocket.utils.JSONUtils; import com.servoy.j2db.server.ngclient.property.types.NGConversions; import com.servoy.j2db.server.ngclient.property.types.Types; /** * @author acostescu */ @SuppressWarnings("nls") public class CustomArrayPropertyRhinoTest { @Before public void setUp() throws Exception { Types.getTypesInstance().registerTypes(); InputStream is = getClass().getResourceAsStream("PropertyTests.manifest"); byte[] bytes = new byte[is.available()]; is.read(bytes); String manifest = new String(bytes); is.close(); is = getClass().getResourceAsStream("mycomponent.spec"); bytes = new byte[is.available()]; is.read(bytes); String comp = new String(bytes); is.close(); HashMap<String, String> components = new HashMap<>(); components.put("mycomponent.spec", comp); WebComponentSpecProvider.init(new IPackageReader[] { new InMemPackageReader(manifest, components) }); } @After public void tearDown() { WebComponentSpecProvider.disposeInstance(); } @Test public void testCustomTypeJavaBasedRhinoChanges() throws JSONException { WebComponent component = new WebComponent("mycomponent", "testComponentName"); BrowserConverterContext allowingBrowserConverterContext = new BrowserConverterContext(component, PushToServerEnum.allow); PropertyDescription objectTPD = component.getSpecification().getProperty("objectT"); PropertyDescription arrayTPD = component.getSpecification().getProperty("arrayT"); PropertyDescription activePD = objectTPD.getProperty("active"); // just some initial checks and setting a java value assertNull(component.getProperty("arrayT")); List<Map<String, Object>> javaV = new ArrayList<>(); HashMap<String, Object> hm = new HashMap<String, Object>(); javaV.add(hm); hm.put("text", "Just some text"); component.setProperty("arrayT", javaV); // set should turn it into a change-aware list Object wrapped = component.getProperty("arrayT"); assertTrue(wrapped instanceof ChangeAwareList< ? , ? >); ChangeAwareList<Map<String, Object>, Map<String, Object>> cal = (ChangeAwareList)wrapped; assertEquals("Just some text", cal.get(0).get("text")); assertTrue(cal.get(0) instanceof ChangeAwareMap< ? , ? >); ChangeAwareMap cam = ((ChangeAwareMap< ? , ? >)cal.get(0)); // TODO I guess this kind of reference changes should be treated in the BaseWebObject directly when we have separate methods for changesToJSON and fullToJSON // so for now the change aware things do not report as being changed... assertTrue(!cal.mustSendAll()); assertTrue(!cam.mustSendAll()); // still the component has to see them as changed! TypedData<Map<String, Object>> changes = component.getAndClearChanges(); assertTrue(changes.content.get("arrayT") != null); assertTrue(changes.contentType.getProperty("arrayT").getType() instanceof CustomJSONArrayType); Object arrayCh = changes.content.get("arrayT"); assertTrue(arrayCh != null); assertTrue(changes.contentType.getProperty("arrayT").getType() instanceof CustomJSONArrayType); Object mapCh = ((ChangeAwareList)arrayCh).get(0); assertTrue(mapCh != null); assertTrue(((CustomJSONPropertyType< ? >)changes.contentType.getProperty("arrayT").getType()).getCustomJSONTypeDefinition().getType() instanceof CustomJSONObjectType); JSONUtils.writeDataWithConversions(changes.content, changes.contentType, allowingBrowserConverterContext); // ok now that we called component.getChanges() no changes should be present any more assertTrue(!cal.mustSendAll()); assertTrue(!cam.mustSendAll()); assertEquals(0, component.getAndClearChanges().content.size()); assertEquals(0, cal.getChangedIndexes().size()); assertEquals(0, cam.getChangedKeys().size()); // check changing java => change reflected in Rhino ScriptableObject topLevel = new ScriptableObject() { @Override public String getClassName() { return "test_top_level_scope"; } }; Scriptable rhinoVal = (Scriptable)NGConversions.INSTANCE.convertSabloComponentToRhinoValue(component.getProperty("arrayT"), arrayTPD, component, topLevel); assertEquals("Just some text", ((Scriptable)rhinoVal.get(0, rhinoVal)).get("text", rhinoVal)); cam.put("text", "Just some text 2"); assertEquals(1, cam.getChangedKeys().size()); assertEquals("text", cam.getChangedKeys().iterator().next()); assertEquals(1, cal.getChangedIndexes().size()); assertEquals(Integer.valueOf(0), cal.getChangedIndexes().iterator().next()); assertTrue(!cal.mustSendAll()); assertTrue(!cam.mustSendAll()); assertEquals("Just some text 2", ((Scriptable)rhinoVal.get(0, rhinoVal)).get("text", rhinoVal)); cam.put("active", new ArrayList()); assertTrue(!cal.mustSendAll()); assertTrue(!cam.mustSendAll()); assertEquals(2, cam.getChangedKeys().size()); assertTrue(cam.getChangedKeys().contains("text")); assertTrue(cam.getChangedKeys().contains("active")); cam.remove("active"); assertTrue(!cal.mustSendAll()); assertTrue(cam.mustSendAll()); cal.add(new HashMap<String, Object>()); ChangeAwareMap cam1 = ((ChangeAwareMap< ? , ? >)cal.get(1)); assertTrue(cal.mustSendAll()); assertTrue(cam.mustSendAll()); // ok clear changes changes = component.getAndClearChanges(); JSONUtils.writeDataWithConversions(changes.content, changes.contentType, allowingBrowserConverterContext); assertEquals(1, changes.content.size()); assertEquals(0, component.getAndClearChanges().content.size()); assertTrue(!cal.mustSendAll()); assertTrue(!cam.mustSendAll()); assertEquals(0, cal.getChangedIndexes().size()); assertEquals(0, cam.getChangedKeys().size()); // assign some native values NativeObject oneO = new NativeObject(); // oneO.setPrototype(ScriptableObject.getObjectPrototype(topLevel)); NativeArray activeA1 = new NativeArray(0); // activeA1.setPrototype(ScriptableObject.getArrayPrototype(topLevel)); NativeObject activeA1Obj = new NativeObject(); NativeObject activeA2Obj = new NativeObject(); // activeA1Obj.setPrototype(ScriptableObject.getObjectPrototype(topLevel)); rhinoVal.put(0, rhinoVal, oneO); assertTrue(!cal.mustSendAll()); assertTrue(cal.getChangedIndexes().size() == 1); cam = ((ChangeAwareMap< ? , ? >)cal.get(0)); activeA1Obj.put("field", activeA1Obj, 11); activeA1.put(0, activeA1, activeA1Obj); oneO.put("active", oneO, activeA1); assertEquals(11, ((Map)((List)((Map)cal.get(0)).get("active")).get(0)).get("field")); assertTrue(cam.mustSendAll()); assertTrue(!cal.mustSendAll()); assertEquals(1, cal.getChangedIndexes().size()); // now change the native values using initial ref to see if it changed in java activeA1Obj.put("field", activeA1Obj, 98); assertEquals(98, ((Map)((List)((Map)cal.get(0)).get("active")).get(0)).get("field")); activeA1.put(1, activeA1, activeA2Obj); activeA2Obj.put("field", activeA2Obj, 45); assertEquals(45, ((Map)((List)((Map)cal.get(0)).get("active")).get(1)).get("field")); changes = component.getAndClearChanges(); assertEquals( "{\"arrayT\":{\"vEr\":3,\"u\":[{\"i\":0,\"v\":{\"vEr\":5,\"v\":{\"active\":{\"vEr\":2,\"v\":[{\"vEr\":2,\"v\":{\"field\":98}},{\"vEr\":2,\"v\":{\"field\":45}}],\"conversions\":{\"1\":\"JSON_obj\",\"0\":\"JSON_obj\"}}},\"conversions\":{\"active\":\"JSON_arr\"}}}],\"conversions\":{\"0\":{\"v\":\"JSON_obj\"}}},\"conversions\":{\"arrayT\":\"JSON_arr\"}}", JSONUtils.writeChangesWithConversions(changes.content, changes.contentType, allowingBrowserConverterContext)); // now simulate another request cycle that makes some change to the property from javascript rhinoVal = (Scriptable)NGConversions.INSTANCE.convertSabloComponentToRhinoValue(component.getProperty("arrayT"), arrayTPD, component, topLevel); Scriptable v = ((Scriptable)rhinoVal.get(0, rhinoVal)); v = (Scriptable)v.get("active", v); v = (Scriptable)v.get(1, v); assertEquals(45, v.get("field", v)); v.put("field", v, 56); assertEquals(56, ((Map)((List)((Map)cal.get(0)).get("active")).get(1)).get("field")); assertTrue(!cam.mustSendAll()); assertTrue(!cal.mustSendAll()); assertEquals(1, cal.getChangedIndexes().size()); assertEquals(1, cam.getChangedKeys().size()); } }