1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29 package org.openrdf.elmo.sesame;
30
31 import java.io.IOException;
32 import java.io.Serializable;
33 import java.net.URL;
34 import java.util.Enumeration;
35 import java.util.Map;
36 import java.util.Properties;
37 import java.util.concurrent.ConcurrentHashMap;
38 import java.util.concurrent.ConcurrentMap;
39
40 import javax.xml.datatype.XMLGregorianCalendar;
41
42 import org.openrdf.elmo.LiteralManager;
43 import org.openrdf.elmo.annotations.rdf;
44 import org.openrdf.elmo.exceptions.ElmoConversionException;
45 import org.openrdf.elmo.sesame.converters.Marshall;
46 import org.openrdf.elmo.sesame.converters.impl.BigDecimalMarshall;
47 import org.openrdf.elmo.sesame.converters.impl.BigIntegerMarshall;
48 import org.openrdf.elmo.sesame.converters.impl.BooleanMarshall;
49 import org.openrdf.elmo.sesame.converters.impl.ByteMarshall;
50 import org.openrdf.elmo.sesame.converters.impl.CharacterMarshall;
51 import org.openrdf.elmo.sesame.converters.impl.DateMarshall;
52 import org.openrdf.elmo.sesame.converters.impl.DoubleMarshall;
53 import org.openrdf.elmo.sesame.converters.impl.DurationMarshall;
54 import org.openrdf.elmo.sesame.converters.impl.FloatMarshall;
55 import org.openrdf.elmo.sesame.converters.impl.GregorianCalendarMarshall;
56 import org.openrdf.elmo.sesame.converters.impl.IntegerMarshall;
57 import org.openrdf.elmo.sesame.converters.impl.LocaleMarshall;
58 import org.openrdf.elmo.sesame.converters.impl.LongMarshall;
59 import org.openrdf.elmo.sesame.converters.impl.ObjectConstructorMarshall;
60 import org.openrdf.elmo.sesame.converters.impl.ObjectSerializationMarshall;
61 import org.openrdf.elmo.sesame.converters.impl.PatternMarshall;
62 import org.openrdf.elmo.sesame.converters.impl.ShortMarshall;
63 import org.openrdf.elmo.sesame.converters.impl.SqlDateMarshall;
64 import org.openrdf.elmo.sesame.converters.impl.SqlTimeMarshall;
65 import org.openrdf.elmo.sesame.converters.impl.SqlTimestampMarshall;
66 import org.openrdf.elmo.sesame.converters.impl.ValueOfMarshall;
67 import org.openrdf.elmo.sesame.converters.impl.XMLDocumentMarshall;
68 import org.openrdf.elmo.sesame.converters.impl.XMLGregorianCalendarMarshall;
69 import org.openrdf.model.Literal;
70 import org.openrdf.model.URI;
71 import org.openrdf.model.ValueFactory;
72 import org.slf4j.Logger;
73 import org.slf4j.LoggerFactory;
74
75
76
77
78
79
80
81 public class SesameLiteralManager implements LiteralManager<URI, Literal> {
82
83 private static final String JAVA_NS = "java:";
84
85 private static final String LITERALS_PROPERTIES = "META-INF/org.openrdf.elmo.literals";
86
87 private final Logger logger = LoggerFactory.getLogger(SesameLiteralManager.class);
88
89 private ClassLoader cl;
90
91 private ValueFactory factory;
92
93 private ConcurrentMap<URI, Class<?>> javaClasses;
94
95 private ConcurrentMap<Class<?>, Marshall<?>> marshalls;
96
97 private ConcurrentMap<Class<?>, URI> rdfTypes;
98
99 public SesameLiteralManager(ValueFactory factory) {
100 this.factory = factory;
101 javaClasses = new ConcurrentHashMap<URI, Class<?>>();
102 rdfTypes = new ConcurrentHashMap<Class<?>, URI>();
103 marshalls = new ConcurrentHashMap<Class<?>, Marshall<?>>();
104 }
105
106 public void init() {
107 if (cl == null)
108 setClassLoader(Thread.currentThread().getContextClassLoader());
109 }
110
111 public void setClassLoader(ClassLoader cl) {
112 this.cl = cl;
113 try {
114 recordMarshall(new BigDecimalMarshall(factory));
115 recordMarshall(new BigIntegerMarshall(factory));
116 recordMarshall(new BooleanMarshall(factory));
117 recordMarshall(new ByteMarshall(factory));
118 recordMarshall(new DoubleMarshall(factory));
119 recordMarshall(new FloatMarshall(factory));
120 recordMarshall(new IntegerMarshall(factory));
121 recordMarshall(new LongMarshall(factory));
122 recordMarshall(new ShortMarshall(factory));
123 recordMarshall(new CharacterMarshall(factory));
124 recordMarshall(new DateMarshall(factory));
125 recordMarshall(new LocaleMarshall(factory));
126 recordMarshall(new PatternMarshall(factory));
127 recordMarshall(new DurationMarshall(factory));
128 recordMarshall(new XMLDocumentMarshall(factory));
129 XMLGregorianCalendarMarshall xgcm;
130 xgcm = new XMLGregorianCalendarMarshall(factory);
131 recordMarshall(xgcm.getJavaClass(), xgcm);
132 recordMarshall(XMLGregorianCalendar.class, xgcm);
133 recordMarshall(new GregorianCalendarMarshall(factory));
134 recordMarshall(new SqlDateMarshall(factory));
135 recordMarshall(new SqlTimeMarshall(factory));
136 recordMarshall(new SqlTimestampMarshall(factory));
137 loadLiterals(SesameLiteralManager.class.getClassLoader());
138 loadLiterals(cl);
139 } catch (Exception e) {
140 throw new ElmoConversionException(e);
141 }
142 }
143
144 public Class<?> getClass(URI datatype) {
145 if (javaClasses.containsKey(datatype))
146 return javaClasses.get(datatype);
147 try {
148 if (datatype.getNamespace().equals(JAVA_NS))
149 return Class.forName(datatype.getLocalName(), true, cl);
150 } catch (ClassNotFoundException e) {
151 throw new ElmoConversionException(e);
152 }
153 throw new ElmoConversionException("Unknown datatype: " + datatype);
154 }
155
156 public URI getDatatype(Class<?> type) {
157 if (type.equals(String.class))
158 return null;
159 if (rdfTypes.containsKey(type))
160 return rdfTypes.get(type);
161 URI datatype = factory.createURI(JAVA_NS, type.getName());
162 recordType(type, datatype);
163 return datatype;
164 }
165
166 @SuppressWarnings("unchecked")
167 public Literal getLiteral(Object object) {
168 if (object instanceof String)
169 return factory.createLiteral((String) object);
170 Marshall marshall = findMarshall(object.getClass());
171 return marshall.serialize(object);
172 }
173
174 public Literal getLiteral(String value, String language) {
175 return factory.createLiteral(value, language);
176 }
177
178 @SuppressWarnings("unchecked")
179 public Object getObject(Literal literal) {
180 URI datatype = literal.getDatatype();
181 if (datatype == null)
182 return literal.getLabel();
183 Marshall marshall = findMarshall(datatype);
184 return marshall.deserialize(literal);
185 }
186
187 public void recordMarshall(Class<?> javaClass, Marshall<?> marshall) {
188 marshalls.put(javaClass, marshall);
189 }
190
191 public void recordType(Class<?> javaClass, String datatype) {
192 recordType(javaClass, factory.createURI(datatype));
193 }
194
195 public boolean isTypeOfLiteral(Class<?> type) {
196 return rdfTypes.containsKey(type);
197 }
198
199 private void recordType(Class<?> javaClass, URI datatype) {
200 if (!javaClasses.containsKey(datatype)) {
201 javaClasses.putIfAbsent(datatype, javaClass);
202 }
203 if (rdfTypes.putIfAbsent(javaClass, datatype) == null) {
204 Marshall<?> marshall = findMarshall(javaClass);
205 marshall.setDatatype(datatype);
206 }
207 }
208
209 @SuppressWarnings("unchecked")
210 private <T> Marshall<T> findMarshall(Class<T> type) {
211 if (marshalls.containsKey(type))
212 return (Marshall<T>) marshalls.get(type);
213 Marshall<T> marshall;
214 try {
215 marshall = new ValueOfMarshall<T>(factory, type);
216 } catch (NoSuchMethodException e1) {
217 try {
218 marshall = new ObjectConstructorMarshall<T>(factory, type);
219 } catch (NoSuchMethodException e2) {
220 if (Serializable.class.isAssignableFrom(type)) {
221 marshall = new ObjectSerializationMarshall<T>(factory, type);
222 } else {
223 throw new ElmoConversionException(e1);
224 }
225 }
226 }
227 Marshall<?> o = marshalls.putIfAbsent(type, marshall);
228 if (o != null) {
229 marshall = (Marshall<T>) o;
230 }
231 return marshall;
232 }
233
234 private Marshall<?> findMarshall(URI datatype) {
235 Class<?> type;
236 if (javaClasses.containsKey(datatype)) {
237 type = javaClasses.get(datatype);
238 } else if (datatype.getNamespace().equals(JAVA_NS)) {
239 try {
240 type = Class.forName(datatype.getLocalName(), true, cl);
241 } catch (ClassNotFoundException e) {
242 throw new ElmoConversionException(e);
243 }
244 } else {
245 throw new ElmoConversionException("Unknown datatype: " + datatype);
246 }
247 return findMarshall(type);
248 }
249
250 private void loadLiterals(ClassLoader cl) throws IOException,
251 ClassNotFoundException {
252 if (cl == null)
253 return;
254 Enumeration<URL> resources = cl.getResources(LITERALS_PROPERTIES);
255 while (resources.hasMoreElements()) {
256 URL url = resources.nextElement();
257 try {
258 Properties p = new Properties();
259 p.load(url.openStream());
260 for (Map.Entry<?, ?> e : p.entrySet()) {
261 String className = (String) e.getKey();
262 String types = (String) e.getValue();
263 Class<?> lc = Class.forName(className, true, cl);
264 boolean present = lc.isAnnotationPresent(rdf.class);
265 for (String rdf : types.split("\\s+")) {
266 if (rdf.length() == 0 && present) {
267 rdf = lc.getAnnotation(rdf.class).value()[0];
268 recordType(lc, factory.createURI(rdf));
269 } else if (rdf.length() == 0) {
270 logger.warn("Unkown literal mapping {}", className);
271 } else {
272 recordType(lc, factory.createURI(rdf));
273 }
274 }
275 }
276 } catch (IOException e) {
277 String msg = e.getMessage() + " in: " + url;
278 IOException ioe = new IOException(msg);
279 ioe.initCause(e);
280 throw ioe;
281 }
282 }
283 }
284
285 private void recordMarshall(Marshall<?> marshall) {
286 recordMarshall(marshall.getJavaClass(), marshall);
287 }
288
289 }