├── .gitignore
├── README.md
├── Roadmap.md
├── pom.xml
└── src
├── main
└── java
│ └── org
│ └── neo4j
│ ├── kernel
│ └── RestConfig.java
│ └── rest
│ └── graphdb
│ ├── AbstractRemoteDatabase.java
│ ├── ExecutingRestRequest.java
│ ├── GraphDatabaseFactory.java
│ ├── PropertiesMap.java
│ ├── RequestResult.java
│ ├── RestAPI.java
│ ├── RestGraphDatabase.java
│ ├── RestRequest.java
│ ├── RestResultException.java
│ ├── UpdatableRestResult.java
│ ├── batch
│ ├── BatchCallback.java
│ ├── BatchIterable.java
│ ├── BatchRestAPI.java
│ ├── RecordingRestRequest.java
│ └── RestOperations.java
│ ├── converter
│ ├── RelationshipIterableConverter.java
│ ├── RestEntityExtractor.java
│ ├── RestIndexHitsConverter.java
│ ├── RestResultConverter.java
│ └── RestTableResultExtractor.java
│ ├── entity
│ ├── RestEntity.java
│ ├── RestNode.java
│ └── RestRelationship.java
│ ├── index
│ ├── IndexInfo.java
│ ├── RestIndex.java
│ ├── RestIndexManager.java
│ ├── RestNodeIndex.java
│ ├── RestRelationshipIndex.java
│ ├── RetrievedIndexInfo.java
│ └── SimpleIndexHits.java
│ ├── query
│ ├── QueryEngine.java
│ ├── RestCypherQueryEngine.java
│ └── RestGremlinQueryEngine.java
│ ├── traversal
│ ├── NodePath.java
│ ├── RelationshipPath.java
│ ├── RestDirection.java
│ ├── RestPathParser.java
│ ├── RestTraversal.java
│ ├── RestTraversalDescription.java
│ ├── RestTraverser.java
│ └── SimplePath.java
│ └── util
│ ├── ArrayConverter.java
│ ├── ConvertedResult.java
│ ├── DefaultConverter.java
│ ├── Handler.java
│ ├── JsonHelper.java
│ ├── QueryResult.java
│ ├── QueryResultBuilder.java
│ ├── ResultConverter.java
│ └── TestHelper.java
└── test
├── java
└── org
│ └── neo4j
│ └── rest
│ └── graphdb
│ ├── BatchRestAPITest.java
│ ├── IsRelationshipToNodeMatcher.java
│ ├── LocalTestServer.java
│ ├── MatrixDataGraph.java
│ ├── MatrixDatabaseRestTest.java
│ ├── MatrixDatabaseTest.java
│ ├── Neo4jDatabaseCleaner.java
│ ├── RecordingRestRequestTest.java
│ ├── RelationshipHasMatcher.java
│ ├── RestAPITest.java
│ ├── RestCypherQueryEngineTest.java
│ ├── RestEntityTest.java
│ ├── RestGraphDbTest.java
│ ├── RestGremlinQueryEngineTest.java
│ ├── RestIndexTest.java
│ ├── RestNodeTest.java
│ ├── RestTestBase.java
│ ├── RestTraversalExecutionTest.java
│ ├── RestTraversalTest.java
│ └── Type.java
└── resources
└── neo4j-server.properties
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | *.ipr
3 | *.iws
4 | *.iml
5 | *.zip
6 | target
7 | *.log
8 | tmp
9 | .idea
10 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | This repository has been deprecated, it has moved to the Neo4j organization.
2 | [Neo4j Github Repositories](http://github.com/neo4j/neo4j-java-rest-binding)
3 |
4 | The Java binding for the Neo4j Server REST API wraps the REST calls behind the well known
5 | [GraphDatabaseService](http://api.neo4j.org/1.2/org/neo4j/graphdb/GraphDatabaseService.html) API.
6 |
7 | Currently supports:
8 | ___________________
9 | * all the node and relationship operations
10 | * the new Index API
11 | * Basic Http Auth (Digest)
12 | * preliminary traversal support
13 |
14 | Open issues:
15 | ____________
16 | * full traversal support
17 | * support for exposing server extensions - via an interface based proxy
18 |
19 | Usage:
20 | ------
21 |
22 | Build it locally. Then use the maven / ivy dependency or copy the jar into your app.
23 |
24 |
25 | org.neo4j
26 | neo4j-rest-graphdb
27 | 0.1-SNAPSHOT
28 |
29 |
30 | GraphDatabaseService gds = new RestGraphDatabase(new URI("http://localhost:7474/db/data"));
31 | GraphDatabaseService gds = new RestGraphDatabase(new URI("http://localhost:7474/db/data"),username,password);
32 |
33 |
34 |
35 |
36 |
37 |
38 | **Please note: Transactions are not supported over this API.**
39 |
40 | Unit Test:
41 | ----------
42 | to start tests you will need the https://github.com/jexp/neo4j-clean-remote-db-addon to cleanup the database while
43 |
44 |
45 | References / Community:
46 | -----------------------
47 |
48 | * [Neo4j community site](http://neo4j.org)
49 | * [Neo4j REST API](http://components.neo4j.org/neo4j-server/snapshot/rest.html)
50 | * [Neo4j Wiki](http://wiki.neo4j.org)
51 | * [Neo4j Mailing List](https://lists.neo4j.org/mailman/listinfo/user)
52 |
--------------------------------------------------------------------------------
/Roadmap.md:
--------------------------------------------------------------------------------
1 |
2 | - AbstractDatabase Class as Parent for RestGraphDatabase. RGD only holds implemented methods
3 | - Simplify the RestAPI e.g. drop RestIndexManager
4 | - Allow RestTraversal with other Params as FULLPATH
5 | - Allow compact Variant of RestAPI
6 | - Evaluator should not throw UOE, make it more intelligent
7 | - Support other Scriptlanguages (e.g. test with Ruby)
8 | - Implement a Rest URI Class (static description of the neo4j protocol)
9 | - Unique Nodes - get or create
10 | - Complete RestTraversalTests (empty tests at the moment)
11 | - support batch mode via batch-command
12 | - support gremlin, cypher plugins
13 | - support Traverser and Index-Paging
14 | - support arbitrary ServerPlugins and Unmanaged Extensions
15 | - support local caching
16 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
3 | 4.0.0
4 | org.neo4j
5 | neo4j-rest-graphdb
6 | jar
7 | 0.2001-SNAPSHOT
8 | Rest GraphDatabaseService Wrapper
9 |
10 | UTF-8
11 | 1.6.1
12 | 1.4.1
13 | 1.4
14 | 0.8
15 | 1.1
16 |
17 |
18 |
19 |
20 | neo4j
21 | http://m2.neo4j.org
22 |
23 |
24 | Sun
25 | http://download.java.net/maven/2
26 |
27 |
28 |
29 |
30 | junit
31 | junit
32 | 4.8.2
33 | test
34 |
35 |
36 | org.neo4j
37 | neo4j-kernel
38 | ${neo4j.version}
39 |
40 |
41 | org.neo4j
42 | neo4j-kernel
43 | ${neo4j.version}
44 | test
45 | test-jar
46 |
47 |
48 | org.neo4j.app
49 | neo4j-server
50 | ${neo4j.version}
51 | test
52 |
53 |
54 | com.tinkerpop
55 | gremlin
56 |
57 |
58 | org.neo4j
59 | neo4j
60 |
61 |
62 | org.mortbay.jetty
63 | jetty
64 |
65 |
66 | com.sun.jersey
67 | jersey-server
68 |
69 |
70 | org.codehaus.jackson
71 | jackson-jaxrs
72 |
73 |
74 | org.codehaus.jackson
75 | jackson-mapper-asl
76 |
77 |
78 | de.huxhorn.lilith
79 | de.huxhorn.lilith.3rdparty.rrd4j
80 |
81 |
82 | com.sun.jersey.contribs
83 | jersey-multipart
84 |
85 |
86 | org.apache.felix
87 | org.apache.felix.main
88 |
89 |
90 | org.apache.felix
91 | org.apache.felix.fileinstall
92 |
93 |
94 | org.neo4j
95 | neo4j-shell
96 |
97 |
98 |
99 |
100 | org.mortbay.jetty
101 | jetty
102 | 6.1.25
103 | test
104 |
105 |
106 | org.neo4j.server.plugin
107 | neo4j-cypher-plugin
108 | ${neo4j.version}
109 | test
110 |
111 |
112 | org.neo4j.server.plugin
113 | neo4j-gremlin-plugin
114 | ${neo4j.version}
115 | test
116 |
117 |
118 | com.tinkerpop.blueprints
119 | blueprints-core
120 | ${blueprints.version}
121 | true
122 | test
123 |
124 |
125 | com.tinkerpop.blueprints
126 | blueprints-neo4j-graph
127 | ${blueprints.version}
128 | true
129 | test
130 |
131 |
132 | com.tinkerpop
133 | gremlin
134 | ${gremlin.version}
135 | true
136 |
137 |
138 | org.neo4j
139 | neo4j-lucene-index
140 | ${neo4j.version}
141 |
142 |
147 |
148 |
149 | org.codehaus.jackson
150 | jackson-jaxrs
151 | 1.6.1
152 |
153 |
154 | org.codehaus.jackson
155 | jackson-mapper-asl
156 | 1.6.1
157 |
158 |
159 | com.sun.jersey
160 | jersey-server
161 | ${jersey.version}
162 | test
163 |
164 |
165 | com.sun.jersey
166 | jersey-client
167 | ${jersey.version}
168 |
169 |
170 |
171 |
172 |
173 | maven-compiler-plugin
174 |
175 | 1.6
176 | 1.6
177 |
178 |
179 |
180 | org.apache.maven.plugins
181 | maven-eclipse-plugin
182 | 2.8
183 |
184 | 1.5
185 |
186 |
187 |
188 |
189 |
190 |
--------------------------------------------------------------------------------
/src/main/java/org/neo4j/kernel/RestConfig.java:
--------------------------------------------------------------------------------
1 | package org.neo4j.kernel;
2 |
3 | import org.neo4j.graphdb.GraphDatabaseService;
4 | import org.neo4j.kernel.impl.core.*;
5 | import org.neo4j.kernel.impl.nioneo.store.FileSystemAbstraction;
6 | import org.neo4j.kernel.impl.nioneo.store.StoreId;
7 | import org.neo4j.kernel.impl.transaction.LockManager;
8 | import org.neo4j.kernel.impl.transaction.TxModule;
9 | import org.neo4j.kernel.impl.transaction.xaframework.TxIdGenerator;
10 | import org.neo4j.rest.graphdb.RestGraphDatabase;
11 |
12 | import javax.transaction.*;
13 | import javax.transaction.xa.XAResource;
14 | import java.util.Collections;
15 | import java.util.Map;
16 |
17 | /**
18 | * @author mh
19 | * @since 23.02.11
20 | */
21 | public class RestConfig extends Config {
22 |
23 | private RestConfig(GraphDatabaseService graphDb, String storeDir, StoreId storeId, Map inputParams, KernelPanicEventGenerator kpe, TxModule txModule, LockManager lockManager, LockReleaser lockReleaser, IdGeneratorFactory idGeneratorFactory, TxEventSyncHookFactory txSyncHookFactory, RelationshipTypeCreator relTypeCreator, TxIdGenerator txIdGenerator, LastCommittedTxIdSetter lastCommittedTxIdSetter, FileSystemAbstraction fileSystem) {
24 | super(graphDb, storeDir, storeId, inputParams, kpe, txModule, lockManager, lockReleaser, idGeneratorFactory, txSyncHookFactory, relTypeCreator, txIdGenerator, lastCommittedTxIdSetter, fileSystem);
25 | }
26 |
27 | public RestConfig(RestGraphDatabase restGraphDatabase) {
28 | super(restGraphDatabase, restGraphDatabase.getStoreDir(), null,
29 | Collections.emptyMap(),null,
30 | new TxModule(true,null){
31 | @Override
32 | public TransactionManager getTxManager() {
33 | return new NullTransactionManager();
34 | }
35 | },
36 | null,null,
37 | null,null,null,null,null,
38 | null);
39 | }
40 |
41 | private static class NullTransactionManager implements TransactionManager {
42 | private static final Transaction TRANSACTION = new Transaction() {
43 | @Override
44 | public void commit() throws HeuristicMixedException, HeuristicRollbackException, RollbackException, SecurityException, SystemException {
45 |
46 | }
47 |
48 | @Override
49 | public boolean delistResource(XAResource xaResource, int i) throws IllegalStateException, SystemException {
50 | return false;
51 | }
52 |
53 | @Override
54 | public boolean enlistResource(XAResource xaResource) throws IllegalStateException, RollbackException, SystemException {
55 | return false;
56 | }
57 |
58 | @Override
59 | public int getStatus() throws SystemException {
60 | return Status.STATUS_NO_TRANSACTION;
61 | }
62 |
63 | @Override
64 | public void registerSynchronization(Synchronization synchronization) throws IllegalStateException, RollbackException, SystemException {
65 |
66 | }
67 |
68 | @Override
69 | public void rollback() throws IllegalStateException, SystemException {
70 |
71 | }
72 |
73 | @Override
74 | public void setRollbackOnly() throws IllegalStateException, SystemException {
75 |
76 | }
77 | };
78 |
79 | @Override
80 | public void begin() throws NotSupportedException, SystemException {
81 |
82 | }
83 |
84 | @Override
85 | public void commit() throws HeuristicMixedException, HeuristicRollbackException, IllegalStateException, RollbackException, SecurityException, SystemException {
86 |
87 | }
88 |
89 | @Override
90 | public int getStatus() throws SystemException {
91 | return 0;
92 | }
93 |
94 | @Override
95 | public Transaction getTransaction() throws SystemException {
96 | return TRANSACTION;
97 | }
98 |
99 | @Override
100 | public void resume(Transaction transaction) throws IllegalStateException, InvalidTransactionException, SystemException {
101 |
102 | }
103 |
104 | @Override
105 | public void rollback() throws IllegalStateException, SecurityException, SystemException {
106 |
107 | }
108 |
109 | @Override
110 | public void setRollbackOnly() throws IllegalStateException, SystemException {
111 |
112 | }
113 |
114 | @Override
115 | public void setTransactionTimeout(int i) throws SystemException {
116 |
117 | }
118 |
119 | @Override
120 | public Transaction suspend() throws SystemException {
121 | return TRANSACTION;
122 | }
123 | }
124 | }
125 |
--------------------------------------------------------------------------------
/src/main/java/org/neo4j/rest/graphdb/AbstractRemoteDatabase.java:
--------------------------------------------------------------------------------
1 | package org.neo4j.rest.graphdb;
2 |
3 | import java.io.Serializable;
4 | import java.util.Map;
5 |
6 | import org.neo4j.graphdb.Node;
7 | import org.neo4j.graphdb.RelationshipType;
8 | import org.neo4j.graphdb.Transaction;
9 | import org.neo4j.graphdb.event.KernelEventHandler;
10 | import org.neo4j.graphdb.event.TransactionEventHandler;
11 | import org.neo4j.kernel.AbstractGraphDatabase;
12 |
13 | abstract class AbstractRemoteDatabase extends AbstractGraphDatabase {
14 | public Transaction beginTx() {
15 | return new Transaction() {
16 | public void success() {
17 | }
18 |
19 | public void finish() {
20 |
21 | }
22 |
23 | public void failure() {
24 | }
25 | };
26 | }
27 |
28 | public TransactionEventHandler registerTransactionEventHandler( TransactionEventHandler tTransactionEventHandler ) {
29 | throw new UnsupportedOperationException();
30 | }
31 |
32 | public TransactionEventHandler unregisterTransactionEventHandler( TransactionEventHandler tTransactionEventHandler ) {
33 | throw new UnsupportedOperationException();
34 | }
35 |
36 | public KernelEventHandler registerKernelEventHandler( KernelEventHandler kernelEventHandler ) {
37 | throw new UnsupportedOperationException();
38 | }
39 |
40 | public KernelEventHandler unregisterKernelEventHandler( KernelEventHandler kernelEventHandler ) {
41 | throw new UnsupportedOperationException();
42 | }
43 |
44 | public boolean enableRemoteShell() {
45 | throw new UnsupportedOperationException();
46 | }
47 |
48 | public boolean enableRemoteShell( Map config ) {
49 | throw new UnsupportedOperationException();
50 | }
51 |
52 | public Iterable getAllNodes() {
53 | throw new UnsupportedOperationException();
54 | }
55 |
56 | public Iterable getRelationshipTypes() {
57 | throw new UnsupportedOperationException();
58 | }
59 |
60 | @Override
61 | public T getManagementBean(Class type) {
62 | return null;
63 | }
64 |
65 | public void shutdown() {
66 | }
67 |
68 | }
69 |
--------------------------------------------------------------------------------
/src/main/java/org/neo4j/rest/graphdb/ExecutingRestRequest.java:
--------------------------------------------------------------------------------
1 | package org.neo4j.rest.graphdb;
2 |
3 | import com.sun.jersey.api.client.Client;
4 | import com.sun.jersey.api.client.ClientResponse;
5 | import com.sun.jersey.api.client.WebResource;
6 | import com.sun.jersey.api.client.WebResource.Builder;
7 | import com.sun.jersey.api.client.filter.HTTPBasicAuthFilter;
8 |
9 | import javax.ws.rs.core.MediaType;
10 |
11 | import org.neo4j.rest.graphdb.RequestResult;
12 | import org.neo4j.rest.graphdb.util.JsonHelper;
13 |
14 |
15 | import java.io.UnsupportedEncodingException;
16 | import java.net.URI;
17 | import java.net.URISyntaxException;
18 | import java.net.URLEncoder;
19 |
20 | import java.util.concurrent.TimeUnit;
21 |
22 | public class ExecutingRestRequest implements RestRequest {
23 |
24 | public static final int CONNECT_TIMEOUT = (int) TimeUnit.SECONDS.toMillis(30);
25 | public static final int READ_TIMEOUT = (int) TimeUnit.SECONDS.toMillis(30);
26 | private final String baseUri;
27 | private final Client client;
28 |
29 | public ExecutingRestRequest( String baseUri ) {
30 | this( baseUri, null, null );
31 | }
32 |
33 | public ExecutingRestRequest( String baseUri, String username, String password ) {
34 | this.baseUri = uriWithoutSlash( baseUri );
35 | client = createClient();
36 | addAuthFilter(username, password);
37 |
38 | }
39 |
40 | protected void addAuthFilter(String username, String password) {
41 | if (username == null) return;
42 | client.addFilter( new HTTPBasicAuthFilter( username, password ) );
43 | }
44 |
45 | protected Client createClient() {
46 | Client client = Client.create();
47 |
48 | client.setConnectTimeout(CONNECT_TIMEOUT);
49 | client.setReadTimeout(READ_TIMEOUT);
50 |
51 | return client;
52 | }
53 |
54 | private ExecutingRestRequest( String uri, Client client ) {
55 | this.baseUri = uriWithoutSlash( uri );
56 | this.client = client;
57 | }
58 |
59 | protected String uriWithoutSlash( String uri ) {
60 | String uriString = uri;
61 | return (uriString.endsWith( "/" ) ? uriString.substring( 0, uriString.length() - 1 ) : uri);
62 | }
63 |
64 | public static String encode( Object value ) {
65 | if ( value == null ) return "";
66 | try {
67 | return URLEncoder.encode( value.toString(), "utf-8" ).replaceAll( "\\+", "%20" );
68 | } catch ( UnsupportedEncodingException e ) {
69 | throw new RuntimeException( e );
70 | }
71 | }
72 |
73 |
74 | private Builder builder( String path ) {
75 | WebResource resource = client.resource( uri( pathOrAbsolute( path ) ) );
76 | return resource.accept( MediaType.APPLICATION_JSON_TYPE );
77 | }
78 |
79 | private String pathOrAbsolute( String path ) {
80 | if ( path.startsWith( "http://" ) ) return path;
81 | return baseUri + "/" + path;
82 | }
83 |
84 |
85 | @Override
86 | public RequestResult get( String path ) {
87 | return RequestResult.extractFrom(builder(path).get(ClientResponse.class));
88 | }
89 |
90 |
91 | @Override
92 | public RequestResult get( String path, Object data ) {
93 | Builder builder = builder(path);
94 | if ( data != null ) {
95 | builder = builder.entity( JsonHelper.createJsonFrom( data ), MediaType.APPLICATION_JSON_TYPE );
96 | }
97 | return RequestResult.extractFrom(builder.get(ClientResponse.class));
98 | }
99 |
100 |
101 | @Override
102 | public RequestResult delete(String path) {
103 | return RequestResult.extractFrom(builder(path).delete(ClientResponse.class));
104 | }
105 |
106 |
107 | @Override
108 | public RequestResult post( String path, Object data ) {
109 | Builder builder = builder( path );
110 | if ( data != null ) {
111 | builder = builder.entity( JsonHelper.createJsonFrom( data ), MediaType.APPLICATION_JSON_TYPE );
112 | }
113 | return RequestResult.extractFrom(builder.post(ClientResponse.class));
114 | }
115 |
116 |
117 | @Override
118 | public RequestResult put( String path, Object data ) {
119 | Builder builder = builder( path );
120 | if ( data != null ) {
121 | builder = builder.entity( JsonHelper.createJsonFrom( data ), MediaType.APPLICATION_JSON_TYPE );
122 | }
123 | final ClientResponse response = builder.put(ClientResponse.class);
124 | response.close();
125 | return RequestResult.extractFrom(builder.put(ClientResponse.class));
126 | }
127 |
128 |
129 |
130 | @Override
131 | public RestRequest with( String uri ) {
132 | return new ExecutingRestRequest( uri , client );
133 | }
134 |
135 | private URI uri( String uri ) {
136 | try {
137 | return new URI( uri );
138 | } catch ( URISyntaxException e ) {
139 | throw new RuntimeException( e );
140 | }
141 | }
142 |
143 |
144 | @Override
145 | public String getUri() {
146 | return baseUri;
147 | }
148 | }
149 |
--------------------------------------------------------------------------------
/src/main/java/org/neo4j/rest/graphdb/GraphDatabaseFactory.java:
--------------------------------------------------------------------------------
1 | package org.neo4j.rest.graphdb;
2 |
3 | import org.neo4j.graphdb.GraphDatabaseService;
4 | import org.neo4j.kernel.EmbeddedGraphDatabase;
5 |
6 | import java.io.File;
7 | import java.net.URI;
8 | import java.net.URISyntaxException;
9 |
10 | /**
11 | * @author mh
12 | * @since 25.01.11
13 | */
14 | public class GraphDatabaseFactory {
15 | public static GraphDatabaseService databaseFor(String url) {
16 | return databaseFor( url, null,null );
17 | }
18 |
19 | public static GraphDatabaseService databaseFor(String url, String username, String password) {
20 | if (url.startsWith( "http://" ) || url.startsWith( "https://" )) {
21 | return new RestGraphDatabase( url , username,password );
22 | }
23 | String path=url;
24 | if (url.startsWith( "file:" )) {
25 | path = toURI( url ).getPath();
26 | }
27 | File file = new File( path );
28 | if (!file.isDirectory()) file=file.getParentFile();
29 | return new EmbeddedGraphDatabase( file.getAbsolutePath() );
30 | }
31 |
32 | private static URI toURI( String uri ) {
33 | try {
34 | return new URI(uri);
35 | } catch ( URISyntaxException e ) {
36 | throw new RuntimeException( "Error using URI "+uri, e);
37 | }
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/src/main/java/org/neo4j/rest/graphdb/PropertiesMap.java:
--------------------------------------------------------------------------------
1 | package org.neo4j.rest.graphdb;
2 |
3 | /**
4 | * @author mh
5 | * @since 13.12.10
6 | */
7 |
8 | import org.neo4j.graphdb.PropertyContainer;
9 |
10 | import java.lang.reflect.Array;
11 | import java.util.*;
12 |
13 | public class PropertiesMap {
14 |
15 | private final Map values = new HashMap();
16 |
17 | public PropertiesMap( PropertyContainer container ) {
18 | for ( String key : container.getPropertyKeys() ) {
19 | values.put( key, container.getProperty( key ) );
20 | }
21 | }
22 |
23 | public PropertiesMap( Map map ) {
24 | for ( Map.Entry entry : map.entrySet() ) {
25 | values.put( entry.getKey(), toInternalType( entry.getValue() ) );
26 | }
27 | }
28 |
29 | public Object getValue( String key ) {
30 | return values.get( key );
31 | }
32 |
33 | public Map serialize() {
34 | // TODO Nice with sorted, but TreeMap the best?
35 | Map result = new TreeMap();
36 | for ( Map.Entry entry : values.entrySet() ) {
37 | result.put( entry.getKey(), toSerializedType( entry.getValue() ) );
38 | }
39 | return result;
40 | }
41 |
42 | void storeTo( PropertyContainer container ) {
43 | for ( Map.Entry entry : values.entrySet() ) {
44 | container.setProperty( entry.getKey(), entry.getValue() );
45 | }
46 | }
47 |
48 | @SuppressWarnings("unchecked")
49 | private static Object toInternalType( Object value ) {
50 | if ( value instanceof List ) {
51 | List list = (List) value;
52 | if ( list.isEmpty() ) {
53 | return new byte[0];
54 | } else {
55 | Object first = list.get( 0 );
56 | if ( first instanceof String ) {
57 | return stringArray( list );
58 | } else if ( first instanceof Number ) {
59 | return numberArray( list );
60 | } else if ( first instanceof Boolean ) {
61 | return booleanArray( list );
62 | } else {
63 | throw new RuntimeException( "Unsupported array type " + first.getClass() +
64 | ". Supported array types are arrays of all java primitives (" +
65 | "byte[], char[], short[], int[], long[], float[], double[]) " +
66 | "and String[]" );
67 | }
68 | }
69 | } else {
70 | return assertSupportedPropertyValue( value );
71 | }
72 | }
73 |
74 | public static Object assertSupportedPropertyValue( Object value ) {
75 | if ( value == null ) {
76 | throw new RuntimeException( "null value not supported" );
77 | }
78 | final Class> type = value.getClass();
79 | if (isSupportedType(type) || type.isArray() && isSupportedType(type.getComponentType())) {
80 | return value;
81 | }
82 | throw new RuntimeException( "Unsupported value type " + type + "." +
83 | " Supported value types are all java primitives (byte, char, short, int, " +
84 | "long, float, double) and String, as well as arrays of all those types" );
85 | }
86 |
87 | private static boolean isSupportedType(Class> type) {
88 | return type.isPrimitive() || String.class.isAssignableFrom(type) || Number.class.isAssignableFrom(type) || Boolean.class.isAssignableFrom(type);
89 | }
90 |
91 | private static Boolean[] booleanArray( List list ) {
92 | return list.toArray( new Boolean[list.size()] );
93 | }
94 |
95 | private static Number[] numberArray( List numbers ) {
96 | Number[] internal = new Number[numbers.size()];
97 | for ( int i = 0; i < internal.length; i++ ) {
98 | Number number = numbers.get( i );
99 | if ( number instanceof Float || number instanceof Double ) {
100 | number = number.doubleValue();
101 | } else {
102 | number = number.longValue();
103 | }
104 | internal[i] = number;
105 | }
106 | final Number[] result;
107 | if ( internal[0] instanceof Double ) {
108 | result = new Double[internal.length];
109 | } else {
110 | result = new Long[internal.length];
111 | }
112 | System.arraycopy( internal, 0, result, 0, internal.length );
113 | return result;
114 | }
115 |
116 | private static String[] stringArray( List strings ) {
117 | return strings.toArray( new String[strings.size()] );
118 | }
119 |
120 | private Object toSerializedType( Object value ) {
121 | if ( value.getClass().isArray() ) {
122 | if ( value.getClass().getComponentType().isPrimitive() ) {
123 | int size = Array.getLength( value );
124 | List