Discussion:
[picocontainer-scm] [scm-git][1/4] Added JSON pico container
Serban Iordache
2013-12-25 20:27:26 UTC
Permalink
commit 6e17359d72a53d9f66aa9136f8bbf7055616e9d1
Author: Serban Iordache <iordache-***@public.gmane.org>
AuthorDate: Sat, 14 Dec 2013 03:33:22 +0100
Commit: Serban Iordache <iordache-***@public.gmane.org>
CommitDate: Sat, 14 Dec 2013 03:33:22 +0100

Added JSON pico container

diff --git a/pico/gems/pom.xml b/pico/gems/pom.xml
index 9d41212..576d5c8 100644
--- a/pico/gems/pom.xml
+++ b/pico/gems/pom.xml
@@ -58,6 +58,11 @@
<groupId>mx4j</groupId><artifactId>mx4j-impl</artifactId><version>2.1.1</version>
<optional>true</optional>
</dependency>
+ <!-- For JsonPicoContainer -->
+ <dependency>
+ <groupId>com.google.code.gson</groupId><artifactId>gson</artifactId><version>2.2.4</version>
+ <optional>true</optional>
+ </dependency>
<!-- For test -->
<dependency>
<groupId>com.picocontainer</groupId><artifactId>picocontainer-tck</artifactId>
diff --git a/pico/gems/src/java/com/picocontainer/gems/containers/JsonPicoContainer.java b/pico/gems/src/java/com/picocontainer/gems/containers/JsonPicoContainer.java
new file mode 100644
index 0000000..21aa14a
--- /dev/null
+++ b/pico/gems/src/java/com/picocontainer/gems/containers/JsonPicoContainer.java
@@ -0,0 +1,223 @@
+/*****************************************************************************
+ * Copyright (C) 2003-2013 PicoContainer Committers. All rights reserved. *
+ * ------------------------------------------------------------------------- *
+ * The software in this package is published under the terms of the BSD *
+ * style license a copy of which has been included with this distribution in *
+ * the LICENSE.txt file. *
+ * *
+ * Original code by Serban Iordache *
+ *****************************************************************************/
+package com.picocontainer.gems.containers;
+
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.Reader;
+import java.util.ArrayList;
+import java.util.List;
+
+import com.google.gson.Gson;
+import com.picocontainer.DefaultPicoContainer;
+import com.picocontainer.MutablePicoContainer;
+import com.picocontainer.Parameter;
+import com.picocontainer.PicoException;
+import com.picocontainer.containers.AbstractDelegatingMutablePicoContainer;
+import com.picocontainer.parameters.ComponentParameter;
+import com.picocontainer.parameters.ConstantParameter;
+import com.picocontainer.parameters.DefaultConstructorParameter;
+import com.picocontainer.parameters.NullParameter;
+
+/**
+ * A mutable container that populates itself by means of a configuration file in JSON format.
+ *
+ * <p/><b>Usage example</b>
+ * <br/>Consider the following code fragment:
+ * <pre>
+ * public class Banner extends JFrame {
+ * public Banner(JTextComponent textComp, String text, int size, boolean bold) {
+ * textComp.setText(text);
+ * textComp.setFont(new Font("Arial", (bold ? Font.BOLD : Font.PLAIN), size));
+ * add(textComp);
+ * setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
+ * pack();
+ * }
+ *
+ * public static void main(String[] args) {
+ * MutablePicoContainer pico = new DefaultPicoContainer();
+ * pico.addComponent("textComp", JTextArea.class, DefaultConstructorParameter.INSTANCE);
+ * pico.addComponent("banner", Banner.class,
+ * new ComponentParameter("textComp"),
+ * new ConstantParameter("Hello, world!"),
+ * new ComponentParameter("size"),
+ * new ComponentParameter("bold"));
+ * pico.addConfig("size", 36);
+ * pico.addConfig("bold", true);
+ *
+ * Banner banner = (Banner)pico.getComponent("banner");
+ * banner.setVisible(true);
+ * }
+ * }
+ * </pre>
+ *
+ * In the main method above, the pico container is populated programmatically. Using {@link JsonPicoContainer}, this can be done by means of a JSON configuration file.
+ * <br>Content of the file <b>banner.json</b>:<pre>
+ * [
+ * {key: textComp, impl: javax.swing.JTextArea, parameters: [{}]},
+ * {key: banner, impl: com.picocontainer.gems.containers.Banner, parameters: [{key: textComp}, {value: "Hello, world"}, {key: size}, {key: bold}]},
+ * {key: size, value: 36, type: int},
+ * {key: bold, value: true, type: boolean}
+ * ]
+ * </pre>
+ *
+ * Now, the main method can be rewritten as:
+ *
+ * <pre>
+ * public static void main(String[] args) {
+ * MutablePicoContainer pico = new JsonPicoContainer("banner.json");
+ *
+ * Banner banner = (Banner)pico.getComponent("banner");
+ * banner.setVisible(true);
+ * }
+ * </pre>
+ *
+ * @author Serban Iordache
+ */
+public class JsonPicoContainer extends AbstractDelegatingMutablePicoContainer {
+ private static final long serialVersionUID = 1L;
+
+ private static class KeyOrValue {
+ String key;
+ String value;
+ String type;
+
+ @Override
+ public String toString() {
+ return "(key: " + key + ", value: " + value + ", type: " + type + ")";
+ }
+ }
+
+ private static class Component {
+ String key;
+ String value;
+ String type;
+ String impl;
+ List<KeyOrValue> parameters;
+
+ @Override
+ public String toString() {
+ return "{key: " + key + ", value: " + value + ", type: " + type + ", impl: " + impl + ", parameters: " + parameters + "}";
+ }
+ }
+
+ private static class Config extends ArrayList<Component> {
+ private static final long serialVersionUID = 1L;
+ }
+
+ /**
+ * Convenience constructor that creates a JSON container with no parent and populates it with data from a file
+ *
+ * @param jsonFileName - the path to the file containing the json configuration
+ * @throws FileNotFoundException
+ */
+ public JsonPicoContainer(String jsonFileName) throws FileNotFoundException {
+ this(new FileReader(jsonFileName), null);
+ }
+
+ /**
+ * Creates a JSON container with no parent and populates it with data from a file
+ *
+ * @param jsonReader
+ * @param parent
+ */
+ public JsonPicoContainer(Reader jsonReader, MutablePicoContainer parent) {
+ super(new DefaultPicoContainer(parent));
+
+ MutablePicoContainer pico = getDelegate();
+ Gson gson = new Gson();
+ Config config;
+ try {
+ config = gson.fromJson(jsonReader, Config.class);
+ for(Component comp : config) {
+ if(comp == null) continue;
+ if(comp.value == null) {
+ if(comp.impl == null) throw new PicoJsonException("Either value or impl must be set in: " + comp);
+ Class<?> implClass = Class.forName(comp.impl);
+ Parameter[] parameters = null;
+ if(comp.parameters != null && !comp.parameters.isEmpty()) {
+ int size = comp.parameters.size();
+ parameters = new Parameter[size];
+ for(int i=0; i<size; i++) {
+ parameters[i] = getParameter(comp.parameters.get(i));
+ }
+ }
+ if(comp.key == null) {
+ if(parameters != null) throw new PicoJsonException("No parameters are allowed when key=null in: " + comp);
+ pico.addComponent(implClass);
+ } else {
+ pico.addComponent(comp.key, implClass, parameters);
+ }
+ } else {
+ if(comp.impl != null) throw new PicoJsonException("It is not allowed to set both value and impl in: " + comp);
+ if(comp.key == null) throw new PicoJsonException("key must be set in: " + comp);
+ Object value = getValue(comp.value, comp.type);
+ pico.addConfig(comp.key, value);
+ }
+ }
+ } catch(Exception e) {
+ if(e instanceof PicoException) throw (PicoException)e;
+ else throw new PicoJsonException(e);
+ }
+ }
+
+ public JsonPicoContainer(Reader jsonReader) {
+ this(jsonReader, null);
+ }
+
+ @Override
+ public void setName(String s) {
+ ((DefaultPicoContainer)getDelegate()).setName(s);
+ }
+
+ private static Parameter getParameter(KeyOrValue keyOrValue) {
+ if(keyOrValue == null) return NullParameter.INSTANCE;
+ if(keyOrValue.key == null && keyOrValue.value == null && keyOrValue.type == null) return DefaultConstructorParameter.INSTANCE;
+ if(keyOrValue.key == null) {
+ if(keyOrValue.value == null) throw new PicoJsonException("Either value or key must be set in: " + keyOrValue);
+ Object value = getValue(keyOrValue.value, keyOrValue.type);
+ if("class".equals(keyOrValue.type)) return new ComponentParameter(value);
+ else return new ConstantParameter(value);
+
+ } else {
+ if(keyOrValue.value != null) throw new PicoJsonException("It is not allowed to set both value and key in: " + keyOrValue);
+ return new ComponentParameter(keyOrValue.key);
+ }
+ }
+
+ private static Object getValue(String valueAsString, String type) {
+ if(type == null) type = "string";
+ if("string".equals(type)) return valueAsString;
+ if("int".equals(type)) return Integer.valueOf(valueAsString);
+ if("long".equals(type)) return Long.valueOf(valueAsString);
+ if("float".equals(type)) return Float.valueOf(valueAsString);
+ if("double".equals(type)) return Double.valueOf(valueAsString);
+ if("boolean".equals(type)) return Boolean.valueOf(valueAsString);
+ if("class".equals(type)) {
+ try {
+ return Class.forName(valueAsString);
+ } catch(ClassNotFoundException e) {
+ throw new PicoJsonException(e);
+ }
+ }
+ throw new PicoJsonException("Unsupported type: " + type + " for value: " + valueAsString);
+ }
+
+
+ @Override
+ public String toString() {
+ return "[Json]:" + getDelegate().toString();
+ }
+
+ @Override
+ public MutablePicoContainer makeChildContainer() {
+ return getDelegate().makeChildContainer();
+ }
+}
diff --git a/pico/gems/src/java/com/picocontainer/gems/containers/PicoJsonException.java b/pico/gems/src/java/com/picocontainer/gems/containers/PicoJsonException.java
new file mode 100644
index 0000000..a402121
--- /dev/null
+++ b/pico/gems/src/java/com/picocontainer/gems/containers/PicoJsonException.java
@@ -0,0 +1,32 @@
+/*****************************************************************************
+ * Copyright (C) 2003-2013 PicoContainer Committers. All rights reserved. *
+ * ------------------------------------------------------------------------- *
+ * The software in this package is published under the terms of the BSD *
+ * style license a copy of which has been included with this distribution in *
+ * the LICENSE.txt file. *
+ * *
+ * Original code by Serban Iordache *
+ *****************************************************************************/
+package com.picocontainer.gems.containers;
+
+import com.picocontainer.PicoException;
+
+public class PicoJsonException extends PicoException {
+ private static final long serialVersionUID = 1L;
+
+ public PicoJsonException() {
+ super();
+ }
+
+ public PicoJsonException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public PicoJsonException(String message) {
+ super(message);
+ }
+
+ public PicoJsonException(Throwable cause) {
+ super(cause);
+ }
+}
\ No newline at end of file
diff --git a/pico/gems/src/test/com/picocontainer/gems/containers/AbsProcessor.java b/pico/gems/src/test/com/picocontainer/gems/containers/AbsProcessor.java
new file mode 100644
index 0000000..be94c4f
--- /dev/null
+++ b/pico/gems/src/test/com/picocontainer/gems/containers/AbsProcessor.java
@@ -0,0 +1,39 @@
+/*****************************************************************************
+ * Copyright (C) 2003-2013 PicoContainer Committers. All rights reserved. *
+ * ------------------------------------------------------------------------- *
+ * The software in this package is published under the terms of the BSD *
+ * style license a copy of which has been included with this distribution in *
+ * the LICENSE.txt file. *
+ * *
+ * Original code by Serban Iordache *
+ *****************************************************************************/
+package com.picocontainer.gems.containers;
+
+public class AbsProcessor implements Processor {
+ private final Processor delegate;
+
+ public AbsProcessor(Processor delegate) {
+ this.delegate = delegate;
+ }
+
+ @Override
+ public void startProcessing() {
+ delegate.startProcessing();
+ }
+
+ @Override
+ public void processElement(int element) {
+ delegate.processElement(Math.abs(element));
+ }
+
+ @Override
+ public void terminateProcessing() {
+ delegate.terminateProcessing();
+ }
+
+ @Override
+ public int getResult() {
+ return delegate.getResult();
+ }
+
+}
diff --git a/pico/gems/src/test/com/picocontainer/gems/containers/JsonPicoContainerTest.java b/pico/gems/src/test/com/picocontainer/gems/containers/JsonPicoContainerTest.java
new file mode 100644
index 0000000..6dfce53
--- /dev/null
+++ b/pico/gems/src/test/com/picocontainer/gems/containers/JsonPicoContainerTest.java
@@ -0,0 +1,37 @@
+/*****************************************************************************
+ * Copyright (C) 2003-2013 PicoContainer Committers. All rights reserved. *
+ * ------------------------------------------------------------------------- *
+ * The software in this package is published under the terms of the BSD *
+ * style license a copy of which has been included with this distribution in *
+ * the LICENSE.txt file. *
+ * *
+ * Original code by Serban Iordache *
+ *****************************************************************************/
+package com.picocontainer.gems.containers;
+
+import static org.junit.Assert.assertEquals;
+
+import java.io.InputStreamReader;
+
+import org.junit.Test;
+
+import com.picocontainer.MutablePicoContainer;
+
+public class JsonPicoContainerTest {
+ public int getResult(String jsonFileName) {
+ InputStreamReader reader = new InputStreamReader(JsonPicoContainerTest.class.getResourceAsStream(jsonFileName));
+ MutablePicoContainer pico = new JsonPicoContainer(reader);
+ SeriesHandler handler = (SeriesHandler)pico.getComponent("handler");
+ if(handler == null) throw new PicoJsonException("handler is null.");
+ handler.handleSeries(1, -2, 3, -4, 5);
+ return handler.getResult();
+ }
+
+ @Test public void test1() { assertEquals(3, getResult("cfg1.json")); }
+
+ @Test public void test2() { assertEquals(6, getResult("cfg2.json")); }
+
+ @Test public void test3() { assertEquals(9, getResult("cfg3.json")); }
+
+ @Test public void test4() { assertEquals(60, getResult("cfg4.json")); }
+}
diff --git a/pico/gems/src/test/com/picocontainer/gems/containers/Processor.java b/pico/gems/src/test/com/picocontainer/gems/containers/Processor.java
new file mode 100644
index 0000000..ecd9801
--- /dev/null
+++ b/pico/gems/src/test/com/picocontainer/gems/containers/Processor.java
@@ -0,0 +1,17 @@
+/*****************************************************************************
+ * Copyright (C) 2003-2013 PicoContainer Committers. All rights reserved. *
+ * ------------------------------------------------------------------------- *
+ * The software in this package is published under the terms of the BSD *
+ * style license a copy of which has been included with this distribution in *
+ * the LICENSE.txt file. *
+ * *
+ * Original code by Serban Iordache *
+ *****************************************************************************/
+package com.picocontainer.gems.containers;
+
+public interface Processor {
+ void startProcessing();
+ void processElement(int element);
+ void terminateProcessing();
+ int getResult();
+}
diff --git a/pico/gems/src/test/com/picocontainer/gems/containers/SeriesHandler.java b/pico/gems/src/test/com/picocontainer/gems/containers/SeriesHandler.java
new file mode 100644
index 0000000..e088d37
--- /dev/null
+++ b/pico/gems/src/test/com/picocontainer/gems/containers/SeriesHandler.java
@@ -0,0 +1,36 @@
+/*****************************************************************************
+ * Copyright (C) 2003-2013 PicoContainer Committers. All rights reserved. *
+ * ------------------------------------------------------------------------- *
+ * The software in this package is published under the terms of the BSD *
+ * style license a copy of which has been included with this distribution in *
+ * the LICENSE.txt file. *
+ * *
+ * Original code by Serban Iordache *
+ *****************************************************************************/
+package com.picocontainer.gems.containers;
+
+public class SeriesHandler {
+ private final Processor processor;
+ private final int multiplier;
+
+ public SeriesHandler(Processor processor) {
+ this(processor, 1);
+ }
+
+ public SeriesHandler(Processor processor, int multiplier) {
+ this.processor = processor;
+ this.multiplier = multiplier;
+ }
+
+ public void handleSeries(int... elements) {
+ processor.startProcessing();
+ for(int element : elements) {
+ processor.processElement(element);
+ }
+ processor.terminateProcessing();
+ }
+
+ public int getResult() {
+ return multiplier * processor.getResult();
+ }
+}
diff --git a/pico/gems/src/test/com/picocontainer/gems/containers/SumProcessor.java b/pico/gems/src/test/com/picocontainer/gems/containers/SumProcessor.java
new file mode 100644
index 0000000..d4bd678
--- /dev/null
+++ b/pico/gems/src/test/com/picocontainer/gems/containers/SumProcessor.java
@@ -0,0 +1,35 @@
+/*****************************************************************************
+ * Copyright (C) 2003-2013 PicoContainer Committers. All rights reserved. *
+ * ------------------------------------------------------------------------- *
+ * The software in this package is published under the terms of the BSD *
+ * style license a copy of which has been included with this distribution in *
+ * the LICENSE.txt file. *
+ * *
+ * Original code by Serban Iordache *
+ *****************************************************************************/
+package com.picocontainer.gems.containers;
+
+public class SumProcessor implements Processor {
+ private int result;
+
+ @Override
+ public void startProcessing() {
+ result = 0;
+ }
+
+ @Override
+ public void processElement(int element) {
+ result += element;
+ }
+
+ @Override
+ public int getResult() {
+ return result;
+ }
+
+ @Override
+ public void terminateProcessing() {
+ // Empty implementation
+ }
+
+}
diff --git a/pico/gems/src/test/com/picocontainer/gems/containers/cfg1.json b/pico/gems/src/test/com/picocontainer/gems/containers/cfg1.json
new file mode 100644
index 0000000..4e84a11
--- /dev/null
+++ b/pico/gems/src/test/com/picocontainer/gems/containers/cfg1.json
@@ -0,0 +1,4 @@
+[
+ {impl: com.picocontainer.gems.containers.SumProcessor},
+ {key: handler, impl: com.picocontainer.gems.containers.SeriesHandler},
+]
diff --git a/pico/gems/src/test/com/picocontainer/gems/containers/cfg2.json b/pico/gems/src/test/com/picocontainer/gems/containers/cfg2.json
new file mode 100644
index 0000000..4142ba9
--- /dev/null
+++ b/pico/gems/src/test/com/picocontainer/gems/containers/cfg2.json
@@ -0,0 +1,4 @@
+[
+ {key: processor, impl: com.picocontainer.gems.containers.SumProcessor},
+ {key: handler, impl: com.picocontainer.gems.containers.SeriesHandler, parameters: [{key: processor}, {value: 2, type: int}]},
+]
diff --git a/pico/gems/src/test/com/picocontainer/gems/containers/cfg3.json b/pico/gems/src/test/com/picocontainer/gems/containers/cfg3.json
new file mode 100644
index 0000000..7070a0c
--- /dev/null
+++ b/pico/gems/src/test/com/picocontainer/gems/containers/cfg3.json
@@ -0,0 +1,5 @@
+[
+ {impl: com.picocontainer.gems.containers.SumProcessor},
+ {key: handler, impl: com.picocontainer.gems.containers.SeriesHandler, parameters: [{value: com.picocontainer.gems.containers.SumProcessor, type: class}, {key: multiplier}]},
+ {key: multiplier, value: 3, type: int},
+]
diff --git a/pico/gems/src/test/com/picocontainer/gems/containers/cfg4.json b/pico/gems/src/test/com/picocontainer/gems/containers/cfg4.json
new file mode 100644
index 0000000..36dab1c
--- /dev/null
+++ b/pico/gems/src/test/com/picocontainer/gems/containers/cfg4.json
@@ -0,0 +1,6 @@
+[
+ {key: baseProcessor, impl: com.picocontainer.gems.containers.SumProcessor},
+ {key: processor, impl: com.picocontainer.gems.containers.AbsProcessor, parameters: [{key: baseProcessor}]},
+ {key: handler, impl: com.picocontainer.gems.containers.SeriesHandler, parameters: [{key: processor}, {key: multiplier}]},
+ {key: multiplier, value: 4, type: int},
+]

Loading...