diff --git a/ShootingStars/src/org/wyrez/persij/NoSuitableConstructorFoundException.java b/ShootingStars/src/org/wyrez/persij/NoSuitableConstructorFoundException.java new file mode 100644 index 0000000..1714221 --- /dev/null +++ b/ShootingStars/src/org/wyrez/persij/NoSuitableConstructorFoundException.java @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2012 Darth Affe and contributors + * + * This program 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, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 . + */ +package org.wyrez.persij; + +/** + * Exception thrown by the PersiJConstructor + * + * @author Darth Affe + */ +public class NoSuitableConstructorFoundException extends Exception { + + NoSuitableConstructorFoundException() { + } + + NoSuitableConstructorFoundException(String msg) { + super(msg); + } +} diff --git a/ShootingStars/src/org/wyrez/persij/PersiJConstructor.java b/ShootingStars/src/org/wyrez/persij/PersiJConstructor.java new file mode 100644 index 0000000..11813f7 --- /dev/null +++ b/ShootingStars/src/org/wyrez/persij/PersiJConstructor.java @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2012 Darth Affe and contributors + * + * This program 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, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 . + */ +package org.wyrez.persij; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Marks an Constructor for primary use by the PersiJConstructor + * + * @author Darth Affe + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.CONSTRUCTOR) +public @interface PersiJConstructor { + + /** + * Returns the default state of the annotation + * + * @return true if default; false otherwise + */ + boolean isDefault(); + + /** + * Returns the id of the param mapping + * + * @return id of the param mapping + */ + int customParamMappingId() default -1; +} diff --git a/ShootingStars/src/org/wyrez/persij/PersiJContainer.java b/ShootingStars/src/org/wyrez/persij/PersiJContainer.java new file mode 100644 index 0000000..33a0b9a --- /dev/null +++ b/ShootingStars/src/org/wyrez/persij/PersiJContainer.java @@ -0,0 +1,172 @@ +/* + * Copyright (C) 2012 Darth Affe and contributors + * + * This program 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, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 . + */ +package org.wyrez.persij; + +import java.lang.reflect.Array; +import java.util.HashMap; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * Mapping and object container for PersiJ + * + * @author Darth Affe + */ +public final class PersiJContainer { + + private static final Logger logger = Logger.getLogger("org.wyrez.persij"); + private final HashMap mappings = new HashMap(); + + public PersiJContainer() { + registerSingleton(PersiJContainer.class, this); + } + + /** + * Registers a mapping + * + * @param type Type which should be mapped + */ + public final void registerType(Class type) { + registerType(type, false); + } + + /** + * Registers a mapping + * + * @param type Type which should be mapped + * @param singleton Singleton indicator + */ + public final void registerType(Class type, boolean singleton) { + registerType(type, type, singleton); + } + + /** + * Registers a mapping + * + * @param type Source-type which should be mapped + * @param mapping Target-type which should be mapped + */ + public final void registerType(Class type, Class mapping) { + registerType(type, mapping, false); + } + + /** + * Registers a mapping + * + * @param type Source-type which should be mapped + * @param mapping Target-type which should be mapped + * @param singleton Singleton indicator + */ + public final void registerType(Class type, Class mapping, boolean singleton) { + mappings.put(type, new PersiJMapping(mapping, singleton)); + } + + /** + * Registers a singleton-mapping to an existing object + * + * @param type Source-type which should be mapped + * @param instance Singleton instance + */ + public final void registerSingleton(Class type, T instance) { + mappings.put(type, new PersiJMapping(type, instance)); + } + + /** + * Tests if the specified type is registered + * + * @param type Type which should be tested + * @return true if the type is registered; else otherwise + */ + public final boolean isTypeRegistered(Class type) { + return mappings.containsKey(type); + } + + /** + * Creates an instance of the type mapped to the given type + * + * @param Type to resolve + * @param type Class of the type to resolve + * @return Insance of T + */ + public final T resolve(Class type) { + PersiJMapping mapping = mappings.get(type); + if (mapping != null) { + try { + return (T) mapping.getInstance(this, null); + } catch (NoSuitableConstructorFoundException ex) { + logger.log(Level.WARNING, "Can't create object for type: " + + type.getName(), ex); + return null; + } + } else { + logger.log(Level.WARNING, "Can't find a mapping for type: " + + type.getName()); + return null; + } + } + + /** + * Creates an instance of the type mapped to the given type + * + * @param Type to resolve + * @param type Class of the type to resolve + * @param paramMapping + * @return Instance of T + */ + public final T resolve(Class type, PersiJParamMapping paramMapping) { + PersiJMapping mapping = mappings.get(type); + if (mapping != null) { + try { + return (T) mapping.getInstance(this, paramMapping); + } catch (NoSuitableConstructorFoundException ex) { + logger.log(Level.WARNING, "Can't create object for type: " + + type.getName(), ex); + return null; + } + } else { + logger.log(Level.WARNING, "Can't find a mapping for type: " + + type.getName()); + return null; + } + } + + /** + * Creates an array with instances of the type mapped to the given type + * + * @param Type to resolve + * @param type Class of the type to resolve + * @param number Size of the array + * @return Array of T + */ + public final T[] resolve(Class type, int number) { + PersiJMapping mapping = mappings.get(type); + if (mapping != null) { + try { + T[] array = (T[]) Array.newInstance(type, number); + return (T[]) mapping.getInstances(this, null, array); + } catch (NoSuitableConstructorFoundException ex) { + logger.log(Level.WARNING, "Can't create object for type: " + + type.getName(), ex); + return null; + } + } else { + logger.log(Level.WARNING, "Can't find a mapping for type: " + + type.getName()); + return null; + } + } +} diff --git a/ShootingStars/src/org/wyrez/persij/PersiJMapping.java b/ShootingStars/src/org/wyrez/persij/PersiJMapping.java new file mode 100644 index 0000000..a00162c --- /dev/null +++ b/ShootingStars/src/org/wyrez/persij/PersiJMapping.java @@ -0,0 +1,208 @@ +/* + * Copyright (C) 2012 Darth Affe and contributors + * + * This program 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, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 . + */ +package org.wyrez.persij; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Constructor; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * + * @author Darth Affe + */ +final class PersiJMapping { + + private static final Logger logger = Logger.getLogger("org.wyrez.persij"); + private Object singletonInstance; + private Class type; + private boolean singleton; + + PersiJMapping(Class type, boolean singleton) { + this.type = type; + this.singleton = singleton; + } + + PersiJMapping(Class type, Object singleton) { + this.type = type; + this.singleton = true; + singletonInstance = singleton; + } + + final boolean isSingleton() { + return singleton; + } + + /** + * Creates a new instance + * + * @param container Container to resolve missing dependencies + * @param paramMapping Optional parameters used to resolve dependencies + * (null if not used) + * @return New instance + * @throws NoSuitableConstructorFoundException thrown if no suitable + * constructor to instantiate could be found + */ + final Object getInstance(PersiJContainer container, PersiJParamMapping paramMapping) throws NoSuitableConstructorFoundException { + if (isSingleton()) { + if (singletonInstance == null) { + singletonInstance = createInstance(container, paramMapping); + } + return singletonInstance; + } else { + return createInstance(container, paramMapping); + } + } + + /** + * Creates an array with new instances + * + * @param container Container to resolve missing dependencies + * @param paramMapping Optional parameters used to resolve dependencies + * (null if not used) + * @param array Array which should be filled and returned + * @return New array with instances + * @throws NoSuitableConstructorFoundException thrown if no suitable + * constructor to instantiate could be found + */ + final Object getInstances(PersiJContainer container, PersiJParamMapping paramMapping, Object[] array) throws NoSuitableConstructorFoundException { + if (isSingleton()) { + if (singletonInstance == null) { + singletonInstance = createInstance(container, paramMapping); + } + for (int i = 0; i < array.length; i++) { + array[i] = singletonInstance; + } + return array; + } else { + return createInstances(container, paramMapping, array); + } + } + + /** + * Creates a new instance + * + * @param container Container to resolve missing dependencies + * @param paramMapping Optional parameters used to resolve dependencies + * (null if not used) + * @return New instance + * @throws NoSuitableConstructorFoundException thrown if no suitable + * constructor to instantiate could be found + */ + private Object createInstance(PersiJContainer container, PersiJParamMapping paramMapping) throws NoSuitableConstructorFoundException { + Constructor constructor = getBestConstructor(container, paramMapping); + return createInstance(container, constructor, paramMapping); + } + + /** + * Creates an array with new instances + * + * @param container Container to resolve missing dependencies + * @param paramMapping Optional parameters used to resolve dependencies + * (null if not used) + * @param array Array which should be filled and returned + * @return New array with instances + * @throws NoSuitableConstructorFoundException thrown if no suitable + * constructor to instantiate could be found + */ + private Object createInstances(PersiJContainer container, PersiJParamMapping paramMapping, Object[] array) throws NoSuitableConstructorFoundException { + Constructor constructor = getBestConstructor(container, paramMapping); + for (int i = 0; i < array.length; i++) { + array[i] = createInstance(container, constructor, paramMapping); + } + return array; + } + + /** + * Creates an instance using the given constructor + * + * @param container Container to resolve missing dependencies + * @param constructor Constructor to instantiate + * @param paramMapping Optional parameters used to resolve dependencies + * (null if not used) + * @return New instance + * @throws NoSuitableConstructorFoundException thrown if no suitable + * constructor to instantiate could be found + */ + private Object createInstance(PersiJContainer container, Constructor constructor, PersiJParamMapping paramMapping) throws NoSuitableConstructorFoundException { + Object[] params; + if (paramMapping != null && paramMapping.getType() == type) { + params = paramMapping.getParams(); + } else { + Class[] conParams = constructor.getParameterTypes(); + int paramCount = conParams.length; + params = new Object[paramCount]; + for (int i = 0; i < paramCount; i++) { + params[i] = container.resolve(conParams[i], paramMapping); + } + } + try { + return constructor.newInstance(params); + } catch (Exception ex) { + logger.log(Level.WARNING, "Error while creating instance of an object", ex); + return null; + } + } + + /** + * Determines the best constructor to instantiate + * + * @param container Container to check registered types + * @param paramMapping Optional parameters used to resolve dependencies + * (null if not used) + * @return Best constructor to instantiate + * @throws NoSuitableConstructorFoundException thrown if no suitable + * constructor to instantiate could be found + */ + private Constructor getBestConstructor(PersiJContainer container, PersiJParamMapping paramMapping) throws NoSuitableConstructorFoundException { + Constructor bestToCreate = null; + int paramCount = -1; + for (Constructor c : type.getConstructors()) { + Annotation annotation = c.getAnnotation(PersiJConstructor.class); + if (paramMapping != null && paramMapping.getType() == type) { + if (annotation != null && ((PersiJConstructor) annotation).customParamMappingId() == paramMapping.getParamMappingId()) { + bestToCreate = c; + break; + } + } else { + boolean possible = true; + for (Class t : c.getParameterTypes()) { + if (t.isPrimitive() || t == String.class || t == Object.class || !container.isTypeRegistered(t)) { + possible = false; + break; + } + } + if (possible) { + int pCount = c.getParameterTypes().length; + if (annotation != null) { + if (((PersiJConstructor) annotation).isDefault()) { + bestToCreate = c; + break; + } + } else if (pCount > paramCount) { + bestToCreate = c; + paramCount = pCount; + } + } + } + } + if (bestToCreate == null) { + throw new NoSuitableConstructorFoundException(); + } + return bestToCreate; + } +} diff --git a/ShootingStars/src/org/wyrez/persij/PersiJParamMapping.java b/ShootingStars/src/org/wyrez/persij/PersiJParamMapping.java new file mode 100644 index 0000000..0c446c3 --- /dev/null +++ b/ShootingStars/src/org/wyrez/persij/PersiJParamMapping.java @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2012 Darth Affe and contributors + * + * This program 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, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 . + */ +package org.wyrez.persij; + +/** + * + * @author Darth Affe + */ +public final class PersiJParamMapping { + + private Class type; + private Object[] params; + private int paramMappingId; + + public PersiJParamMapping(Class type, Object... params) { + this(-1, type, params); + } + + public PersiJParamMapping(int paramMappingId, Class type, Object... params) { + this.type = type; + this.params = params; + this.paramMappingId = paramMappingId; + } + + /** + * Returns the Type to which the ParamMapping belongs + * + * @return Type of the ParamMapping + */ + public final Class getType() { + return type; + } + + /** + * Returns the mapped parameters + * + * @return Array with mapped parameters + */ + public final Object[] getParams() { + return params; + } + + /** + * Returns the id of the mapping + * + * @return id of the mapping + */ + public final int getParamMappingId() { + return paramMappingId; + } +}