package fi.vincit.jmobster.util; /* * Copyright 2012-2013 Juha Siponen * * 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. */ import fi.vincit.jmobster.JMobsterFactory; import fi.vincit.jmobster.ModelGenerator; import fi.vincit.jmobster.processor.ModelFactory; import fi.vincit.jmobster.processor.ModelProcessor; import fi.vincit.jmobster.processor.languages.LanguageContext; import fi.vincit.jmobster.processor.model.Model; import fi.vincit.jmobster.util.groups.GroupMode; import fi.vincit.jmobster.util.writer.DataWriter; import java.util.*; import java.util.concurrent.ConcurrentHashMap; /** * The purpose of model cache is to provide an easy way to generate and store generated * models dynamically by model name. One usage example could be a REST interface from which * validation rules could be queried. The interface could take the model name and give it * to a model cache. The cache then generates the model if needed or fetches it from the cache. * @param <C> {@link LanguageContext} to use * @param <W> {@link DataWriter} the LanguageContext uses */ public abstract class BaseModelCache<C extends LanguageContext<W>, W extends DataWriter> { static class ModelIdentity { public String name; public Set<Class> groups; public Class[] groupArray; public ModelIdentity(String name, Set<Class> groups) { this.name = name; this.groups = groups; // TODO Optimize array creation groupArray = new Class[groups.size()]; int i = 0; for( Class group : groups ) { groupArray[i] = group; ++i; } } @Override public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof ModelIdentity)) return false; ModelIdentity that = (ModelIdentity) o; return groups.equals(that.groups) && name.equals(that.name); } @Override public int hashCode() { int result = name.hashCode(); result = 31 * result + groups.hashCode(); return result; } @Override public String toString() { return name + ":" + groups; } } private Map<ModelIdentity, String> generatedModelsByName = new ConcurrentHashMap<ModelIdentity, String>(); private Map<String, Model> modelsByName = new ConcurrentHashMap<String, Model>(); private Map<String, Class> groupDirectory = new ConcurrentHashMap<String, Class>(); private ModelGenerator<W> modelGenerator; private ModelFactory modelFactory; private ModelProcessor<C,W> modelProcessor; private C languageContext; public BaseModelCache(ModelProcessor<C, W> modelProcessor, ModelFactory modelFactory) { this.modelGenerator = JMobsterFactory.getModelGenerator(modelProcessor); this.modelProcessor = modelProcessor; this.modelFactory = modelFactory; this.languageContext = modelProcessor.getLanguageContext(); } protected C getLanguageContext() { return languageContext; } public void addGroup(Class group, String groupName) { groupDirectory.put(groupName, group); } public String getModelByNameAndGroupNames(String name, String... groups) { Set<Class> groupSet = groupNamesToClasses(groups); return getModelInternal(new ModelIdentity(name, groupSet)); } private String getModelInternal(ModelIdentity internalModelName) { if( !generatedModelsByName.containsKey(internalModelName) ) { if( !modelsByName.containsKey(internalModelName.name) ) { throw new IllegalArgumentException("No such model: " + internalModelName); } generateModelAndAddToCache(internalModelName); } return generatedModelsByName.get(internalModelName); } private Set<Class> groupNamesToClasses(String... groups) { Set<Class> groupClasses = new HashSet<Class>(); for( String group : groups ) { if( groupDirectory.containsKey(group) ) { groupClasses.add(groupDirectory.get(group)); } else { throw new RuntimeException("Could not map " + group + " to a group"); } } return groupClasses; } public String getModelByNameAndGroupClasses(String name, Class... groups) { return getModelInternal(nameToInternalName(name, groups)); } private synchronized void generateModelAndAddToCache(ModelIdentity modelIdentity) { Model model = modelsByName.get(modelIdentity.name); assert(model != null); modelProcessor.setValidatorFilter(GroupMode.ANY_OF_REQUIRED, modelIdentity.groupArray); modelGenerator.process(model); String modelData = getLanguageContext().getWriter().toString(); generatedModelsByName.put(modelIdentity, modelData); getLanguageContext().getWriter().clear(); } public void addModels(Collection<Class> classes) { Collection<Model> models = modelFactory.createAll(classes); for( Model model : models ) { ModelIdentity modelName = nameToInternalName(model.getName()); modelsByName.put(modelName.name, model); } } private ModelIdentity nameToInternalName(String name, Class... groups) { Set<Class> groupSet = new HashSet<Class>(groups.length); Collections.addAll(groupSet, groups); return new ModelIdentity(name, groupSet); } public void clearModelCache() { generatedModelsByName.clear(); } public void clearModelCacheAndModels() { clearModelCache(); modelsByName.clear(); } public Set<String> getModelNames() { Set<String> names = new TreeSet<String>(); for( String identity : modelsByName.keySet() ) { names.add(identity); } return names; } }