package com.framsticks.params; import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.LogManager; import com.framsticks.params.annotations.FramsClassAnnotation; import com.framsticks.params.annotations.ParamAnnotation; import com.framsticks.util.DoubleMap; import com.framsticks.util.FramsticksException; import com.framsticks.util.lang.Casting; import com.framsticks.util.lang.Strings; import java.util.IdentityHashMap; import java.util.Map; import java.util.Set; import javax.annotation.Nonnull; /** * Author: Piotr Ĺšniegowski */ @FramsClassAnnotation public class Registry implements AccessProvider { private static final Logger log = LogManager.getLogger(Registry.class.getName()); protected final DoubleMap> javaClasses = new DoubleMap<>(); protected final DoubleMap framsClasses = new DoubleMap<>(); protected final Map, FramsClass> javaToFramsAssociation = new IdentityHashMap<>(); /** * */ public Registry() { // registerAndBuild(Registry.class); // registerAndBuild(FramsClass.class); // registerAndBuild(Param.class); } public void registerReflectedClass(String name, String id, Class javaClass) { javaClasses.put(id, name, javaClass); } public void associate(Class javaClass, FramsClass framsClass) { javaToFramsAssociation.put(javaClass, framsClass); } public Registry registerAndBuild(Class javaClass) { if (javaToFramsAssociation.containsKey(javaClass)) { return this; } register(javaClass); associate(javaClass, putFramsClass(FramsClass.build().forClass(javaClass))); for (Class r : ParamCandidate.getAllCandidates(javaClass).getDependentClasses()) { registerAndBuild(r); } for (String i : ParamCandidate.getAllCandidates(javaClass).getDependentClassesFromInfo()) { putFramsClass(FramsClassBuilder.readFromStream(getClass().getResourceAsStream("/info/" + i + ".info"))); } return this; } public FramsClass registerReflectedIfNeeded(Class javaClass) { if (!javaToFramsAssociation.containsKey(javaClass)) { registerAndBuild(javaClass); } return javaToFramsAssociation.get(javaClass); } public Registry register(Class javaClass) { FramsClassAnnotation a = javaClass.getAnnotation(FramsClassAnnotation.class); if (a == null) { throw new FramsticksException().msg("class is not annotated").arg("class", javaClass); } registerReflectedClass(FramsClassBuilder.getName(a, javaClass), FramsClassBuilder.getId(a, javaClass), javaClass); return this; } public @Nonnull ReflectionAccess createAccess(Class javaClass) throws ConstructionException { try { if (!javaClasses.containsValue(javaClass)) { registerAndBuild(javaClass); } if (!javaClasses.containsValue(javaClass)) { throw new FramsticksException().msg("java class is not registered"); } if (!javaToFramsAssociation.containsKey(javaClass)) { throw new FramsticksException().msg("java class is not associated with any frams class"); } return new ReflectionAccess(javaClass, javaToFramsAssociation.get(javaClass)); } catch (FramsticksException e) { throw new FramsticksException().msg("failed to create access for java class").arg("class", javaClass).cause(e); } } public @Nonnull Access createAccess(String name, FramsClass framsClass) throws ConstructionException { // assert framsClasses.containsValue(framsClass); if (javaClasses.containsKey(name)) { return new ReflectionAccess(javaClasses.get(name), framsClass); } return new PropertiesAccess(framsClass); } public FramsClass putFramsClass(FramsClass framsClass) { log.debug("caching info for {}", framsClass); framsClasses.put(framsClass.getId(), framsClass.getName(), framsClass); return framsClass; } public FramsClass getFramsClass(@Nonnull CompositeParam param) { return framsClasses.get(param.getContainedTypeName()); } public FramsClass getFramsClass(@Nonnull String identifier) { return framsClasses.get(identifier); } public static @Nonnull Access wrapAccessWithListIfNeeded(@Nonnull CompositeParam param, @Nonnull Access access) { return param.prepareAccess(access); } public @Nonnull Access prepareAccess(CompositeParam param, boolean force) throws ConstructionException { return wrapAccessWithListIfNeeded(param, createAccess(param.getContainedTypeName(), force)); } public @Nonnull Access createAccess(@Nonnull Object object) throws ConstructionException { if (object instanceof MapBasedObject) { return createAccess(((MapBasedObject) object).getFramsTypeName()); } return createAccess(object.getClass()); } public @Nonnull Access createAccess(@Nonnull String name, boolean force) throws ConstructionException { try { Strings.assureNotEmpty(name); FramsClass framsClass = getFramsClass(name); if (framsClass == null) { if (!force) { throw new ConstructionException().msg("framsclass is missing"); } return new FreeAccess(name); } return createAccess(name, framsClass); } catch (FramsticksException e) { throw new FramsticksException().msg("failed to create access for name").arg("name", name).cause(e); } } public @Nonnull Access createAccess(@Nonnull String name) throws ConstructionException { return createAccess(name, false); } public FramsClass getFramsClassForJavaClass(Class javaClass) { return javaToFramsAssociation.get(javaClass); } public Set> getReflectedClasses() { return javaClasses.getValues(); } public Set getFramsClasses() { return framsClasses.getValues(); } @ParamAnnotation public Map getFramsClassesById() { return framsClasses.getValuesById(); } public void takeAllFrom(Registry source) { for (Class javaClass : source.getReflectedClasses()) { register(javaClass); } for (FramsClass framsClass : source.getFramsClasses()) { putFramsClass(framsClass); } } @Override public Access getAccess(String name) { return createAccess(name); } public Access bindAccessFor(Object object) { return createAccess(object).select(object); } public Registry register(ParamsPackage paramsPackage) { paramsPackage.register(this); return this; } public Registry registerAndBuild(ParamsPackage paramsPackage) { paramsPackage.setBuild(true); paramsPackage.register(this); return this; } public Access bindAccessFor(Access parent, String paramName) { CompositeParam childParam = Casting.throwCast(CompositeParam.class, parent.getParam(paramName)); return prepareAccess(childParam, true).select(parent.get(childParam, Object.class)); } }