/*
* Copyright 2016 Red Hat, Inc. and/or its affiliates.
*
* 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 org.uberfire.preferences.backend;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import javax.enterprise.inject.Instance;
import javax.enterprise.util.AnnotationLiteral;
import javax.inject.Inject;
import org.jboss.errai.bus.server.annotations.Service;
import org.uberfire.annotations.Customizable;
import org.uberfire.mvp.Command;
import org.uberfire.mvp.ParameterizedCommand;
import org.uberfire.preferences.shared.PreferenceScope;
import org.uberfire.preferences.shared.PreferenceScopeResolutionStrategy;
import org.uberfire.preferences.shared.PreferenceStore;
import org.uberfire.preferences.shared.annotations.PortablePreference;
import org.uberfire.preferences.shared.annotations.Property;
import org.uberfire.preferences.shared.annotations.WorkbenchPreference;
import org.uberfire.preferences.shared.bean.BasePreference;
import org.uberfire.preferences.shared.bean.BasePreferencePortable;
import org.uberfire.preferences.shared.bean.Preference;
import org.uberfire.preferences.shared.bean.PreferenceBeanServerStore;
import org.uberfire.preferences.shared.bean.PreferenceBeanStore;
import org.uberfire.preferences.shared.bean.PreferenceHierarchyElement;
import org.uberfire.preferences.shared.impl.PreferenceScopeResolutionStrategyInfo;
/**
* Backend implementation for {@link PreferenceBeanStore}.
*/
@Service
public class PreferenceBeanStoreImpl implements PreferenceBeanServerStore {
private static final AnnotationLiteral<PortablePreference> portablePreferenceAnnotation = new AnnotationLiteral<PortablePreference>() {
};
private PreferenceStore preferenceStore;
private PreferenceScopeResolutionStrategy defaultScopeResolutionStrategy;
private Instance<Preference> preferences;
private Map<String, List<BasePreferencePortable>> childrenByParent;
public PreferenceBeanStoreImpl() {
}
@Inject
public PreferenceBeanStoreImpl(final PreferenceStore preferenceStore,
@Customizable final PreferenceScopeResolutionStrategy defaultScopeResolutionStrategy,
@PortablePreference final Instance<Preference> preferences) {
this.preferenceStore = preferenceStore;
this.defaultScopeResolutionStrategy = defaultScopeResolutionStrategy;
this.preferences = preferences;
}
@Override
public <U extends BasePreference<U>, T extends BasePreferencePortable<U>> T load(final T emptyPortablePreference) {
return load(emptyPortablePreference,
defaultScopeResolutionStrategy.getInfo());
}
@Override
public <U extends BasePreference<U>, T extends BasePreferencePortable<U>> T load(final T emptyPortablePreference,
final PreferenceScopeResolutionStrategyInfo scopeResolutionStrategyInfo) {
Class<U> clazz = emptyPortablePreference.getPojoClass();
T portablePreference = preferenceStore.get(scopeResolutionStrategyInfo,
emptyPortablePreference.identifier());
try {
return load(clazz,
portablePreference,
scopeResolutionStrategyInfo);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
@Override
public <U extends BasePreference<U>, T extends BasePreferencePortable<U>> void load(final T emptyPortablePreference,
final ParameterizedCommand<T> successCallback,
final ParameterizedCommand<Throwable> errorCallback) {
load(emptyPortablePreference,
defaultScopeResolutionStrategy.getInfo(),
successCallback,
errorCallback);
}
@Override
public <U extends BasePreference<U>, T extends BasePreferencePortable<U>> void load(final T emptyPortablePreference,
final PreferenceScopeResolutionStrategyInfo scopeResolutionStrategyInfo,
final ParameterizedCommand<T> successCallback,
final ParameterizedCommand<Throwable> errorCallback) {
T loadedPreference = null;
try {
loadedPreference = load(emptyPortablePreference,
scopeResolutionStrategyInfo);
} catch (Exception e) {
if (errorCallback != null) {
errorCallback.execute(e);
}
}
if (successCallback != null) {
successCallback.execute(loadedPreference);
}
}
@Override
public <U extends BasePreference<U>, T extends BasePreferencePortable<U>> void save(final T portablePreference) {
save(portablePreference,
defaultScopeResolutionStrategy.getInfo());
}
private <U extends BasePreference<U>, T extends BasePreferencePortable<U>> void save(final T portablePreference,
final PreferenceScope scope) {
try {
Class<U> clazz = portablePreference.getPojoClass();
save(clazz,
portablePreference,
scope);
if (portablePreference.isPersistable()) {
preferenceStore.put(scope,
portablePreference.identifier(),
portablePreference);
}
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
@Override
public <U extends BasePreference<U>, T extends BasePreferencePortable<U>> void save(final T portablePreference,
final PreferenceScopeResolutionStrategyInfo scopeResolutionStrategyInfo) {
save(portablePreference,
scopeResolutionStrategyInfo.defaultScope());
}
@Override
public <U extends BasePreference<U>, T extends BasePreferencePortable<U>> void save(final T portablePreference,
final Command successCallback,
final ParameterizedCommand<Throwable> errorCallback) {
save(portablePreference,
defaultScopeResolutionStrategy.getInfo(),
successCallback,
errorCallback);
}
@Override
public <U extends BasePreference<U>, T extends BasePreferencePortable<U>> void save(final T portablePreference,
final PreferenceScopeResolutionStrategyInfo scopeResolutionStrategyInfo,
final Command successCallback,
final ParameterizedCommand<Throwable> errorCallback) {
try {
save(portablePreference,
scopeResolutionStrategyInfo);
} catch (Exception e) {
if (errorCallback != null) {
errorCallback.execute(e);
}
}
if (successCallback != null) {
successCallback.execute();
}
}
@Override
public void save(final Collection<BasePreferencePortable<? extends BasePreference<?>>> portablePreferences) {
save(portablePreferences,
defaultScopeResolutionStrategy.getInfo());
}
@Override
public void save(final Collection<BasePreferencePortable<? extends BasePreference<?>>> portablePreferences,
final PreferenceScopeResolutionStrategyInfo scopeResolutionStrategyInfo) {
for (BasePreferencePortable<? extends BasePreference<?>> portablePreference : portablePreferences) {
saveOne(portablePreference,
scopeResolutionStrategyInfo.defaultScope());
}
}
@Override
public void save(final Collection<BasePreferencePortable<? extends BasePreference<?>>> portablePreferences,
final Command successCallback,
final ParameterizedCommand<Throwable> errorCallback) {
save(portablePreferences,
defaultScopeResolutionStrategy.getInfo(),
successCallback,
errorCallback);
}
@Override
public void save(final Collection<BasePreferencePortable<? extends BasePreference<?>>> portablePreferences,
final PreferenceScopeResolutionStrategyInfo scopeResolutionStrategyInfo,
final Command successCallback,
final ParameterizedCommand<Throwable> errorCallback) {
try {
save(portablePreferences,
scopeResolutionStrategyInfo);
} catch (Exception e) {
if (errorCallback != null) {
errorCallback.execute(e);
}
}
if (successCallback != null) {
successCallback.execute();
}
}
@Override
public <U extends BasePreference<U>, T extends BasePreferencePortable<U>> void saveDefaultValue(final T defaultValue) {
final List<PreferenceScope> scopeOrder = defaultScopeResolutionStrategy.getInfo().order();
final int lastIndex = scopeOrder.size() - 1;
final PreferenceScope lastScope = scopeOrder.get(lastIndex);
save(defaultValue,
lastScope);
}
@Override
public <U extends BasePreference<U>, T extends BasePreferencePortable<U>> void saveDefaultValue(final T defaultValue,
final Command successCallback,
final ParameterizedCommand<Throwable> errorCallback) {
try {
saveDefaultValue(defaultValue);
} catch (Exception e) {
if (errorCallback != null) {
errorCallback.execute(e);
}
}
if (successCallback != null) {
successCallback.execute();
}
}
@Override
public PreferenceHierarchyElement<?> buildHierarchyStructureForPreference(String identifier) {
return buildHierarchyStructureForPreference(identifier,
defaultScopeResolutionStrategy.getInfo());
}
@Override
public PreferenceHierarchyElement<?> buildHierarchyStructureForPreference(final String identifier,
final PreferenceScopeResolutionStrategyInfo scopeResolutionStrategyInfo) {
BasePreferencePortable preference = getPortablePreferenceByIdentifier(identifier);
preference = load(preference,
scopeResolutionStrategyInfo);
final PreferenceHierarchyElement<?> rootElement = buildHierarchyElement(preference,
null,
false,
true,
preference.bundleKey(),
scopeResolutionStrategyInfo);
return rootElement;
}
private <U extends BasePreference<U>, T extends BasePreferencePortable<U>> T load(final Class<U> clazz,
T portablePreference,
final PreferenceScopeResolutionStrategyInfo scopeResolutionStrategyInfo) throws IllegalAccessException {
if (portablePreference == null) {
portablePreference = lookupPortablePreference(clazz);
}
for (Field field : portablePreference.getPojoClass().getDeclaredFields()) {
Property propertyAnnotation = field.getAnnotation(Property.class);
if (propertyAnnotation != null) {
if (field.getType().isAnnotationPresent(WorkbenchPreference.class)) {
final Class<? extends BasePreference<?>> propertyType = (Class<? extends BasePreference<?>>) field.getType();
boolean shared = propertyAnnotation.shared();
field.setAccessible(true);
if (shared) {
BasePreferencePortable<?> loadedSharedProperty = loadSharedPreference(field,
scopeResolutionStrategyInfo);
field.set(portablePreference,
loadedSharedProperty);
} else {
final BasePreferencePortable<?> subPreferenceValue = loadSubPreferenceValue(portablePreference,
field,
scopeResolutionStrategyInfo);
field.set(portablePreference,
subPreferenceValue);
}
}
}
}
return portablePreference;
}
private <U extends BasePreference<U>, T extends BasePreferencePortable<U>> T loadSharedPreference(final Field field,
final PreferenceScopeResolutionStrategyInfo scopeResolutionStrategyInfo) {
final Class<U> propertyType = (Class<U>) field.getType();
T loadedPreference;
try {
T emptyPortablePreference = lookupPortablePreference(propertyType);
T portablePreference = preferenceStore.get(scopeResolutionStrategyInfo,
emptyPortablePreference.identifier());
loadedPreference = load(propertyType,
portablePreference,
scopeResolutionStrategyInfo);
} catch (Exception e) {
throw new RuntimeException(e);
}
return loadedPreference;
}
private <U extends BasePreference<U>, T extends BasePreferencePortable<U>> T loadSubPreferenceValue(final Object portablePreference,
final Field field,
final PreferenceScopeResolutionStrategyInfo scopeResolutionStrategyInfo) throws IllegalAccessException {
final Class<U> propertyType = (Class<U>) field.getType();
final T subPreferenceValue = (T) field.get(portablePreference);
return load(propertyType,
subPreferenceValue,
scopeResolutionStrategyInfo);
}
private <U extends BasePreference<U>, T extends BasePreferencePortable<U>> void save(final Class<U> clazz,
final T portablePreference,
final PreferenceScope scope) throws IllegalAccessException {
for (Field field : portablePreference.getPojoClass().getDeclaredFields()) {
Property propertyAnnotation = field.getAnnotation(Property.class);
if (propertyAnnotation != null) {
if (field.getType().isAnnotationPresent(WorkbenchPreference.class)) {
boolean shared = propertyAnnotation.shared();
field.setAccessible(true);
if (shared) {
saveSharedPreference(portablePreference,
field,
scope);
} else {
saveSubPreference(portablePreference,
field,
scope);
}
}
}
}
}
private <U extends BasePreference<U>, T extends BasePreferencePortable<U>> void saveSharedPreference(final Object portablePreference,
final Field field,
final PreferenceScope scope) throws IllegalAccessException {
final Class<U> propertyType = (Class<U>) field.getType();
final T sharedPropertyValue = (T) field.get(portablePreference);
save(sharedPropertyValue,
scope);
}
private <U extends BasePreference<U>, T extends BasePreferencePortable<U>> void saveSubPreference(final Object portablePreference,
final Field field,
final PreferenceScope scope) throws IllegalAccessException {
final Class<U> propertyType = (Class<U>) field.getType();
final T subPreferenceValue = (T) field.get(portablePreference);
save(propertyType,
subPreferenceValue,
scope);
}
private <T extends BasePreference<T>> void saveOne(final BasePreferencePortable<?> portablePreference,
final PreferenceScope scope) {
Class<T> clazz = (Class<T>) portablePreference.getPojoClass();
try {
save(clazz,
(BasePreferencePortable<T>) portablePreference,
scope);
if (portablePreference.isPersistable()) {
preferenceStore.put(scope,
portablePreference.identifier(),
portablePreference);
}
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
private List<BasePreferencePortable> getAnnotatedChildren(String parentIdentifier) {
if (childrenByParent == null) {
childrenByParent = new HashMap<>();
final Iterable<Preference> portablePreferences = getPortablePreferences();
portablePreferences.forEach(preference -> {
final BasePreferencePortable portablePreference = (BasePreferencePortable) preference;
final String[] parents = portablePreference.parents();
for (String parent : parents) {
if (parent != null & !parent.isEmpty()) {
List<BasePreferencePortable> children = childrenByParent.get(parent);
if (children == null) {
children = new ArrayList<>();
childrenByParent.put(parent,
children);
}
children.add(portablePreference);
}
}
});
}
return childrenByParent.get(parentIdentifier);
}
private <T> PreferenceHierarchyElement<T> buildHierarchyElement(final BasePreferencePortable<T> portablePreference,
final PreferenceHierarchyElement<?> parent,
final boolean shared,
final boolean root,
final String bundleKey,
final PreferenceScopeResolutionStrategyInfo scopeResolutionStrategyInfo) {
PreferenceHierarchyElement<T> hierarchyElement = new PreferenceHierarchyElement<>(UUID.randomUUID().toString(),
portablePreference,
shared,
root,
bundleKey);
buildHierarchyElementForAnnotatedChildren(portablePreference,
hierarchyElement,
scopeResolutionStrategyInfo);
try {
hierarchyElement.setPortablePreference(portablePreference);
for (Field field : portablePreference.getPojoClass().getDeclaredFields()) {
Property propertyAnnotation = field.getAnnotation(Property.class);
if (propertyAnnotation != null) {
String propertyBundleKey = "";
if (!propertyAnnotation.bundleKey().isEmpty()) {
propertyBundleKey = propertyAnnotation.bundleKey();
}
if (field.getType().isAnnotationPresent(WorkbenchPreference.class)) {
field.setAccessible(true);
final BasePreferencePortable fieldValue = (BasePreferencePortable) field.get(portablePreference);
if (propertyBundleKey.isEmpty()) {
propertyBundleKey = fieldValue.bundleKey();
}
final PreferenceHierarchyElement<?> childElement = buildHierarchyElement(fieldValue,
hierarchyElement,
propertyAnnotation.shared(),
false,
propertyBundleKey,
scopeResolutionStrategyInfo);
hierarchyElement.getChildren().add(childElement);
} else {
if (propertyBundleKey.isEmpty()) {
propertyBundleKey = field.getName();
}
hierarchyElement.addPropertyBundleKey(field.getName(),
propertyBundleKey);
}
}
}
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return hierarchyElement;
}
private <T> void buildHierarchyElementForAnnotatedChildren(final BasePreferencePortable<T> portablePreference,
final PreferenceHierarchyElement<T> hierarchyElement,
final PreferenceScopeResolutionStrategyInfo scopeResolutionStrategyInfo) {
final List<BasePreferencePortable> annotatedChildren = getAnnotatedChildren(portablePreference.identifier());
if (annotatedChildren != null) {
annotatedChildren.forEach(childPreference -> {
final BasePreferencePortable<?> loadedChild = load(childPreference,
scopeResolutionStrategyInfo);
final PreferenceHierarchyElement<?> childElement = buildHierarchyElement(loadedChild,
hierarchyElement,
false,
true,
childPreference.bundleKey(),
scopeResolutionStrategyInfo);
hierarchyElement.getChildren().add(childElement);
});
}
}
BasePreferencePortable getPortablePreferenceByIdentifier(String identifier) {
for (Preference preference : getPortablePreferences()) {
BasePreferencePortable portablePreference = (BasePreferencePortable) preference;
if (portablePreference.identifier().equals(identifier)) {
return portablePreference;
}
}
return null;
}
Iterable<Preference> getPortablePreferences() {
return preferences.select(portablePreferenceAnnotation);
}
<U extends BasePreference<U>, T extends BasePreferencePortable<U>> T lookupPortablePreference(final Class<U> clazz) {
return (T) preferences.select(clazz,
portablePreferenceAnnotation).get();
}
}