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 String propertyNamesPrefix;
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 String getPropertyNamesPrefix() {
108 		return propertyNamesPrefix;
109 	}
110 
111 	public void setPropertyNamesPrefix(String prefixPropertyNames) {
112 		this.propertyNamesPrefix = 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 				buildOntology(ontology, manager, handler);
185 			}
186 		} finally {
187 			manager.close();
188 		}
189 	}
190 
191 	private void buildClass(SesameEntity bean, String packageName,
192 			SesameManager manager, SourceCodeHandler handler) throws Exception {
193 		Binding bindings = createBindings();
194 		CharArrayWriter output = new CharArrayWriter();
195 		setVariables(bindings, manager, packageName, bean);
196 		bindings.setVariable("content", new PrintWriter(output));
197 		bindings.setVariable("propertyPrefix", propertyNamesPrefix);
198 		bindings.setVariable("baseClasses", baseClasses);
199 		engine.run(CONCEPT_TEMPLATE, bindings);
200 		String content = output.toString();
201 		if (content.length() > 0) {
202 			handler.handleSource(content);
203 		}
204 	}
205 
206 	private void buildDatatype(SesameEntity bean, String packageName,
207 			SesameManager manager, SourceCodeHandler handler) throws Exception {
208 		Binding bindings = createBindings();
209 		CharArrayWriter output = new CharArrayWriter();
210 		setVariables(bindings, manager, packageName, bean);
211 		bindings.setVariable("content", new PrintWriter(output));
212 		engine.run(DATATYPE_TEMPLATE, bindings);
213 		String content = output.toString();
214 		if (content.length() > 0) {
215 			handler.handleSource(content);
216 		}
217 	}
218 
219 	private void buildOntology(URI ontology, SesameManager manager,
220 			SourceCodeHandler handler) throws Exception {
221 		Binding bindings = createBindings();
222 		CharArrayWriter output = new CharArrayWriter();
223 		Ontology ont = manager.designate(ontology, Ontology.class);
224 		bindings.setVariable("bean", ont);
225 		bindings.setVariable("packageName", ontologies.get(ontology));
226 		bindings.setVariable("uri", ontology);
227 		bindings.setVariable("manager", manager);
228 		bindings.setVariable("content", new PrintWriter(output));
229 		engine.run(PACKAGE_TEMPLATE, bindings);
230 		String content = output.toString();
231 		if (content.length() > 0) {
232 			handler.handleSource(content);
233 		}
234 	}
235 
236 	private Binding createBindings() {
237 		Binding bindings = new Binding();
238 		bindings.setVariable("packageNames", ontologies);
239 		bindings.setVariable("literals", factory.getLiteralManager());
240 		bindings.setVariable("roles", factory.getRoleMapper());
241 		return bindings;
242 	}
243 
244 	private void setVariables(Binding bindings, SesameManager manager,
245 			String packageName, SesameEntity bean) {
246 		URI uri = (URI) bean.getSesameResource();
247 		uri = normalizer.getOriginal(uri);
248 		bindings.setVariable("bean", bean);
249 		bindings.setVariable("packageName", packageName);
250 		bindings.setVariable("uri", uri);
251 		bindings.setVariable("manager", manager);
252 	}
253 }