/** * Returns the final value by using the appropriate type support. * * @param <T> the type of the value * @param oldValue the oldValue, which was previously in the previous notification * @param newValue the newValue, which was computed during the scanning * @return the value to be notified */ public static <T> Notification<T> notification(final T oldValue, final T newValue) { NotificationSupport<T> support = findNotificationSupportFor(newValue); return support.prepareNotification(oldValue, newValue); }
/** * Retrieves the notification support based on the given value. * <p> * If no support is found, an exception is thrown. * * @param <T> value type * @param newValue the value; can't be null * @return the notification support */ public static <T> NotificationSupport<T> findNotificationSupportFor(final T newValue) { @SuppressWarnings("unchecked") Class<T> typeClass = (Class<T>) newValue.getClass(); NotificationSupport<T> support = (NotificationSupport<T>) findTypeSupportFor(NotificationSupport.class, typeClass); if (support == null) { String name = typeClass.getSimpleName(); if (name == null || name.trim().isEmpty()) { name = typeClass.getName(); } throw new RuntimeException("Final value can't be of type " + name + "."); } return support; }
/** * Installs type support. This should only be called by either DataSources * or ExpressionLanguage libraries that require support for these types. */ public static void install() { // Install only once if (installed) { return; } // This looks like double-checked locking and in fact it is, but it is // safe because installed is volatile. // If we used an AtomicBoolean instead of the volatile boolean, we would // not need the lock (we could use a compare-and-set operation), but in // this case another thread calling this method might return before the // installation has actually finished. synchronized (installLock) { if (installed) { return; } // Add notification support for all immutable types TypeSupport.addTypeSupport(NotificationSupport.immutableTypeSupport(VType.class)); installed = true; } }
@Override @SuppressWarnings({"unchecked", "rawtypes"}) public Notification<Map> prepareNotification(Map oldValue, final Map newValue) { // Check all the elements in the map and use StandardTypeSupport // to understand whether any needs notification. // Notification is done only if at least one element needs notification. boolean notificationNeeded = false; if (oldValue == null || (oldValue.size() != newValue.size())) { notificationNeeded = true; } int index = 0; Iterator<Map.Entry> iterator = newValue.entrySet().iterator(); while (notificationNeeded == false && iterator.hasNext()) { Entry entry = iterator.next(); Object key = entry.getKey(); if (entry.getValue() != null) { Notification itemNotification = NotificationSupport.notification(oldValue.get(key), entry.getValue()); if (itemNotification.isNotificationNeeded()) { notificationNeeded = true; } } } if (notificationNeeded) { return new Notification<>(true, (Map) Collections.unmodifiableMap(new HashMap<>(newValue))); } else { return new Notification<>(false, oldValue); } } });
NotificationSupport.findNotificationSupportFor(newValue);
/** * Installs type support. */ public static void install() { // Install only once if (installed) { return; } // This looks like double-checked locking and in fact it is, but it is // safe because installed is volatile. // If we used an AtomicBoolean instead of the volatile boolean, we would // not need the lock (we could use a compare-and-set operation), but in // this case another thread calling this method might return before the // installation has actually finished. synchronized (installLock) { if (installed) { return; } // Add support for lists addList(); addMap(); // Add support for numbers and strings TypeSupport.addTypeSupport(NotificationSupport.immutableTypeSupport(Number.class)); TypeSupport.addTypeSupport(NotificationSupport.immutableTypeSupport(String.class)); installed = true; } }
@Override @SuppressWarnings({"unchecked", "rawtypes"}) public Notification<List> prepareNotification(List oldValue, final List newValue) { // Check all the elements in the list and use StandardTypeSupport // to understand whether any needs notification. // Notification is done only if at least one element needs notification. boolean notificationNeeded = false; if (oldValue == null || (oldValue.size() != newValue.size())) { notificationNeeded = true; } if (newValue.isEmpty()) { notificationNeeded = false; } int index = 0; while (notificationNeeded == false && index < newValue.size()) { if (newValue.get(index) != null) { Notification itemNotification = NotificationSupport.notification(oldValue.get(index), newValue.get(index)); if (itemNotification.isNotificationNeeded()) { notificationNeeded = true; } } index++; } if (notificationNeeded) { return new Notification<>(true, (List) Collections.unmodifiableList(new ArrayList<Object>(newValue))); } else { return new Notification<>(false, oldValue); } } });
NotificationSupport.notification(pv.getValue(), finalValue);