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.codegen;
30
31 import groovy.lang.GroovyClassLoader;
32 import groovy.util.GroovyScriptEngine;
33
34 import java.io.File;
35 import java.io.FileNotFoundException;
36 import java.io.FileOutputStream;
37 import java.io.FileWriter;
38 import java.io.IOException;
39 import java.io.InputStream;
40 import java.io.OutputStream;
41 import java.io.PrintStream;
42 import java.io.Writer;
43 import java.net.MalformedURLException;
44 import java.net.URL;
45 import java.net.URLClassLoader;
46 import java.util.ArrayList;
47 import java.util.Collection;
48 import java.util.Enumeration;
49 import java.util.HashMap;
50 import java.util.List;
51 import java.util.Map;
52 import java.util.Properties;
53 import java.util.jar.JarEntry;
54 import java.util.jar.JarFile;
55 import java.util.jar.JarOutputStream;
56
57 import org.apache.commons.cli.CommandLine;
58 import org.apache.commons.cli.GnuParser;
59 import org.apache.commons.cli.HelpFormatter;
60 import org.apache.commons.cli.Option;
61 import org.apache.commons.cli.Options;
62 import org.apache.commons.cli.ParseException;
63 import org.openrdf.elmo.LiteralManager;
64 import org.openrdf.elmo.sesame.SesameLiteralManager;
65 import org.openrdf.model.Literal;
66 import org.openrdf.model.Statement;
67 import org.openrdf.model.URI;
68 import org.openrdf.model.ValueFactory;
69 import org.openrdf.model.impl.URIImpl;
70 import org.openrdf.model.vocabulary.OWL;
71 import org.openrdf.model.vocabulary.RDF;
72 import org.openrdf.repository.Repository;
73 import org.openrdf.repository.RepositoryConnection;
74 import org.openrdf.repository.RepositoryException;
75 import org.openrdf.repository.RepositoryResult;
76 import org.openrdf.repository.sail.SailRepository;
77 import org.openrdf.rio.RDFFormat;
78 import org.openrdf.rio.RDFParseException;
79 import org.openrdf.rio.RDFWriter;
80 import org.openrdf.rio.Rio;
81 import org.openrdf.sail.memory.MemoryStore;
82 import org.slf4j.Logger;
83 import org.slf4j.LoggerFactory;
84
85
86
87
88
89
90
91
92
93 public class OntologyConverter {
94
95 private static final String META_INF_ELMO_ROLES = "META-INF/org.openrdf.elmo.roles";
96
97 private static final String META_INF_ELMO_LITERALS = "META-INF/org.openrdf.elmo.literals";
98
99 private static final String META_INF_ONTOLOGIES = "META-INF/org.openrdf.elmo.ontologies";
100
101 private static final Options options = new Options();
102 static {
103 Option pkg = new Option("b", "bind", true,
104 "Binds the package name and ontology URI together");
105 pkg.setArgName("package=uri");
106 Option jar = new Option("j", "jar", true,
107 "filename where the jar will be saved");
108 jar.setArgName("jar file");
109 Option file = new Option("r", "rdf", true,
110 "filename where the rdf ontology will be saved");
111 file.setArgName("RDF file");
112 Option prefix = new Option("p", "prefix", true,
113 "prefix the property names with namespace prefix (default is true)");
114 prefix.setArgName("true|false");
115 Option baseClass = new Option("e", "extends", true,
116 "super class that all concepts should extend");
117 baseClass.setArgName("full class name");
118 options.addOption(baseClass);
119 options.addOption(prefix);
120 options.addOption("h", "help", false, "print this message");
121 options.addOption(pkg);
122 options.addOption(jar);
123 options.addOption(file);
124 }
125
126 public static void main(String[] args) throws Exception {
127 try {
128 CommandLine line = new GnuParser().parse(options, args);
129 if (line.hasOption('h')) {
130 HelpFormatter formatter = new HelpFormatter();
131 String cmdLineSyntax = "codegen [options] [ontology | jar]...";
132 String header = "[ontology | jar]... are a list of RDF and jar files that should be imported before converting.";
133 formatter.printHelp(cmdLineSyntax, header, options, "");
134 return;
135 }
136 if (!line.hasOption('b'))
137 throw new ParseException("Required bind option missing");
138 if (!line.hasOption('j') && !line.hasOption('r'))
139 throw new ParseException("Required jar or rdf option missing");
140 if (line.hasOption('j') && line.hasOption('r'))
141 throw new ParseException(
142 "Only one jar or rdf option can be present");
143 OntologyConverter converter = new OntologyConverter();
144 boolean prefix = Boolean.parseBoolean(line.getOptionValue('p',
145 "true"));
146 converter.setPropertyNamesPrefixed(prefix);
147 if (line.hasOption('e')) {
148 converter.setBaseClasses(line.getOptionValues('e'));
149 }
150 findJars(line.getArgs(), 0, converter);
151 findRdfSources(line.getArgs(), 0, converter);
152 for (String value : line.getOptionValues('b')) {
153 String[] split = value.split("=", 2);
154 if (split.length != 2) {
155 throw new ParseException("Invalid bind option: " + value);
156 }
157 converter.addOntology(new URIImpl(split[1]), split[0]);
158 }
159 converter.init();
160 if (line.hasOption('j')) {
161 converter.createClasses(new File(line.getOptionValue('j')));
162 } else {
163 converter.createOntology(new File(line.getOptionValue('r')));
164 }
165 return;
166 } catch (ParseException exp) {
167 System.err.println(exp.getMessage());
168 System.exit(1);
169 }
170 }
171
172 private static void findRdfSources(String[] args, int offset,
173 OntologyConverter converter) throws MalformedURLException {
174 for (int i = offset; i < args.length; i++) {
175 if (args[i].endsWith(".jar"))
176 continue;
177 URL url;
178 File file = new File(args[i]);
179 if (file.exists()) {
180 url = file.toURI().toURL();
181 } else {
182 url = new URL(args[i]);
183 }
184 converter.addRdfSource(url);
185 }
186 }
187
188 private static void findJars(String[] args, int offset,
189 OntologyConverter converter) throws MalformedURLException {
190 for (int i = offset; i < args.length; i++) {
191 URL url;
192 File file = new File(args[i]);
193 if (file.exists()) {
194 url = file.toURI().toURL();
195 } else {
196 url = new URL(args[i]);
197 }
198 if (args[i].endsWith(".jar")) {
199 converter.addJar(url);
200 }
201 }
202 }
203
204 final Logger logger = LoggerFactory.getLogger(OntologyConverter.class);
205
206 private boolean importJarOntologies = true;
207
208 private List<URL> jars = new ArrayList<URL>();
209
210 private List<URL> rdfSources = new ArrayList<URL>();
211
212 private Map<String, String> namespaces = new HashMap<String, String>();
213
214 private Map<URI, String> ontologies = new HashMap<URI, String>();
215
216 private Repository repository;
217
218 private URLClassLoader cl;
219
220 private boolean propertyNamesPrefixed = true;
221
222 private String[] baseClasses;
223
224
225
226
227
228
229 public boolean isImportJarOntologies() {
230 return importJarOntologies;
231 }
232
233
234
235
236
237
238
239 public void setImportJarOntologies(boolean importJarOntologies) {
240 this.importJarOntologies = importJarOntologies;
241 }
242
243
244
245
246
247
248 public boolean isPropertyNamesPrefixed() {
249 return propertyNamesPrefixed;
250 }
251
252
253
254
255
256
257
258 public void setPropertyNamesPrefixed(boolean propertyNamesPrefixed) {
259 this.propertyNamesPrefixed = propertyNamesPrefixed;
260 }
261
262
263
264
265
266
267 public String[] getBaseClasses() {
268 return baseClasses;
269 }
270
271
272
273
274
275
276 public void setBaseClasses(String[] strings) {
277 this.baseClasses = strings;
278 }
279
280
281
282
283
284
285 public void addJar(URL url) {
286 jars.add(url);
287 }
288
289
290
291
292
293
294 public void addRdfSource(URL url) {
295 rdfSources.add(url);
296 }
297
298
299
300
301
302
303
304 public void setNamespace(String prefix, String namespace) {
305 namespaces.put(prefix, namespace);
306 }
307
308
309
310
311
312
313
314 public void addOntology(URI ontology, String pkgName) {
315 ontologies.put(ontology, pkgName);
316 }
317
318
319
320
321
322
323 public void init() throws Exception {
324 cl = createClassLoader(jars);
325 repository = createRepository(cl);
326 for (URL url : rdfSources) {
327 loadOntology(repository, url);
328 }
329 }
330
331
332
333
334
335
336
337
338
339 public void createOntology(File rdfOutputFile) throws Exception {
340 List<Class<?>> beans = new ArrayList<Class<?>>();
341 if (ontologies.isEmpty()) {
342 beans.addAll(findBeans(null, jars, cl));
343 } else {
344 for (String packageName : ontologies.values()) {
345 beans.addAll(findBeans(packageName, jars, cl));
346 }
347 }
348 ValueFactory vf = repository.getValueFactory();
349 SesameLiteralManager manager = new SesameLiteralManager(vf);
350 manager.setClassLoader(cl);
351 createOntology(beans, manager, rdfOutputFile);
352 }
353
354
355
356
357
358
359
360
361
362
363 public void createClasses(File jarOutputFile) throws Exception {
364 createJar(repository, cl, jarOutputFile);
365 }
366
367 protected Repository createRepository() throws RepositoryException {
368 Repository repository = new SailRepository(new MemoryStore());
369 repository.initialize();
370 return repository;
371 }
372
373 private URLClassLoader createClassLoader(List<URL> importJars)
374 throws MalformedURLException {
375 Thread thread = Thread.currentThread();
376 ClassLoader cl = thread.getContextClassLoader();
377 if (cl == null) {
378 cl = OntologyConverter.class.getClassLoader();
379 }
380 URL[] classpath = importJars.toArray(new URL[0]);
381 if (cl instanceof URLClassLoader) {
382 URL[] urls = ((URLClassLoader) cl).getURLs();
383 URL[] jars = classpath;
384 classpath = new URL[jars.length + urls.length];
385 System.arraycopy(jars, 0, classpath, 0, jars.length);
386 System.arraycopy(urls, 0, classpath, jars.length, urls.length);
387 }
388 return URLClassLoader.newInstance(classpath, cl);
389 }
390
391 private Repository createRepository(ClassLoader cl)
392 throws RepositoryException, IOException, RDFParseException {
393 Repository repository = createRepository();
394 RepositoryConnection conn = repository.getConnection();
395 try {
396 for (Map.Entry<String, String> e : namespaces.entrySet()) {
397 conn.setNamespace(e.getKey(), e.getValue());
398 }
399 } finally {
400 conn.close();
401 }
402 if (importJarOntologies) {
403 for (String owl : loadOntologyList(cl)) {
404 URL url = cl.getResource(owl);
405 loadOntology(repository, url);
406 }
407 }
408 return repository;
409 }
410
411 @SuppressWarnings("unchecked")
412 private Collection<String> loadOntologyList(ClassLoader cl)
413 throws IOException {
414 Properties ontologies = new Properties();
415 String name = "META-INF/org.openrdf.elmo.ontologies";
416 Enumeration<URL> resources = cl.getResources(name);
417 while (resources.hasMoreElements()) {
418 URL url = resources.nextElement();
419 ontologies.load(url.openStream());
420 }
421 Collection<?> list = ontologies.keySet();
422 return (Collection<String>) list;
423 }
424
425 private void loadOntology(Repository repository, URL url)
426 throws RepositoryException, IOException, RDFParseException {
427 String filename = url.toString();
428 RDFFormat format = formatForFileName(filename);
429 RepositoryConnection conn = repository.getConnection();
430 try {
431 conn.add(url, "", format);
432 } finally {
433 conn.close();
434 }
435 }
436
437 private RDFFormat formatForFileName(String filename) {
438 RDFFormat format = RDFFormat.forFileName(filename);
439 if (format != null)
440 return format;
441 if (filename.endsWith(".owl"))
442 return RDFFormat.RDFXML;
443 throw new IllegalArgumentException("Unknow RDF format for " + filename);
444 }
445
446 private List<Class<?>> findBeans(String pkgName, List<URL> urls,
447 URLClassLoader cl) throws Exception {
448 List<Class<?>> beans = new ArrayList<Class<?>>();
449 for (URL jar : urls) {
450 JarFile file = new JarFile(jar.getFile());
451 Enumeration<JarEntry> entries = file.entries();
452 while (entries.hasMoreElements()) {
453 JarEntry entry = entries.nextElement();
454 String name = entry.getName();
455 if (name.contains("-") || !name.endsWith(".class"))
456 continue;
457 name = name.replace('/', '.').replace('\\', '.');
458 if (pkgName == null || name.startsWith(pkgName)
459 && name.substring(pkgName.length() + 1).contains(".")) {
460 name = name.replaceAll(".class$", "");
461 beans.add(Class.forName(name, true, cl));
462 }
463 }
464 }
465 return beans;
466 }
467
468 private void createOntology(List<Class<?>> beans,
469 LiteralManager<URI, Literal> manager, File output) throws Exception {
470 RDFFormat format = formatForFileName(output.getName());
471 Writer out = new FileWriter(output);
472 try {
473 RDFWriter writer = Rio.createWriter(format, out);
474 OwlGenerator gen = new OwlGenerator();
475 gen.setLiteralManager(manager);
476 writer.startRDF();
477 writer.handleNamespace("owl", OWL.NAMESPACE);
478 for (Map.Entry<URI, String> e : ontologies.entrySet()) {
479 URI ontology = e.getKey();
480 String pkgName = e.getValue();
481 String prefix = pkgName.substring(pkgName.lastIndexOf('.') + 1);
482 String namespace = ontology.toString();
483 if (!namespace.endsWith("/") && !namespace.endsWith("#"))
484 namespace += '#';
485 writer.handleNamespace(prefix, namespace);
486 gen.setNamespace(pkgName, namespace);
487 }
488 gen.exportOntology(beans, writer);
489 writer.endRDF();
490 } finally {
491 out.close();
492 }
493 }
494
495 private void createJar(Repository repository, URLClassLoader cl, File output)
496 throws Exception {
497 FileSourceCodeHandler handler = new FileSourceCodeHandler();
498 generateSourceCode(repository, cl, handler);
499 if (handler.getClasses().isEmpty())
500 throw new IllegalArgumentException("No classes found - Try a different ontology URI.");
501 JavaCompiler javac = new JavaCompiler();
502 javac.compile(handler.getTarget(), handler.getClasses(), cl);
503 packageJar(output, handler);
504 }
505
506 private void generateSourceCode(Repository repository, ClassLoader cl,
507 FileSourceCodeHandler handler) throws Exception {
508 CodeGenerator gen = new CodeGenerator();
509 gen.setPropertyNamesPrefixed(propertyNamesPrefixed);
510 if (baseClasses != null) {
511 List<Class<?>> base = new ArrayList<Class<?>>();
512 for (String bc : baseClasses) {
513 base.add(Class.forName(bc, true, cl));
514 }
515 gen.setBaseClasses(base.toArray(new Class<?>[base.size()]));
516 }
517 gen.setRepository(repository);
518 ClassLoader loader = new GroovyClassLoader(cl);
519 gen.setGroovyScriptEngine(new GroovyScriptEngine(gen, loader));
520 for (Map.Entry<URI, String> e : ontologies.entrySet()) {
521 gen.addOntologyPackage(e.getKey(), e.getValue());
522 }
523 gen.init();
524 gen.exportSourceCode(handler);
525 }
526
527 private void packageJar(File output, FileSourceCodeHandler handler)
528 throws Exception {
529 File dir = handler.getTarget();
530 FileOutputStream stream = new FileOutputStream(output);
531 JarOutputStream jar = new JarOutputStream(stream);
532 try {
533 packClasses(dir, handler.getClasses(), jar);
534 printRoles(handler.getAnnotatedClasses(), jar);
535 printLiterals(handler.getConcreteClasses(), jar);
536 packOntologies(rdfSources, jar);
537 } finally {
538 jar.close();
539 stream.close();
540 }
541 }
542
543 private void packClasses(File dir, List<String> classes, JarOutputStream jar)
544 throws IOException, FileNotFoundException {
545 for (String name : classes) {
546 String basename = name.replace('.', '/');
547 String source = basename + ".java";
548 String binary = basename + ".class";
549 File sourceFile = new File(dir, source);
550 File binaryFile = new File(dir, binary);
551 jar.putNextEntry(new JarEntry(source));
552 copyInto(sourceFile.toURI().toURL(), jar);
553 sourceFile.delete();
554 if (binaryFile.exists()) {
555 jar.putNextEntry(new JarEntry(binary));
556 copyInto(binaryFile.toURI().toURL(), jar);
557 binaryFile.delete();
558 }
559 }
560 }
561
562 private void copyInto(URL source, OutputStream out)
563 throws FileNotFoundException, IOException {
564 logger.debug("Packaging {}", source);
565 InputStream in = source.openStream();
566 try {
567 int read;
568 byte[] buf = new byte[512];
569 while ((read = in.read(buf)) > 0) {
570 out.write(buf, 0, read);
571 }
572 } finally {
573 in.close();
574 }
575 }
576
577 private void printRoles(List<String> roles, JarOutputStream jar)
578 throws IOException {
579 jar.putNextEntry(new JarEntry(META_INF_ELMO_ROLES));
580 PrintStream out = new PrintStream(jar);
581 for (String name : roles) {
582 out.println(name);
583 }
584 out.flush();
585 }
586
587 private void printLiterals(List<String> roles, JarOutputStream jar)
588 throws IOException {
589 jar.putNextEntry(new JarEntry(META_INF_ELMO_LITERALS));
590 PrintStream out = new PrintStream(jar);
591 for (String name : roles) {
592 out.println(name);
593 }
594 out.flush();
595 }
596
597 private void packOntologies(List<URL> rdfSources, JarOutputStream jar)
598 throws RepositoryException, RDFParseException, IOException {
599 Map<String, URI> ontologies = new HashMap<String, URI>();
600 for (URL rdf : rdfSources) {
601 String path = "META-INF/ontologies/";
602 path += new File(rdf.getPath()).getName();
603 URI ontology = findOntology(rdf);
604 if (ontology != null) {
605 ontologies.put(path, ontology);
606 jar.putNextEntry(new JarEntry(path));
607 copyInto(rdf, jar);
608 }
609 }
610 if (ontologies.isEmpty())
611 return;
612 jar.putNextEntry(new JarEntry(META_INF_ONTOLOGIES));
613 PrintStream out = new PrintStream(jar);
614 for (Map.Entry<String, URI> e : ontologies.entrySet()) {
615 out.print(e.getKey());
616 out.print("\t=\t");
617 out.println(e.getValue().toString());
618 }
619 out.flush();
620 }
621
622 private URI findOntology(URL rdf) throws RepositoryException,
623 RDFParseException, IOException {
624 Repository repository = createRepository();
625 loadOntology(repository, rdf);
626 RepositoryConnection conn = repository.getConnection();
627 try {
628 RepositoryResult<Statement> stmts;
629 stmts = conn.getStatements(null, RDF.TYPE, OWL.ONTOLOGY, true);
630 try {
631 if (stmts.hasNext())
632 return (URI) stmts.next().getSubject();
633 return null;
634 } finally {
635 stmts.close();
636 }
637 } finally {
638 conn.clear();
639 conn.close();
640 }
641 }
642
643 }