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.codegen;
30  
31  import groovy.lang.Binding;
32  import groovy.lang.MissingPropertyException;
33  import groovy.util.GroovyScriptEngine;
34  import groovy.util.ResourceConnector;
35  import groovy.util.ResourceException;
36  
37  import java.io.CharArrayWriter;
38  import java.io.IOException;
39  import java.io.PrintWriter;
40  import java.net.URL;
41  import java.net.URLConnection;
42  import java.util.HashMap;
43  import java.util.Map;
44  
45  import javax.xml.namespace.QName;
46  
47  import org.openrdf.concepts.owl.Ontology;
48  import org.openrdf.concepts.rdfs.Datatype;
49  import org.openrdf.elmo.ElmoModule;
50  import org.openrdf.elmo.ElmoQuery;
51  import org.openrdf.elmo.sesame.SesameManager;
52  import org.openrdf.elmo.sesame.SesameManagerFactory;
53  import org.openrdf.elmo.sesame.roles.SesameEntity;
54  import org.openrdf.model.URI;
55  import org.openrdf.model.vocabulary.OWL;
56  import org.openrdf.model.vocabulary.RDF;
57  import org.openrdf.model.vocabulary.RDFS;
58  import org.openrdf.repository.Repository;
59  import org.slf4j.Logger;
60  import org.slf4j.LoggerFactory;
61  
62  /**
63   * Converts OWL ontologies into JavaBeans. This class can be used to create Elmo
64   * concepts or other JavaBean interfaces or classes.
65   * 
66   * @author James Leigh
67   * 
68   */
69  public class CodeGenerator implements ResourceConnector {
70  
71  	private static String CONCEPT_TEMPLATE = "org/openrdf/elmo/codegen/templates/concept.groovy";
72  
73  	private static String DATATYPE_TEMPLATE = "org/openrdf/elmo/codegen/templates/datatype.groovy";
74  
75  	private static String PACKAGE_TEMPLATE = "org/openrdf/elmo/codegen/templates/package.groovy";
76  
77  	private static final String CLASS_DEFINED_BY = "PREFIX rdf: <"
78  			+ RDF.NAMESPACE
79  			+ "> PREFIX rdfs: <"
80  			+ RDFS.NAMESPACE
81  			+ "> PREFIX owl: <"
82  			+ OWL.NAMESPACE
83  			+ "> SELECT ?bean WHERE { ?bean rdfs:isDefinedBy ?ont . { ?bean rdf:type owl:Class } UNION {?bean rdf:type rdfs:Datatype } }";
84  
85  	private final Logger logger = LoggerFactory.getLogger(CodeGenerator.class);
86  
87  	private SesameManagerFactory factory;
88  
89  	private GroovyScriptEngine engine;
90  
91  	private OwlNormalizer normalizer;
92  
93  	private Map<URI, String> ontologies = new HashMap<URI, String>();
94  
95  	private boolean propertyNamesPrefixed = true;
96  
97  	private Class<?>[] baseClasses = new Class<?>[0];
98  
99  	public Class<?>[] getBaseClasses() {
100 		return baseClasses;
101 	}
102 
103 	public void setBaseClasses(Class<?>[] baseClasses) {
104 		this.baseClasses = baseClasses;
105 	}
106 
107 	public boolean isPropertyNamesPrefixed() {
108 		return propertyNamesPrefixed;
109 	}
110 
111 	public void setPropertyNamesPrefixed(boolean prefixPropertyNames) {
112 		this.propertyNamesPrefixed = prefixPropertyNames;
113 	}
114 
115 	public void addOntologyPackage(URI ontology, String pkgName) {
116 		ontologies.put(ontology, pkgName);
117 	}
118 
119 	public URLConnection getResourceConnection(String name)
120 			throws ResourceException {
121 		try {
122 			URL url = getClass().getResource("/" + name);
123 			if (url != null)
124 				return url.openConnection();
125 			throw new ResourceException("Resource not found: " + name);
126 		} catch (IOException e) {
127 			throw new ResourceException(e);
128 		}
129 	}
130 
131 	public void setRepository(Repository repository) {
132 		this.factory = new SesameManagerFactory(new ElmoModule(), repository);
133 	}
134 
135 	public void setGroovyScriptEngine(GroovyScriptEngine engine) {
136 		this.engine = engine;
137 	}
138 
139 	public void init() throws Exception {
140 		normalizer = new OwlNormalizer();
141 		normalizer.setRepository(factory.getRepository());
142 		normalizer.normalize();
143 	}
144 
145 	public void exportSourceCode(SourceCodeHandler handler) throws Exception {
146 		SesameManager manager = factory.createElmoManager();
147 		try {
148 			ElmoQuery query;
149 			query = manager.createQuery(CLASS_DEFINED_BY);
150 			for (Map.Entry<URI, String> e : ontologies.entrySet()) {
151 				query.setParameter("ont", new QName(e.getKey().toString()));
152 				try {
153 					for (Object o : query.getResultList()) {
154 						SesameEntity bean = (SesameEntity) o;
155 						try {
156 							if (bean.getQName() == null)
157 								continue;
158 							if (bean instanceof Datatype) {
159 								buildDatatype(bean, e.getValue(), manager,
160 										handler);
161 							} else {
162 								buildClass(bean, e.getValue(), manager, handler);
163 							}
164 						} catch (MissingPropertyException exc) {
165 							logger.error("Error processing {}", bean);
166 							StringBuilder sb = new StringBuilder();
167 							for (Class<?> inter : exc.getType().getInterfaces()) {
168 								sb.append(inter.getSimpleName()).append(" ");
169 							}
170 							String sn = exc.getType().getSimpleName();
171 							logger.warn("Class {} implements {}", sn, sb
172 									.toString());
173 							throw exc;
174 						} catch (Exception exc) {
175 							logger.error("Error processing {}", bean);
176 							throw exc;
177 						}
178 					}
179 				} finally {
180 					query.close();
181 				}
182 			}
183 			for (URI ontology : ontologies.keySet()) {
184 				if (manager.contains(ontology)) {
185 					buildOntology(ontology, manager, handler);
186 				}
187 			}
188 		} finally {
189 			manager.close();
190 		}
191 	}
192 
193 	private void buildClass(SesameEntity bean, String packageName,
194 			SesameManager manager, SourceCodeHandler handler) throws Exception {
195 		Binding bindings = createBindings();
196 		CharArrayWriter output = new CharArrayWriter();
197 		setVariables(bindings, manager, packageName, bean);
198 		bindings.setVariable("content", new PrintWriter(output));
199 		bindings.setVariable("usePrefix", Boolean.valueOf(propertyNamesPrefixed));
200 		bindings.setVariable("baseClasses", baseClasses);
201 		engine.run(CONCEPT_TEMPLATE, bindings);
202 		String content = output.toString();
203 		if (content.length() > 0) {
204 			handler.handleSource(content);
205 		}
206 	}
207 
208 	private void buildDatatype(SesameEntity bean, String packageName,
209 			SesameManager manager, SourceCodeHandler handler) throws Exception {
210 		Binding bindings = createBindings();
211 		CharArrayWriter output = new CharArrayWriter();
212 		setVariables(bindings, manager, packageName, bean);
213 		bindings.setVariable("content", new PrintWriter(output));
214 		engine.run(DATATYPE_TEMPLATE, bindings);
215 		String content = output.toString();
216 		if (content.length() > 0) {
217 			handler.handleSource(content);
218 		}
219 	}
220 
221 	private void buildOntology(URI ontology, SesameManager manager,
222 			SourceCodeHandler handler) throws Exception {
223 		Binding bindings = createBindings();
224 		CharArrayWriter output = new CharArrayWriter();
225 		Ontology ont = manager.designate(ontology, Ontology.class);
226 		bindings.setVariable("bean", ont);
227 		bindings.setVariable("packageName", ontologies.get(ontology));
228 		bindings.setVariable("uri", ontology);
229 		bindings.setVariable("manager", manager);
230 		bindings.setVariable("content", new PrintWriter(output));
231 		engine.run(PACKAGE_TEMPLATE, bindings);
232 		String content = output.toString();
233 		if (content.length() > 0) {
234 			handler.handleSource(content);
235 		}
236 	}
237 
238 	private Binding createBindings() {
239 		Binding bindings = new Binding();
240 		bindings.setVariable("packageNames", ontologies);
241 		bindings.setVariable("literals", factory.getLiteralManager());
242 		bindings.setVariable("roles", factory.getRoleMapper());
243 		return bindings;
244 	}
245 
246 	private void setVariables(Binding bindings, SesameManager manager,
247 			String packageName, SesameEntity bean) {
248 		URI uri = (URI) bean.getSesameResource();
249 		uri = normalizer.getOriginal(uri);
250 		bindings.setVariable("bean", bean);
251 		bindings.setVariable("packageName", packageName);
252 		bindings.setVariable("uri", uri);
253 		bindings.setVariable("manager", manager);
254 	}
255 }