/* * * * This file is part of the Hesperides distribution. * * (https://github.com/voyages-sncf-technologies/hesperides) * * Copyright (c) 2016 VSCT. * * * * Hesperides is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as * * published by the Free Software Foundation, version 3. * * * * Hesperides is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see <http://www.gnu.org/licenses/>. * * */ package com.vsct.dt.hesperides.applications; import com.google.common.base.Preconditions; import com.vsct.dt.hesperides.resources.KeyValueValorisation; import com.vsct.dt.hesperides.templating.models.KeyValuePropertyModel; import com.vsct.dt.hesperides.templating.platform.ValorisationData; import java.util.*; import java.util.function.BiConsumer; import java.util.function.BiFunction; import java.util.function.Function; import java.util.stream.Collectors; /** * Created by william_montaz on 20/08/2015. */ /** * We use a two pass processes. * That allows properties to refer to themselves * All remaining properties are considered to be instance properties, * even if they have the same name as some properties * (that might happen if properties call themselves back and forth) like : * key1 -> {{key2}} * key2 -> {{key1}} * That would result in * key1 -> {{key1}} * key2 -> {{key2}} * Such a thing would be bad properties design but we don't want to fail * Generic case looks more like * key1 -> something {{key2}} * key2 -> something else * that would result in * key1 -> something something else * key2 -> something else * And further * key1 -> something {{key2}} * key2 -> {{key2}} * that would result in * key1 -> something {{key2}} * key2 -> {{key2}} * See tests * <p> * Same stuff apply to iterable valorisations. The only difference is that we only allow KeyValueValorisations to be injected in iterable valorisations * otherwise it gets to complicated to figure out the result from a user perspective. */ public class MustacheScope implements Map<String, Object> { public Map<String, Object> scope = new HashMap<>(); public MustacheScope(Set<ValorisationData> valorisations){ for(ValorisationData valorisation: valorisations){ MustacheScopeEntry<String, Object> entry = valorisation.toMustacheScopeEntry(); scope.put(entry.getKey(), entry.getValue()); } } public Set<KeyValuePropertyModel> getMissingKeyValueProperties() { return getMissingKeyValueProperties(scope); } private Set<KeyValuePropertyModel> getMissingKeyValueProperties(Map<String, Object> scope){ /* Iterate through the scope. There can be string scope object or lists (depending if it comes from KeyValue or Iterable properties) */ Set<KeyValuePropertyModel> missingKeyValueProperties = new HashSet<>(); for (Map.Entry<String, Object> entry : scope.entrySet()) { Object scopeObject = entry.getValue(); if (scopeObject instanceof String) { String value = (String) scopeObject; missingKeyValueProperties.addAll(KeyValueValorisation.extractWantedValorisations(value)); } if (scopeObject instanceof List) { /* These are Lists of scopes */ List<Map<String, Object>> blocksOfvalorisations = (List) scopeObject; blocksOfvalorisations.forEach(block -> { missingKeyValueProperties.addAll(getMissingKeyValueProperties(block)); }); } } return missingKeyValueProperties; } /* Builder */ public static InjectableMustacheScope from(Set<ValorisationData> valorisations) { return new InjectableMustacheScope(new HashSet<>(valorisations)); } public static class InjectableMustacheScope { private final Set<ValorisationData> valorisations; private InjectableMustacheScope(Set<ValorisationData> valorisations) { Preconditions.checkNotNull(valorisations); this.valorisations = valorisations; } public InjectableMustacheScope inject(Map<String, String> injectedValues) { if(injectedValues == null || injectedValues.size() == 0){ return this; } Set<ValorisationData> injectedValorisations = valorisations.stream().map(valorisation -> valorisation.inject(injectedValues) ).collect(Collectors.toSet()); return new InjectableMustacheScope(injectedValorisations); } public MustacheScope create() { return new MustacheScope(valorisations); } } /* MAP implem, just link to the inner map */ @Override public int size() { return scope.size(); } @Override public boolean isEmpty() { return scope.isEmpty(); } @Override public boolean containsKey(Object key) { return scope.containsKey(key); } @Override public boolean containsValue(Object value) { return scope.containsValue(value); } @Override public Object get(Object key) { return scope.get(key); } @Override public Object put(String key, Object value) { return scope.put(key, value); } @Override public Object remove(Object key) { return scope.remove(key); } @Override public void putAll(Map<? extends String, ?> m) { scope.putAll(m); } @Override public void clear() { scope.clear(); } @Override public Set<String> keySet() { return scope.keySet(); } @Override public Collection<Object> values() { return scope.values(); } @Override public Set<Entry<String, Object>> entrySet() { return scope.entrySet(); } @Override public Object getOrDefault(Object key, Object defaultValue) { return scope.getOrDefault(key, defaultValue); } @Override public void forEach(BiConsumer<? super String, ? super Object> action) { scope.forEach(action); } @Override public void replaceAll(BiFunction<? super String, ? super Object, ?> function) { scope.replaceAll(function); } @Override public Object putIfAbsent(String key, Object value) { return scope.putIfAbsent(key, value); } @Override public boolean remove(Object key, Object value) { return scope.remove(key, value); } @Override public boolean replace(String key, Object oldValue, Object newValue) { return scope.replace(key, oldValue, newValue); } @Override public Object replace(String key, Object value) { return scope.replace(key, value); } @Override public Object computeIfAbsent(String key, Function<? super String, ?> mappingFunction) { return scope.computeIfAbsent(key, mappingFunction); } @Override public Object computeIfPresent(String key, BiFunction<? super String, ? super Object, ?> remappingFunction) { return scope.computeIfPresent(key, remappingFunction); } @Override public Object compute(String key, BiFunction<? super String, ? super Object, ?> remappingFunction) { return scope.compute(key, remappingFunction); } @Override public Object merge(String key, Object value, BiFunction<? super Object, ? super Object, ?> remappingFunction) { return scope.merge(key, value, remappingFunction); } }