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.sesame.behaviours;
30  
31  import info.aduna.iteration.CloseableIteration;
32  
33  import java.util.AbstractList;
34  import java.util.HashSet;
35  
36  import org.openrdf.elmo.Entity;
37  import org.openrdf.elmo.Mergeable;
38  import org.openrdf.elmo.Refreshable;
39  import org.openrdf.elmo.annotations.intercepts;
40  import org.openrdf.elmo.annotations.rdf;
41  import org.openrdf.elmo.exceptions.ElmoIOException;
42  import org.openrdf.elmo.exceptions.ElmoPersistException;
43  import org.openrdf.elmo.sesame.SesameManager;
44  import org.openrdf.elmo.sesame.iterators.ElmoIteration;
45  import org.openrdf.elmo.sesame.roles.SesameEntity;
46  import org.openrdf.model.Resource;
47  import org.openrdf.model.Statement;
48  import org.openrdf.model.URI;
49  import org.openrdf.model.Value;
50  import org.openrdf.model.vocabulary.RDF;
51  import org.openrdf.repository.Repository;
52  import org.openrdf.repository.RepositoryConnection;
53  import org.openrdf.repository.RepositoryException;
54  import org.openrdf.repository.contextaware.ContextAwareConnection;
55  
56  /**
57   * This behaviour provides a java.util.List interface for RDF containers.
58   * 
59   * @author James Leigh
60   */
61  @rdf("http://www.w3.org/2000/01/rdf-schema#Container")
62  public class SesameContainer extends AbstractList<Object> implements
63  		java.util.List<Object>, Refreshable, Mergeable {
64  
65  	private static final int UNKNOWN = -1;
66  
67  	private SesameManager manager;
68  
69  	private Resource resource;
70  
71  	private int _size = UNKNOWN;
72  
73  	public SesameContainer(Entity bean) {
74  		SesameEntity sbean = (SesameEntity) bean;
75  		this.manager = sbean.getSesameManager();
76  		this.resource = sbean.getSesameResource();
77  	}
78  
79  	public void refresh() {
80  		_size = UNKNOWN;
81  	}
82  
83  	public void merge(Object source) {
84  		if (source instanceof java.util.List) {
85  			RepositoryConnection conn = manager.getConnection();
86  			try {
87  				boolean autoCommit = conn.isAutoCommit();
88  				if (autoCommit)
89  					conn.setAutoCommit(false);
90  				java.util.List list = (java.util.List) source;
91  				int size = list.size();
92  				for (int i=0,n=size; i<n; i++) {
93  					internalSet(i, list.get(i));
94  				}
95  				if (_size > UNKNOWN && _size < size)
96  					_size = size;
97  				if (autoCommit)
98  					conn.setAutoCommit(true);
99  			} catch (RepositoryException e) {
100 				throw new ElmoPersistException(e);
101 			}
102 		}
103 	}
104 
105 	private URI getMemberPredicate(int index) {
106 		RepositoryConnection conn = manager.getConnection();
107 		Repository repository;
108 		repository = conn.getRepository();
109 		String uri = RDF.NAMESPACE + '_' + (index + 1);
110 		return repository.getValueFactory().createURI(uri);
111 	}
112 
113 	@Override
114 	public Object set(int index, Object obj) {
115 		RepositoryConnection conn = manager.getConnection();
116 		try {
117 			boolean autoCommit = conn.isAutoCommit();
118 			if (autoCommit)
119 				conn.setAutoCommit(false);
120 			Value value = internalSet(index, obj);
121 			Object old = createInstance(value);
122 			if (autoCommit)
123 				conn.setAutoCommit(true);
124 			return old;
125 		} catch (RepositoryException e) {
126 			throw new ElmoPersistException(e);
127 		}
128 	}
129 
130 	private Value internalSet(int index, Object o) {
131 		URI pred = getMemberPredicate(index);
132 		ElmoIteration<Statement, Value> stmts = getStatements(pred);
133 		try {
134 			Value newValue = o == null ? null : manager.getValue(o);
135 			Value oldValue = null;
136 			while (stmts.hasNext()) {
137 				oldValue = stmts.next();
138 				if (newValue == null || !newValue.equals(oldValue))
139 					stmts.remove();
140 			}
141 			if (newValue != null && !newValue.equals(oldValue)) {
142 				ContextAwareConnection conn = manager.getConnection();
143 				conn.add(resource, pred, newValue);
144 			}
145 			return oldValue;
146 		} catch (RepositoryException e) {
147 			throw new ElmoPersistException(e);
148 		} finally {
149 			stmts.close();
150 		}
151 	}
152 
153 	private ElmoIteration<Statement, Value> getStatements(URI pred) {
154 		try {
155 			CloseableIteration<? extends Statement, RepositoryException> stmts;
156 			final ContextAwareConnection conn = manager.getConnection();
157 			stmts = conn.getStatements(resource, pred, null);
158 			return new ElmoIteration<Statement, Value>(stmts) {
159 				@Override
160 				protected Value convert(Statement stmt) throws Exception {
161 					return stmt.getObject();
162 				}
163 
164 				@Override
165 				protected void remove(Statement stmt) throws Exception {
166 					conn.remove(stmt);
167 				}
168 			};
169 		} catch (RepositoryException e) {
170 			throw new ElmoIOException(e);
171 		}
172 	}
173 
174 	@Override
175 	public void add(int index, Object obj) {
176 		RepositoryConnection conn = manager.getConnection();
177 		try {
178 			boolean autoCommit = conn.isAutoCommit();
179 			if (autoCommit)
180 				conn.setAutoCommit(false);
181 			for (int i = size() - 1; i >= index; i--) {
182 				internalSet(i + 1, get(i));
183 			}
184 			internalSet(index, obj);
185 			if (_size > UNKNOWN)
186 				_size++;
187 			if (autoCommit)
188 				conn.setAutoCommit(true);
189 		} catch (RepositoryException e) {
190 			throw new ElmoPersistException(e);
191 		}
192 	}
193 
194 	@Override
195 	public Object remove(int index) {
196 		RepositoryConnection conn = manager.getConnection();
197 		try {
198 			boolean autoCommit = conn.isAutoCommit();
199 			conn.setAutoCommit(false);
200 			Object obj = get(index);
201 			int size = size();
202 			for (int i = index; i < size - 1; i++) {
203 				internalSet(i, get(i + 1));
204 			}
205 			URI pred = getMemberPredicate(size - 1);
206 			ElmoIteration<Statement, Value> stmts = getStatements(pred);
207 			try {
208 				while (stmts.hasNext()) {
209 					stmts.next();
210 					stmts.remove();
211 				}
212 			} finally {
213 				stmts.close();
214 			}
215 			if (_size > UNKNOWN)
216 				_size--;
217 			conn.setAutoCommit(autoCommit);
218 			return obj;
219 		} catch (RepositoryException e) {
220 			throw new ElmoPersistException(e);
221 		}
222 	}
223 
224 	@Override
225 	public Object get(int index) {
226 		URI pred = getMemberPredicate(index);
227 		ElmoIteration<Statement, Value> stmts = getStatements(pred);
228 		try {
229 			if (stmts.hasNext()) {
230 				Value next = stmts.next();
231 				return createInstance(next);
232 			}
233 			return null;
234 		} finally {
235 			stmts.close();
236 		}
237 	}
238 
239 	@Override
240 	public int size() {
241 		if (_size < 0) {
242 			synchronized (this) {
243 				if (_size < 0) {
244 					int index = getSize();
245 					_size = index;
246 				}
247 			}
248 		}
249 		return _size;
250 	}
251 
252 	@Override
253 	@intercepts(method="toString",argc=0)
254 	public String toString() {
255 		return super.toString();
256 	}
257 
258 	private Object createInstance(Value next) {
259 		return manager.getInstance(next);
260 	}
261 
262 	private int getSize() {
263 		try {
264 			CloseableIteration<? extends Statement, RepositoryException> iter;
265 			HashSet<URI> set = new HashSet<URI>();
266 			ContextAwareConnection conn = manager.getConnection();
267 			iter = conn.getStatements(resource, null, null);
268 			try {
269 				while (iter.hasNext()) {
270 					set.add(iter.next().getPredicate());
271 				}
272 			} finally {
273 				iter.close();
274 			}
275 			int index = 0;
276 			while (set.contains(getMemberPredicate(index)))
277 				index++;
278 			return index;
279 		} catch (RuntimeException e) {
280 			throw e;
281 		} catch (Exception e) {
282 			throw new ElmoIOException(e);
283 		}
284 	}
285 }