/* * Copyright (C) 2015 Google Inc. * * 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. */ /** * Utilities for binding JSON parsed to maps with the Java interfaces. * The main idea is to work with an object of a given well-defined type, * while the state of the object (values of its properties) are stored in a map * that is a result of parsing a JSON object and/or may be serialized to JSON. * This JSON-orientedness implies some limitations and peculiarities: * <ul> * <li>Supported types are most of the primitive types, String, List, Map, Object. * <li>Maps can only have String keys. * <li>Lists can have elements of any supported type. * <li>Conforming to Java type system may require some transformations, e.g., * Integers/Longs and Strings "NaN", "Infinity", "-Infinity" to Doubles/Floats; * Maps to dynamic objects, etc. * </ul> * * <p> * Implementation details: * <ul> * <li>Two dynamic objects are considered equal if they implement the same interface, * and their backing maps are equal. * <li>Because of the implicit transformations that occur on method invocations, * two dynamic objects may be not equal even if values of the properties returned * from the corresponding methods are equal.<br> * For example, if a dynamic object interface has a property of type Long: * <pre>{@code * public interface Example { * Long getProperty(); * } * } * </pre> * And one object is created with state {@code {"property": 1}} (value of type Integer), * and another one with state {@code {"property": 1L}} (value of type Long), * these two objects will not be equal even though getProperty() will return 1L * for each of them. * * <li>Primitive getters (except {@code boolean} ones) will throw a {@link NullPointerException} * if there is no corresponding value in the backing map. * This is similar to un-boxing null value. * <li>Primitive boolean getters will return {@code false} if there is no value. * <li>If a value in the map cannot be converted to the target type, a {@link ClassCastException} * will be thrown. This also means that {@code null} value can be returned without exceptions * from a getter with any (even unsupported) return type (for primitive types - see above). * <li>Wildcard types in Maps and Lists are deliberately not supported. * <li>Generally, if method's signature is not supported, an {@link UnsupportedOperationException} * will be thrown. * </ul> * * <p> * {@link InterfaceMap} supports following methods in dynamic objects: * <ul> * <li>{@link java.lang.Object#hashCode()} and {@link java.lang.Object#toString()} * which are delegated to the backing map. * <li>{@link java.lang.Object#equals(java.lang.Object)} that compares interfaces * implemented by two dynamic objects and the corresponding backing maps. * <li>{@link Boolean} getters - methods that return {@code Boolean} or {@code boolean} * and whose names start with 'is' followed by capitalized property name, * and which have no parameters. * <li>general getters - methods whose return type is any of the supported types * and whose name starts with 'get', and which have no parameters. * <li>setters - methods whose return type is {@link Void} or {@code void}, * and which have exactly one parameter. * </ul> * * <p> * Use generic interfaces for dynamic objects judiciously. Consider following interfaces: * <pre>{@code * interface GenericProperty<T> { * T getValue(); * } * interface LongProperty extends GenericProperty<Long> { * // Long getValue(); * } * }</pre> * For compiler the method in the subinterface seems to exist, but not for the runtime. * Thus, the return type is erased and is seen as {@code Object}, so no conversions will occur, * which may lead to a ClassCastException. If, however, the method is explicitly overriden * (here - uncommented), everything will work fine. */ package interactivespaces.util.data.dynamic;