| 1 | /** | 
| 2 | *    Copyright 2005-2011 Steve McDuff d-duff@users.sourceforge.net | 
| 3 | * | 
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); | 
| 5 | * you may not use this file except in compliance with the License. | 
| 6 | * You may obtain a copy of the License at | 
| 7 | * | 
| 8 | * http://www.apache.org/licenses/LICENSE-2.0 | 
| 9 | * | 
| 10 | * Unless required by applicable law or agreed to in writing, software | 
| 11 | * distributed under the License is distributed on an "AS IS" BASIS, | 
| 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
| 13 | * See the License for the specific language governing permissions and | 
| 14 | * limitations under the License. | 
| 15 | */ | 
| 16 | package org.deduced.viewer.web.serializer; | 
| 17 |  | 
| 18 | import java.io.Serializable; | 
| 19 | import java.util.ArrayList; | 
| 20 | import java.util.HashSet; | 
| 21 | import java.util.IdentityHashMap; | 
| 22 | import java.util.List; | 
| 23 | import java.util.Map; | 
| 24 | import java.util.Set; | 
| 25 |  | 
| 26 | import org.deduced.ChangeType; | 
| 27 | import org.deduced.DeducedUtilities; | 
| 28 | import org.deduced.EnumerationPropertyCollection; | 
| 29 | import org.deduced.PropertyChangeEvent; | 
| 30 | import org.deduced.PropertyCollection; | 
| 31 | import org.deduced.PropertyCollectionType; | 
| 32 | import org.deduced.PropertyList; | 
| 33 | import org.deduced.implementation.OrderingPropertyCollectionTypeImplementation; | 
| 34 | import org.deduced.implementation.PropertyInstanceImplementation; | 
| 35 | import org.deduced.utilities.AssertUtilities; | 
| 36 | import org.deduced.utilities.IdentityReferenceMap; | 
| 37 | import org.deduced.viewer.model.Button; | 
| 38 | import org.deduced.viewer.model.CheckBox; | 
| 39 | import org.deduced.viewer.model.Component; | 
| 40 | import org.deduced.viewer.model.FlowPanel; | 
| 41 | import org.deduced.viewer.model.Image; | 
| 42 | import org.deduced.viewer.model.Label; | 
| 43 | import org.deduced.viewer.model.OrderedComponent; | 
| 44 | import org.deduced.viewer.model.OrderedComponentList; | 
| 45 | import org.deduced.viewer.model.PopupPanel; | 
| 46 | import org.deduced.viewer.model.ScrollPanel; | 
| 47 | import org.deduced.viewer.model.SplitPanel; | 
| 48 | import org.deduced.viewer.model.TextArea; | 
| 49 | import org.deduced.viewer.model.TextBox; | 
| 50 | import org.deduced.viewer.model.View; | 
| 51 | import org.deduced.viewer.model.combobox.ComboBox; | 
| 52 | import org.deduced.viewer.model.combobox.ComboBoxItem; | 
| 53 | import org.deduced.viewer.model.combobox.OrderedComboBoxItem; | 
| 54 | import org.deduced.viewer.model.combobox.OrderedComboBoxItemList; | 
| 55 | import org.deduced.viewer.model.tree.OrderedTreeItem; | 
| 56 | import org.deduced.viewer.model.tree.OrderedTreeItemList; | 
| 57 | import org.deduced.viewer.model.tree.Tree; | 
| 58 | import org.deduced.viewer.model.tree.TreeItem; | 
| 59 | import org.deduced.viewer.web.shared.ChangeEvent; | 
| 60 | import org.deduced.viewer.web.shared.SerializedValue; | 
| 61 | import org.deduced.viewer.web.shared.Serializer; | 
| 62 | import org.deduced.viewer.web.shared.Serializer.StringSerializedValue; | 
| 63 |  | 
| 64 | /** | 
| 65 | * Master Web Serializer Implementation | 
| 66 | * | 
| 67 | * @author Steve McDuff | 
| 68 | * | 
| 69 | */ | 
| 70 | public class MasterWebSerializerImplementation implements MasterWebSerializer | 
| 71 | { | 
| 72 |  | 
| 73 | /** | 
| 74 | * map containing the association between the base user interface types and | 
| 75 | * their associated serializer. | 
| 76 | */ | 
| 77 | private Map<PropertyCollection<?, ?>, WebSerializer> baseTypeToSerializerAssociation = | 
| 78 | new IdentityHashMap<PropertyCollection<?, ?>, WebSerializer>(); | 
| 79 |  | 
| 80 | /** | 
| 81 | * map containing the relationship between a property collection type and a | 
| 82 | * serializer used for that type. The key is used by identity and held with | 
| 83 | * a weak pointer in case the type gets deleted. | 
| 84 | */ | 
| 85 | @SuppressWarnings("unchecked") | 
| 86 | private Map<PropertyCollection<?, ?>, WebSerializer> typeToSerializerAssociation = | 
| 87 | new IdentityReferenceMap(true, true, IdentityReferenceMap.WEAK, | 
| 88 | IdentityReferenceMap.HARD); | 
| 89 |  | 
| 90 | /** | 
| 91 | * prioritized Base User Interface Type List. This list will be parsed in | 
| 92 | * order to figure out which base UI type to use in case a type inherits | 
| 93 | * from multiple types. | 
| 94 | */ | 
| 95 | private List<PropertyCollection<?, ?>> prioritizedBaseUserInterfaceTypeList = | 
| 96 | new ArrayList<PropertyCollection<?, ?>>(); | 
| 97 |  | 
| 98 | /** | 
| 99 | * set of all the allowed classes for external serialization | 
| 100 | */ | 
| 101 | private static Set<Class<?>> ALLOWED_SERIALIZED_CLASSES = | 
| 102 | new HashSet<Class<?>>(); | 
| 103 |  | 
| 104 | static | 
| 105 | { | 
| 106 | populateAllowedSerializedClasses(); | 
| 107 | } | 
| 108 |  | 
| 109 | /** | 
| 110 | * MasterWebSerializerImplementation constructor | 
| 111 | */ | 
| 112 | public MasterWebSerializerImplementation() | 
| 113 | { | 
| 114 | createPrioritizedTypeToSerializerAssociationMap(); | 
| 115 | } | 
| 116 |  | 
| 117 | /** | 
| 118 | * populate Allowed Serialized Classes set | 
| 119 | */ | 
| 120 | private static void populateAllowedSerializedClasses() | 
| 121 | { | 
| 122 | ALLOWED_SERIALIZED_CLASSES.add(Integer.class); | 
| 123 | ALLOWED_SERIALIZED_CLASSES.add(String.class); | 
| 124 | ALLOWED_SERIALIZED_CLASSES.add(Boolean.class); | 
| 125 | ALLOWED_SERIALIZED_CLASSES.add(Short.class); | 
| 126 | ALLOWED_SERIALIZED_CLASSES.add(Byte.class); | 
| 127 | ALLOWED_SERIALIZED_CLASSES.add(Character.class); | 
| 128 | ALLOWED_SERIALIZED_CLASSES.add(Float.class); | 
| 129 | ALLOWED_SERIALIZED_CLASSES.add(Double.class); | 
| 130 | } | 
| 131 |  | 
| 132 | /** | 
| 133 | * create Prioritized Type To Serializer Association Map | 
| 134 | */ | 
| 135 | private void createPrioritizedTypeToSerializerAssociationMap() | 
| 136 | { | 
| 137 | associatePrioritizedBaseUserInterfaceTypeToSerializer( | 
| 138 | CheckBox.CHECK_BOX_TYPE.getPropertyCollectionType(), | 
| 139 | new CheckBoxModelSerializer()); | 
| 140 |  | 
| 141 | associatePrioritizedBaseUserInterfaceTypeToSerializer( | 
| 142 | Button.BUTTON_TYPE.getPropertyCollectionType(), | 
| 143 | new ButtonModelSerializer()); | 
| 144 |  | 
| 145 | associatePrioritizedBaseUserInterfaceTypeToSerializer( | 
| 146 | ComboBox.COMBO_BOX_TYPE.getPropertyCollectionType(), | 
| 147 | new ComboBoxModelSerializer()); | 
| 148 |  | 
| 149 | associatePrioritizedBaseUserInterfaceTypeToSerializer( | 
| 150 | FlowPanel.FLOW_PANEL_TYPE.getPropertyCollectionType(), | 
| 151 | new FlowPanelModelSerializer()); | 
| 152 |  | 
| 153 | associatePrioritizedBaseUserInterfaceTypeToSerializer( | 
| 154 | Image.IMAGE_TYPE.getPropertyCollectionType(), | 
| 155 | new ImageModelSerializer()); | 
| 156 |  | 
| 157 | associatePrioritizedBaseUserInterfaceTypeToSerializer( | 
| 158 | Label.LABEL_TYPE.getPropertyCollectionType(), | 
| 159 | new LabelModelSerializer()); | 
| 160 |  | 
| 161 | associatePrioritizedBaseUserInterfaceTypeToSerializer( | 
| 162 | PopupPanel.POPUP_PANEL_TYPE.getPropertyCollectionType(), | 
| 163 | new PopupPanelModelSerializer()); | 
| 164 |  | 
| 165 | associatePrioritizedBaseUserInterfaceTypeToSerializer( | 
| 166 | ScrollPanel.SCROLL_PANEL_TYPE.getPropertyCollectionType(), | 
| 167 | new ScrollPanelModelSerializer()); | 
| 168 |  | 
| 169 | associatePrioritizedBaseUserInterfaceTypeToSerializer( | 
| 170 | OrderedTreeItem.ORDERED_TREE_ITEM_TYPE.getPropertyCollectionType(), | 
| 171 | new OrderedTreeItemModelSerializer()); | 
| 172 |  | 
| 173 | associatePrioritizedBaseUserInterfaceTypeToSerializer( | 
| 174 | OrderedComponent.ORDERED_COMPONENT_TYPE.getPropertyCollectionType(), | 
| 175 | new OrderedWidgetModelSerializer()); | 
| 176 |  | 
| 177 | associatePrioritizedBaseUserInterfaceTypeToSerializer( | 
| 178 | OrderedComboBoxItem.ORDERED_COMBO_BOX_ITEM_TYPE | 
| 179 | .getPropertyCollectionType(), | 
| 180 | new OrderedComboBoxItemModelSerializer()); | 
| 181 |  | 
| 182 | associatePrioritizedBaseUserInterfaceTypeToSerializer( | 
| 183 | SplitPanel.SPLIT_PANEL_TYPE.getPropertyCollectionType(), | 
| 184 | new SplitPanelModelSerializer()); | 
| 185 |  | 
| 186 | associatePrioritizedBaseUserInterfaceTypeToSerializer( | 
| 187 | TextArea.TEXT_AREA_TYPE.getPropertyCollectionType(), | 
| 188 | new TextAreaModelSerializer()); | 
| 189 |  | 
| 190 | associatePrioritizedBaseUserInterfaceTypeToSerializer( | 
| 191 | TextBox.TEXT_BOX_TYPE.getPropertyCollectionType(), | 
| 192 | new TextBoxModelSerializer()); | 
| 193 |  | 
| 194 | associatePrioritizedBaseUserInterfaceTypeToSerializer( | 
| 195 | TreeItem.TREE_ITEM_TYPE.getPropertyCollectionType(), | 
| 196 | new TreeItemModelSerializer()); | 
| 197 |  | 
| 198 | associatePrioritizedBaseUserInterfaceTypeToSerializer( | 
| 199 | View.VIEW_TYPE.getPropertyCollectionType(), | 
| 200 | new ViewModelSerializer()); | 
| 201 |  | 
| 202 | associatePrioritizedBaseUserInterfaceTypeToSerializer( | 
| 203 | ComboBoxItem.COMBO_BOX_ITEM_TYPE.getPropertyCollectionType(), | 
| 204 | new ComboBoxItemModelSerializer()); | 
| 205 |  | 
| 206 | associatePrioritizedBaseUserInterfaceTypeToSerializer( | 
| 207 | Tree.TREE_TYPE.getPropertyCollectionType(), | 
| 208 | new TreeModelSerializer()); | 
| 209 |  | 
| 210 | associatePrioritizedBaseUserInterfaceTypeToSerializer( | 
| 211 | OrderedTreeItemList.ORDERED_TREE_ITEM_LIST_TYPE | 
| 212 | .getPropertyCollectionType(), | 
| 213 | new OrderedListModelSerializer()); | 
| 214 |  | 
| 215 | associatePrioritizedBaseUserInterfaceTypeToSerializer( | 
| 216 | OrderedComponentList.ORDERED_COMPONENT_LIST_TYPE | 
| 217 | .getPropertyCollectionType(), | 
| 218 | new OrderedListModelSerializer()); | 
| 219 |  | 
| 220 | associatePrioritizedBaseUserInterfaceTypeToSerializer( | 
| 221 | OrderedComboBoxItemList.ORDERED_COMBO_BOX_ITEM_LIST_TYPE | 
| 222 | .getPropertyCollectionType(), | 
| 223 | new OrderedListModelSerializer()); | 
| 224 |  | 
| 225 | associatePrioritizedBaseUserInterfaceTypeToSerializer( | 
| 226 | PropertyList.PROPERTY_LIST_TYPE.getPropertyCollectionType(), | 
| 227 | new ListModelSerializer()); | 
| 228 |  | 
| 229 | associatePrioritizedBaseUserInterfaceTypeToSerializer( | 
| 230 | Component.COMPONENT_TYPE.getPropertyCollectionType(), | 
| 231 | new WidgetModelSerializer()); | 
| 232 | } | 
| 233 |  | 
| 234 | /** | 
| 235 | * associate a Prioritized Base User Interface Type To a Serializer | 
| 236 | * | 
| 237 | * @param userInterfaceType the user interface type | 
| 238 | * @param serializer the serializer to use | 
| 239 | */ | 
| 240 | private void associatePrioritizedBaseUserInterfaceTypeToSerializer( | 
| 241 | PropertyCollection<?, ?> userInterfaceType, WebSerializer serializer) | 
| 242 | { | 
| 243 | AssertUtilities.assertNotNull(userInterfaceType); | 
| 244 | AssertUtilities.assertNotNull(serializer); | 
| 245 |  | 
| 246 | baseTypeToSerializerAssociation.put(userInterfaceType, serializer); | 
| 247 |  | 
| 248 | prioritizedBaseUserInterfaceTypeList.add(userInterfaceType); | 
| 249 | } | 
| 250 |  | 
| 251 | /** | 
| 252 | * (non-JSDoc) | 
| 253 | * | 
| 254 | * @see org.deduced.viewer.web.serializer.MasterWebSerializer#serialize(org.deduced.PropertyCollection) | 
| 255 | */ | 
| 256 | @Override | 
| 257 | public Serializable serialize( | 
| 258 | PropertyCollection<?, ?> collection) | 
| 259 | { | 
| 260 | Serializable returnValue = null; | 
| 261 | if (collection == null) | 
| 262 | { | 
| 263 | return returnValue; | 
| 264 | } | 
| 265 |  | 
| 266 | PropertyCollection<?, ?> collectionType = collection.type(); | 
| 267 | WebSerializer webSerializer = | 
| 268 | typeToSerializerAssociation.get(collectionType); | 
| 269 |  | 
| 270 | if (webSerializer == null) | 
| 271 | { | 
| 272 | webSerializer = | 
| 273 | createAndCacheTypeToSerializerAssociation(collectionType); | 
| 274 | } | 
| 275 |  | 
| 276 | if (webSerializer != null) | 
| 277 | { | 
| 278 | returnValue = webSerializer.serialize(collection, this); | 
| 279 | } | 
| 280 |  | 
| 281 | return returnValue; | 
| 282 | } | 
| 283 |  | 
| 284 | /** | 
| 285 | * create And Cache Type To Serializer Association will look up which type | 
| 286 | * of serializer to use for a type and cache it so it can be reused in the | 
| 287 | * future. | 
| 288 | * | 
| 289 | * @param collectionType the collection type to bind to a serializer | 
| 290 | * @return the serializer to use on the collection type | 
| 291 | */ | 
| 292 | private WebSerializer createAndCacheTypeToSerializerAssociation( | 
| 293 | PropertyCollection<?, ?> collectionType) | 
| 294 | { | 
| 295 | WebSerializer webSerializer; | 
| 296 | PropertyCollection<?, ?> baseUserInterfaceType = | 
| 297 | getBaseUserInterfaceType(collectionType); | 
| 298 |  | 
| 299 | webSerializer = | 
| 300 | baseTypeToSerializerAssociation.get(baseUserInterfaceType); | 
| 301 |  | 
| 302 | if (webSerializer != null) | 
| 303 | { | 
| 304 | typeToSerializerAssociation.put(collectionType, webSerializer); | 
| 305 | } | 
| 306 |  | 
| 307 | return webSerializer; | 
| 308 | } | 
| 309 |  | 
| 310 | /** | 
| 311 | * get the Base User Interface Type of a collection type by looking at it's | 
| 312 | * list of parents and matching the first type we find in the prioritized | 
| 313 | * user interface type list. | 
| 314 | * | 
| 315 | * @param collectionType the collection | 
| 316 | * @return the associated base user interface type. Null if none can be | 
| 317 | *         found. | 
| 318 | */ | 
| 319 | private PropertyCollection<?, ?> getBaseUserInterfaceType( | 
| 320 | PropertyCollection<?, ?> collectionType) | 
| 321 | { | 
| 322 | for (PropertyCollection<?, ?> userInterfaceType : prioritizedBaseUserInterfaceTypeList) | 
| 323 | { | 
| 324 | if (DeducedUtilities | 
| 325 | .isInstanceOf(collectionType, userInterfaceType)) | 
| 326 | { | 
| 327 | return userInterfaceType; | 
| 328 | } | 
| 329 | } | 
| 330 | return null; | 
| 331 | } | 
| 332 |  | 
| 333 | /** | 
| 334 | * (non-JSDoc) | 
| 335 | * | 
| 336 | * @see org.deduced.viewer.web.serializer.MasterWebSerializer#serializeChangeEvent(org.deduced.PropertyChangeEvent) | 
| 337 | */ | 
| 338 | @Override | 
| 339 | public ChangeEvent serializeChangeEvent( | 
| 340 | PropertyChangeEvent<?, ?> propertyChangeEvent) | 
| 341 | { | 
| 342 | if (propertyChangeEvent == null) | 
| 343 | { | 
| 344 | return null; | 
| 345 | } | 
| 346 |  | 
| 347 | ChangeEvent retVal = new ChangeEvent(); | 
| 348 | retVal | 
| 349 | .setId(ModelSerializer.getID(propertyChangeEvent.getCollection())); | 
| 350 |  | 
| 351 | PropertyCollection<?, ?> changedCollectionType = | 
| 352 | propertyChangeEvent.getCollection().type(); | 
| 353 |  | 
| 354 | PropertyCollection orderingInstance = | 
| 355 | OrderingPropertyCollectionTypeImplementation | 
| 356 | .getOrderingInstance(changedCollectionType); | 
| 357 |  | 
| 358 | PropertyCollection<?, ?> eventInstance = | 
| 359 | propertyChangeEvent.getInstance(); | 
| 360 |  | 
| 361 | if (orderingInstance == eventInstance) | 
| 362 | { | 
| 363 | retVal.setSortingInstance(true); | 
| 364 | } | 
| 365 |  | 
| 366 | retVal.setName(DeducedUtilities.getCollectionName(propertyChangeEvent | 
| 367 | .getInstance())); | 
| 368 |  | 
| 369 | retVal.setType(propertyChangeEvent.getChangeType().toString()); | 
| 370 |  | 
| 371 | serializeChangeEventValue(propertyChangeEvent, retVal); | 
| 372 |  | 
| 373 | return retVal; | 
| 374 | } | 
| 375 |  | 
| 376 | /** | 
| 377 | * serialize Change Event Value | 
| 378 | * | 
| 379 | * @param propertyChangeEvent the change event to serialize | 
| 380 | * @param changeEvent the change event to fill | 
| 381 | */ | 
| 382 | private void serializeChangeEventValue( | 
| 383 | PropertyChangeEvent<?, ?> propertyChangeEvent, ChangeEvent changeEvent) | 
| 384 | { | 
| 385 | Object newValue = propertyChangeEvent.getNewValue(); | 
| 386 | if (newValue != null) | 
| 387 | { | 
| 388 | if (newValue instanceof PropertyCollection<?, ?>) | 
| 389 | { | 
| 390 | serializeChangeEventCollectionValue(propertyChangeEvent, | 
| 391 | changeEvent, (PropertyCollection<?, ?>) newValue); | 
| 392 | } | 
| 393 | else if (isSerializationAllowedOnObject(newValue)) | 
| 394 | { | 
| 395 | SerializedValue serialized = | 
| 396 | Serializer.serialize((Serializable) newValue); | 
| 397 | changeEvent.setValue(serialized); | 
| 398 | } | 
| 399 | } | 
| 400 |  | 
| 401 | if (propertyChangeEvent.getChangeType() == ChangeType.REMOVE) | 
| 402 | { | 
| 403 | Object oldValue = propertyChangeEvent.getOldValue(); | 
| 404 | if (oldValue instanceof PropertyCollection<?, ?>) | 
| 405 | { | 
| 406 | StringSerializedValue stringValue = | 
| 407 | Serializer.serializeString(ModelSerializer | 
| 408 | .getID((PropertyCollection<?, ?>) oldValue)); | 
| 409 | changeEvent.setValue(stringValue); | 
| 410 | } | 
| 411 | } | 
| 412 | } | 
| 413 |  | 
| 414 | /** | 
| 415 | * is Serialization Allowed on a specified object | 
| 416 | * | 
| 417 | * @param newValue the value to test | 
| 418 | * @return true if serialization is allowed, false otherwise | 
| 419 | */ | 
| 420 | public boolean isSerializationAllowedOnObject( | 
| 421 | Object newValue) | 
| 422 | { | 
| 423 | return ALLOWED_SERIALIZED_CLASSES.contains(newValue.getClass()); | 
| 424 | } | 
| 425 |  | 
| 426 | /** | 
| 427 | * serialize Change Event Collection Value | 
| 428 | * | 
| 429 | * @param propertyChangeEvent the change event | 
| 430 | * @param changeEvent the change event to fill | 
| 431 | * @param newCollectionValue the new collection value to serialize | 
| 432 | */ | 
| 433 | private void serializeChangeEventCollectionValue( | 
| 434 | PropertyChangeEvent<?, ?> propertyChangeEvent, ChangeEvent changeEvent, | 
| 435 | PropertyCollection<?, ?> newCollectionValue) | 
| 436 | { | 
| 437 | boolean isReference = | 
| 438 | PropertyInstanceImplementation.isReference( | 
| 439 | propertyChangeEvent.getInstance()).booleanValue(); | 
| 440 |  | 
| 441 | if (isEnumerationPropertyCollection(newCollectionValue)) | 
| 442 | { | 
| 443 | String collectionName = | 
| 444 | DeducedUtilities.getCollectionName(newCollectionValue); | 
| 445 | StringSerializedValue serializeString = | 
| 446 | Serializer.serializeString(collectionName); | 
| 447 | changeEvent.setValue(serializeString); | 
| 448 | } | 
| 449 | else if (isReference) | 
| 450 | { | 
| 451 | String id = ModelSerializer.getID(newCollectionValue); | 
| 452 | StringSerializedValue serializeString = | 
| 453 | Serializer.serializeString(id); | 
| 454 | changeEvent.setValue(serializeString); | 
| 455 | } | 
| 456 | else | 
| 457 | { | 
| 458 | Serializable serializedValue = serialize(newCollectionValue); | 
| 459 | SerializedValue serialize = Serializer.serialize(serializedValue); | 
| 460 | changeEvent.setValue(serialize); | 
| 461 | } | 
| 462 | } | 
| 463 |  | 
| 464 | /** | 
| 465 | * check if a collection is an Enumeration Property Collection | 
| 466 | * | 
| 467 | * @param potentialEnumeration the potential enumeration | 
| 468 | * @return true if the collection is an enumeration type | 
| 469 | */ | 
| 470 | public static boolean isEnumerationPropertyCollection( | 
| 471 | PropertyCollection<?, ?> potentialEnumeration) | 
| 472 | { | 
| 473 | PropertyCollection<?, ?> objectType = potentialEnumeration.type(); | 
| 474 | PropertyCollectionType enumerationType = | 
| 475 | EnumerationPropertyCollection.ENUMERATION_PROPERTY_COLLECTION_TYPE | 
| 476 | .getPropertyCollectionType(); | 
| 477 | return DeducedUtilities.isInstanceOf(objectType, enumerationType); | 
| 478 | } | 
| 479 |  | 
| 480 | } |