View Javadoc

1   /*
2    * Copyright (c) 2007, James Leigh All rights reserved.
3    * 
4    * Redistribution and use in source and binary forms, with or without
5    * modification, are permitted provided that the following conditions are met:
6    * 
7    * - Redistributions of source code must retain the above copyright notice, this
8    *   list of conditions and the following disclaimer.
9    * - Redistributions in binary form must reproduce the above copyright notice,
10   *   this list of conditions and the following disclaimer in the documentation
11   *   and/or other materials provided with the distribution. 
12   * - Neither the name of the openrdf.org nor the names of its contributors may
13   *   be used to endorse or promote products derived from this software without
14   *   specific prior written permission.
15   * 
16   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17   * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18   * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19   * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
20   * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21   * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22   * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23   * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24   * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26   * POSSIBILITY OF SUCH DAMAGE.
27   * 
28   */
29  package org.openrdf.elmo.dynabean;
30  
31  import java.util.Collection;
32  import java.util.HashMap;
33  import java.util.List;
34  import java.util.Map;
35  import java.util.Set;
36  import java.util.WeakHashMap;
37  
38  import org.apache.commons.beanutils.DynaBean;
39  import org.apache.commons.beanutils.DynaClass;
40  import org.openrdf.elmo.Entity;
41  import org.openrdf.elmo.dynabean.helpers.DynaClassInfo;
42  import org.openrdf.elmo.dynabean.helpers.DynaClassInfoFactory;
43  import org.openrdf.elmo.exceptions.ElmoIOException;
44  import org.openrdf.elmo.exceptions.ElmoPersistException;
45  import org.openrdf.elmo.sesame.SesameManager;
46  import org.openrdf.elmo.sesame.SesameProperty;
47  import org.openrdf.elmo.sesame.helpers.PropertyChanger;
48  import org.openrdf.elmo.sesame.roles.SesameEntity;
49  import org.openrdf.model.BNode;
50  import org.openrdf.model.Resource;
51  import org.openrdf.model.URI;
52  import org.openrdf.model.ValueFactory;
53  import org.openrdf.model.vocabulary.RDF;
54  import org.openrdf.repository.Repository;
55  import org.openrdf.repository.RepositoryException;
56  import org.openrdf.repository.contextaware.ContextAwareConnection;
57  
58  /**
59   * DynaBean interface for Elmo JavaBeans. Property names can be in one of three
60   * supported formats:
61   * <ul>
62   * <li>local-name of the property that existing in an ontology in the
63   * repository;</li>
64   * <li>prefix:local-name of the property, where the prefix is defined in the
65   * repository;</li>
66   * <li>uri of the property.</li>
67   * </ul>
68   * Unless the local-name format is used and the property is either functional or
69   * has a cardinality of one, the property type will be a java.uti.Set.
70   * 
71   * @author James Leigh
72   * @see DynaClassSupport
73   * 
74   */
75  @SuppressWarnings("unchecked")
76  public class DynaBeanSupport implements DynaBean {
77  
78  	private static WeakHashMap<Repository, DynaClassInfoFactory> factories = new WeakHashMap();
79  
80  	private SesameManager manager;
81  
82  	private SesameEntity bean;
83  
84  	private DynaClassInfo dynaClassInfo;
85  
86  	private Map<String, SesameProperty> properties = new HashMap<String, SesameProperty>();
87  
88  	public DynaBeanSupport(Entity elmo) {
89  		this.bean = (SesameEntity) elmo;
90  		this.manager = bean.getSesameManager();
91  		DynaClassInfoFactory factory;
92  		Repository repository;
93  		repository = manager.getConnection().getRepository();
94  		synchronized (factories) {
95  			factory = factories.get(repository);
96  			if (factory == null) {
97  				factory = new DynaClassInfoFactory();
98  				factories.put(repository, factory);
99  			}
100 		}
101 		Resource resource = bean.getSesameResource();
102 		this.dynaClassInfo = factory.findDyanClass(manager, resource);
103 	}
104 
105 	public DynaClass getDynaClass() {
106 		DynaClassSupport c = new DynaClassSupport(bean);
107 		c.setDynaClassInfo(dynaClassInfo);
108 		return c;
109 	}
110 
111 	@SuppressWarnings("unchecked")
112 	private SesameProperty getSesameProperty(String property) {
113 		if (properties.containsKey(property))
114 			return properties.get(property);
115 		URI pred = getPredicate(property);
116 		assert pred != null;
117 		PropertyChanger pc = new PropertyChanger(pred);
118 		SesameProperty sp = new SesameProperty(bean, pc);
119 		properties.put(property, sp);
120 		return sp;
121 	}
122 
123 	private URI getPredicate(String property) {
124 		int colon = property.indexOf(':');
125 		if (colon < 0)
126 			return dynaClassInfo.getPredicate(property);
127 		ContextAwareConnection conn = manager.getConnection();
128 		ValueFactory vf = conn.getRepository().getValueFactory();
129 		for (int i = colon + 1, n = property.length(); i < n; i++) {
130 			switch (property.charAt(i)) {
131 			case ':':
132 			case '/':
133 			case '#':
134 				return vf.createURI(property);
135 			}
136 		}
137 		try {
138 			String ns = conn.getNamespace(property.substring(0, colon));
139 			if (ns == null) {
140 				String msg = "Unknown prefix in property: " + property;
141 				throw new IllegalArgumentException(msg);
142 			}
143 			return vf.createURI(ns, property.substring(colon + 1));
144 		} catch (RepositoryException e) {
145 			throw new ElmoIOException(e);
146 		}
147 	}
148 
149 	public Object get(String property) {
150 		if (dynaClassInfo.isFunctional(property))
151 			return getSesameProperty(property).getSingle();
152 		return getSesameProperty(property);
153 	}
154 
155 	public void set(String property, Object value) {
156 		boolean func = dynaClassInfo.isFunctional(property);
157 		if (value == null) {
158 			getSesameProperty(property).clear();
159 		} else if (!func && value instanceof Set) {
160 			getSesameProperty(property).setAll((Set<?>) value);
161 		} else {
162 			getSesameProperty(property).setSingle(value);
163 		}
164 	}
165 
166 	public Object get(String property, int index) {
167 		assert dynaClassInfo.isFunctional(property) : dynaClassInfo
168 				.getDynaProperty(property);
169 		SesameProperty sp = getSesameProperty(property);
170 		List list = (List) sp.getSingle();
171 		if (list == null)
172 			return null;
173 		return list.get(index);
174 	}
175 
176 	@SuppressWarnings("unchecked")
177 	public void set(String property, int index, Object value) {
178 		assert dynaClassInfo.isFunctional(property) : dynaClassInfo
179 				.getDynaProperty(property);
180 		SesameProperty sp = getSesameProperty(property);
181 		List<Object> list = (List) sp.getSingle();
182 		if (list == null && value == null) {
183 			return;
184 		} else if (list == null) {
185 			Collection<URI> types = dynaClassInfo.getRdfTypes(property);
186 			ContextAwareConnection conn = manager.getConnection();
187 			ValueFactory vf = conn.getRepository().getValueFactory();
188 			BNode bNode = vf.createBNode();
189 			try {
190 				for (URI rdfType : types) {
191 					conn.add(bNode, RDF.TYPE, rdfType);
192 				}
193 			} catch (RepositoryException e) {
194 				throw new ElmoPersistException(e);
195 			}
196 			list = (List<Object>) manager.find(bNode);
197 			list.add(value);
198 			getSesameProperty(property).setSingle(list);
199 		} else if (index == list.size()) {
200 			if (value != null)
201 				list.add(value);
202 		} else if (value == null) {
203 			list.remove(index);
204 		} else {
205 			list.set(index, value);
206 		}
207 	}
208 
209 	public Object get(String property, String key) {
210 		if (!getDynaClass().getDynaProperty(property).isMapped())
211 			throw new UnsupportedOperationException();
212 		assert dynaClassInfo.isFunctional(property) : dynaClassInfo
213 				.getDynaProperty(property);
214 		SesameProperty sp = getSesameProperty(property);
215 		Map map = (Map) sp.getSingle();
216 		if (map == null)
217 			return null;
218 		return map.get(key);
219 	}
220 
221 	@SuppressWarnings("unchecked")
222 	public void set(String property, String key, Object value) {
223 		if (!getDynaClass().getDynaProperty(property).isIndexed())
224 			throw new UnsupportedOperationException();
225 		assert dynaClassInfo.isFunctional(property) : dynaClassInfo
226 				.getDynaProperty(property);
227 		SesameProperty sp = getSesameProperty(property);
228 		Map<String, Object> map = (Map) sp.getSingle();
229 		if (map == null) {
230 			Collection<URI> types = dynaClassInfo.getRdfTypes(property);
231 			ContextAwareConnection conn = manager.getConnection();
232 			ValueFactory vf = conn.getRepository().getValueFactory();
233 			BNode bNode = vf.createBNode();
234 			try {
235 				for (URI rdfType : types) {
236 					conn.add(bNode, RDF.TYPE, rdfType);
237 				}
238 			} catch (RepositoryException e) {
239 				throw new ElmoPersistException(e);
240 			}
241 			map = (Map<String, Object>) manager.find(bNode);
242 			map.put(key, value);
243 			getSesameProperty(property).setSingle(map);
244 		} else if (value == null) {
245 			map.remove(key);
246 		} else {
247 			map.put(key, value);
248 		}
249 	}
250 
251 	public boolean contains(String property, String key) {
252 		if (!getDynaClass().getDynaProperty(property).isMapped())
253 			throw new UnsupportedOperationException();
254 		assert dynaClassInfo.isFunctional(property);
255 		SesameProperty sp = getSesameProperty(property);
256 		Map map = (Map) sp.getSingle();
257 		if (map == null)
258 			return false;
259 		return map.containsKey(key);
260 	}
261 
262 	public void remove(String property, String key) {
263 		if (!getDynaClass().getDynaProperty(property).isMapped())
264 			throw new UnsupportedOperationException();
265 		assert dynaClassInfo.isFunctional(property);
266 		SesameProperty sp = getSesameProperty(property);
267 		Map map = (Map) sp.getSingle();
268 		if (map != null)
269 			map.remove(key);
270 	}
271 
272 }