Commits

Sam Adams committed ea6e58b

Refactoring chemical importers to share more code

  • Participants
  • Parent commits 7d8d7ee

Comments (0)

Files changed (20)

File crystallography-common/src/main/java/net/chempound/crystal/rdf/CrystalEyeEntry.java

-package net.chempound.crystal.rdf;
-
-import com.hp.hpl.jena.rdf.model.Resource;
-
-/**
- * @author Sam Adams
- */
-public interface CrystalEyeEntry extends Resource {
-
-
-}

File crystallography-common/src/main/java/net/chempound/crystal/rdf/CrystalEyeUtil.java

-package net.chempound.crystal.rdf;
-
-import com.hp.hpl.jena.rdf.model.Model;
-import com.hp.hpl.jena.vocabulary.RDF;
-import net.chempound.crystal.rdf.ont.Cryst;
-import net.chempound.crystal.rdf.ont.CrystalEye;
-
-/**
- * @author Sam Adams
- */
-public class CrystalEyeUtil {
-
-    public static CrystalEyeEntry createCrystalEyeEntry(final Model model, final String uri) {
-        return model.createResource(uri).addProperty(RDF.type, CrystalEye.CrystalEyeEntry).as(CrystalEyeEntry.class);
-    }
-
-    public static CrystalStructure createCrystalStructure(final Model model, final String uri) {
-        return model.createResource(uri).addProperty(RDF.type, Cryst.CrystallochemicalStructure).as(CrystalStructure.class);
-    }
-}

File crystallography-common/src/main/java/net/chempound/crystal/rdf/CrystalPersonalities.java

-package net.chempound.crystal.rdf;
-
-import com.hp.hpl.jena.enhanced.BuiltinPersonalities;
-import net.chempound.crystal.rdf.impl.CrystalEyeEntryImpl;
-import net.chempound.crystal.rdf.impl.CrystalStructureImpl;
-import net.chempound.rdf.ChempoundPersonalities;
-
-/**
- * @author Sam Adams
- */
-public class CrystalPersonalities extends BuiltinPersonalities {
-
-    static {
-        ChempoundPersonalities.init();
-
-        model.add(CrystalStructure.class, CrystalStructureImpl.factory);
-        model.add(CrystalEyeEntry.class, CrystalEyeEntryImpl.factory);
-    }
-
-    public static void init() { }
-
-}

File crystallography-common/src/main/java/net/chempound/crystal/rdf/CrystalStructure.java

 
 import com.hp.hpl.jena.rdf.model.Resource;
 
+import java.net.URI;
+
 /**
  * @author Sam Adams
  */
 public interface CrystalStructure extends Resource {
 
+    String getTitle();
+
+
+    String getJournalNameFull();
+
+    String getJournalYear();
+
+    String getJournalVolume();
+
+    String getJournalIssue();
+
+
+    String getPublContactAuthorName();
+
+    String getPublContactAuthorEmail();
+
+
     UnitCell getUnitCell();
 
 
 
     Double getRefineLsWrFactorRef();
 
+
+    URI getCifFile();
+
+    URI getCmlFile();
+
+
+    URI getRdfXmlFile();
+
+    URI getRdfN3File();
+
+
+    URI getImageFile();
+
+    String getCifPath();
+
+
 }

File crystallography-common/src/main/java/net/chempound/crystal/rdf/CrystalStructurePersonalities.java

+package net.chempound.crystal.rdf;
+
+import com.hp.hpl.jena.enhanced.BuiltinPersonalities;
+import net.chempound.crystal.rdf.impl.CrystalStructureImpl;
+import net.chempound.rdf.ChempoundPersonalities;
+
+/**
+ * @author Sam Adams
+ */
+public class CrystalStructurePersonalities extends BuiltinPersonalities {
+
+    static {
+        ChempoundPersonalities.init();
+
+        model.add(CrystalStructure.class, CrystalStructureImpl.factory);
+    }
+
+    public static void init() { }
+
+}

File crystallography-common/src/main/java/net/chempound/crystal/rdf/impl/CrystalEyeEntryImpl.java

-package net.chempound.crystal.rdf.impl;
-
-import com.hp.hpl.jena.enhanced.EnhGraph;
-import com.hp.hpl.jena.enhanced.EnhNode;
-import com.hp.hpl.jena.enhanced.Implementation;
-import com.hp.hpl.jena.graph.Node;
-import com.hp.hpl.jena.rdf.model.Model;
-import com.hp.hpl.jena.rdf.model.ResIterator;
-import com.hp.hpl.jena.rdf.model.Resource;
-import com.hp.hpl.jena.sparql.vocabulary.FOAF;
-import com.hp.hpl.jena.vocabulary.RDF;
-import net.chempound.crystal.rdf.CrystalEyeEntry;
-import net.chempound.crystal.rdf.CrystalStructure;
-import net.chempound.crystal.rdf.ont.CrystalEye;
-import net.chempound.rdf.Bibo;
-import net.chempound.rdf.chempound.ItemImpl;
-import net.chempound.rdf.dc.DCUtils;
-import net.chempound.util.MimeType;
-import org.apache.commons.io.FilenameUtils;
-
-import java.net.URI;
-
-/**
- * @author Sam Adams
- */
-public class CrystalEyeEntryImpl extends ItemImpl implements CrystalEyeEntry {
-
-    public static Implementation factory = new Implementation() {
-        @Override
-        public EnhNode wrap(final Node node, final EnhGraph eg) {
-            return new CrystalEyeEntryImpl(node, eg);
-        }
-        @Override
-        public boolean canWrap(final Node node, final EnhGraph eg) {
-            return !node.isLiteral();
-        }
-    };
-
-    public CrystalEyeEntryImpl(final Node n, final EnhGraph m) {
-        super(n, m);
-    }
-
-    //    public CrystalEyeEntryImpl(Resource resource) {
-//        super(resource);
-//    }
-//
-//    public CrystalEyeEntryImpl(URI uri, Model model) {
-//        super(uri, model);
-//        addType(CrystalEye.CrystalEyeEntry);
-//    }
-
-    public CrystalStructure getCrystalStructure() {
-        final Resource r = getPropertyResourceValue(CrystalEye.crystal);
-        return r == null ? null : r.as(CrystalStructure.class);
-    }
-
-    public void setCrystalStructure(final CrystalStructureImpl crystal) {
-        addProperty(CrystalEye.crystal, crystal);
-    }
-
-    public static CrystalEyeEntry find(final Model model) {
-        final ResIterator it = model.listResourcesWithProperty(RDF.type, CrystalEye.CrystalEyeEntry);
-        if (it.hasNext()) {
-            return it.next().as(CrystalEyeEntry.class);
-        }
-        return null;
-    }
-
-    public URI getImageFile() {
-        final Resource r = getPropertyResourceValue(FOAF.img);
-        return r == null ? null : URI.create(r.getURI());
-    }
-
-    private URI getFile(final String type) {
-        for (final Resource r : getAggregatedResources()) {
-            final MimeType mt = DCUtils.getMimeType(r);
-            if (mt != null && type.equals(mt.getName())) {
-                return URI.create(r.getURI());
-            }
-        }
-        return null;
-    }
-
-    public URI getCifFile() {
-        return getFile("chemical/x-cif");
-    }
-
-    public URI getHtmlFile() {
-        for (final Resource r : getResourceMaps()) {
-            if (r.getURI().endsWith("html")) {
-                return URI.create(r.getURI());
-            }
-        }
-        return null;
-    }
-
-
-    public URI getRdfXmlFile() {
-        for (final Resource r : getResourceMaps()) {
-            if (r.getURI().endsWith("rdf")) {
-                return URI.create(r.getURI());
-            }
-        }
-        return null;
-    }
-
-    public URI getRdfN3File() {
-        for (final Resource r : getResourceMaps()) {
-            if (r.getURI().endsWith("n3")) {
-                return URI.create(r.getURI());
-            }
-        }
-        return null;
-    }
-
-
-    public URI getCmlFile() {
-        return getFile("chemical/x-cml");
-    }
-
-    public String getCmlFileName() {
-        final URI uri = getCmlFile();
-        return FilenameUtils.getName(uri.toString());
-    }
-
-    public String getCifPath() {
-        final URI html = getHtmlFile();
-        final URI cif = getCifFile();
-        final URI uri = html.resolve("./").relativize(cif);
-        return uri.toString();
-    }
-
-    public String getCmlPath() {
-        final URI html = getHtmlFile();
-        final URI cml = getCmlFile();
-        final URI uri = html.resolve("./").relativize(cml);
-        return uri.toString();
-    }
-
-
-    public void setDoi(final String doi) {
-        addProperty(Bibo.doi, doi);
-    }
-
-    public String getDoi() {
-        return getPropertyStringValue(Bibo.doi);
-    }
-
-}

File crystallography-common/src/main/java/net/chempound/crystal/rdf/impl/CrystalStructureImpl.java

 import com.hp.hpl.jena.enhanced.Implementation;
 import com.hp.hpl.jena.graph.Node;
 import com.hp.hpl.jena.rdf.model.*;
+import com.hp.hpl.jena.sparql.vocabulary.FOAF;
 import com.hp.hpl.jena.vocabulary.RDF;
 import com.hp.hpl.jena.vocabulary.RDFS;
 import net.chempound.crystal.rdf.CrystalStructure;
 import net.chempound.rdf.Bibo;
 import net.chempound.rdf.chempound.ItemImpl;
 import net.chempound.rdf.cml.CmlRdf;
+import net.chempound.rdf.dc.DCUtils;
+import net.chempound.util.MimeType;
 
+import java.net.URI;
 import java.util.ArrayList;
 import java.util.List;
 
         public EnhNode wrap(final Node node, final EnhGraph eg) {
             return new CrystalStructureImpl(node, eg);
         }
+
         @Override
         public boolean canWrap(final Node node, final EnhGraph eg) {
             return !node.isLiteral();
     }
 
 
+    @Override
+    public String getJournalNameFull() {
+        return getPropertyStringValue(CifDict.journalNameFull);
+    }
+
+    @Override
+    public String getJournalYear() {
+        return getPropertyStringValue(CifDict.journalYear);
+    }
+
+    @Override
+    public String getJournalVolume() {
+        return getPropertyStringValue(CifDict.journalVolume);
+    }
+
+    @Override
+    public String getJournalIssue() {
+        return getPropertyStringValue(CifDict.journalIssue);
+    }
+
+    @Override
+    public String getPublContactAuthorName() {
+        return getPropertyStringValue(CifDict.publContactAuthorName);
+    }
+
+    @Override
+    public String getPublContactAuthorEmail() {
+        return getPropertyStringValue(CifDict.publContactAuthorEmail);
+    }
+
     public UnitCell getUnitCell() {
         if (unitCell == null) {
             Resource r = getPropertyResourceValue(Cryst.hasUnitCell);
 
     public List<Moiety> getMoieties() {
         final List<Moiety> list = new ArrayList<Moiety>();
-        for (StmtIterator it = listProperties(Cryst.hasMoiety); it.hasNext();) {
+        for (StmtIterator it = listProperties(Cryst.hasMoiety); it.hasNext(); ) {
             final Statement s = it.next();
             list.add(new Moiety(s.getResource()));
         }
     }
 
 
-    public String getString(final String url) {
-        final Statement s = getProperty(getModel().getProperty(url));
-        if (s == null) {
-            return null;
-        }
-        if (s.getObject().isLiteral()) {
-            return s.getString();
-        }
-        final Statement s2 = s.getObject().asResource().getProperty(RDF.value);
-        return s2 == null ? null : s2.getString();
-    }
-
     public static CrystalStructure find(final Model model) {
         final ResIterator it = model.listResourcesWithProperty(RDF.type, Cryst.CrystallochemicalStructure);
         if (it.hasNext()) {
         return getPropertyDoubleValue(CifDict.refineLsWrFactorRef);
     }
 
+
+    @Override
+    public URI getCifFile() {
+        return getFile("chemical/x-cif");
+    }
+
+    @Override
+    public URI getCmlFile() {
+        return getFile("chemical/x-cml");
+    }
+
+    @Override
+    public URI getRdfXmlFile() {
+        for (final Resource r : getResourceMaps()) {
+            if (r.getURI().endsWith("rdf")) {
+                return URI.create(r.getURI());
+            }
+        }
+        return null;
+    }
+
+    @Override
+    public URI getRdfN3File() {
+        for (final Resource r : getResourceMaps()) {
+            if (r.getURI().endsWith("n3")) {
+                return URI.create(r.getURI());
+            }
+        }
+        return null;
+    }
+
+    @Override
+    public URI getImageFile() {
+        final Resource r = getPropertyResourceValue(FOAF.img);
+        return r == null ? null : URI.create(r.getURI());
+    }
+
+    @Override
+    public String getCifPath() {
+        final URI html = getHtmlFile();
+        final URI cif = getCifFile();
+        final URI uri = html.resolve("./").relativize(cif);
+        return uri.toString();
+    }
+
+    private URI getFile(final String type) {
+        for (final Resource r : getAggregatedResources()) {
+            final MimeType mt = DCUtils.getMimeType(r);
+            if (mt != null && type.equals(mt.getName())) {
+                return URI.create(r.getURI());
+            }
+        }
+        return null;
+    }
+
+    private URI getHtmlFile() {
+        for (final Resource r : getResourceMaps()) {
+            if (r.getURI().endsWith("html")) {
+                return URI.create(r.getURI());
+            }
+        }
+        return null;
+    }
+
 }

File crystallography-common/src/main/java/net/chempound/crystal/rdf/ont/CrystalEye.java

-package net.chempound.crystal.rdf.ont;
-
-import com.hp.hpl.jena.rdf.model.Model;
-import com.hp.hpl.jena.rdf.model.ModelFactory;
-import com.hp.hpl.jena.rdf.model.Property;
-import com.hp.hpl.jena.rdf.model.Resource;
-
-/**
- * @author Sam Adams
- */
-public class CrystalEye {
-
-    /** <p>The RDF model that holds the vocabulary terms</p> */
-    private static final Model m_model = ModelFactory.createDefaultModel();
-
-    /** <p>The namespace of the vocabulary as a string</p> */
-    public static final String NS = "http://crystaleye.ch.cam.ac.uk/-/schema/";
-
-    public static final Resource CrystalEyeIndex = m_model.createResource(NS+"Index");
-    public static final Resource CrystalEyeEntry = m_model.createResource(NS+"Entry");
-    public static final Resource CrystalEyeIssue = m_model.createResource(NS+"Issue");
-    public static final Resource CrystalEyeVolume = m_model.createResource(NS+"Volume");
-    public static final Resource CrystalEyeJournal = m_model.createResource(NS+"Journal");
-
-    public static final Resource Image = m_model.createResource(NS+"Image");
-    public static final Resource Thumbnail = m_model.createResource(NS+"Thumbnail");
-    public static final Resource Cif = m_model.createResource(NS+"Cif");
-    public static final Resource Cml = m_model.createResource(NS+"Cml");
-
-    public static final Property crystal = m_model.createProperty(NS+"crystal");
-    
-
-    
-
-
-}

File crystallography-handler/src/main/java/net/chempound/crystal/CrystallographyModule.java

 package net.chempound.crystal;
 
 import net.chempound.crystal.display.CrystalContentType;
-import net.chempound.crystal.rdf.CrystalPersonalities;
+import net.chempound.crystal.rdf.CrystalStructurePersonalities;
 import net.chempound.crystal.search.CrystalSearchProvider;
 import net.chempound.webapp.plugins.ChempoundPluginModule;
 
 public class CrystallographyModule extends ChempoundPluginModule {
 
     static {
-        CrystalPersonalities.init();
+        CrystalStructurePersonalities.init();
     }
 
     @Override

File crystallography-handler/src/main/java/net/chempound/crystal/display/CrystalStructureSplashPageWriter.java

 
 import com.hp.hpl.jena.rdf.model.Model;
 import com.hp.hpl.jena.rdf.model.Resource;
-import freemarker.ext.beans.BeanModel;
 import freemarker.ext.beans.BeansWrapper;
-import freemarker.template.Template;
 import freemarker.template.TemplateException;
 import freemarker.template.TemplateHashModel;
-import net.chempound.crystal.rdf.CrystalEyeEntry;
 import net.chempound.crystal.rdf.CrystalStructure;
 import net.chempound.crystal.rdf.ont.CifDict;
 import net.chempound.crystal.rdf.ont.Cryst;
 import net.chempound.rdf.DCTerms;
 import net.chempound.services.FreemarkerService;
-import net.chempound.webapp.output.SplashPageWriter;
+import net.chempound.webapp.output.AbstractSplashPageWriter;
 import org.apache.commons.io.IOUtils;
 
 import javax.inject.Inject;
 import java.io.IOException;
 import java.io.InputStream;
-import java.io.StringWriter;
 import java.net.URI;
-import java.util.HashMap;
 import java.util.Map;
 
 /**
  * @author Sam Adams
  */
-public class CrystalStructureSplashPageWriter implements SplashPageWriter {
+public class CrystalStructureSplashPageWriter extends AbstractSplashPageWriter {
 
     private static final String style;
     private static final String headers;
         }
     }
 
-    private final FreemarkerService freemarker;
-
     @Inject
     public CrystalStructureSplashPageWriter(final FreemarkerService freemarker) {
-        this.freemarker = freemarker;
+        super(freemarker);
+    }
+
+    @Override
+    protected Class<? extends Resource> getResourceType() {
+        return CrystalStructure.class;
+    }
+
+    @Override
+    protected String getTemplateName() {
+        return "/plugins/crystal/crystal.ftl";
     }
 
     @Override
     public String write(final Model model, final URI aggregationUri, final Map<String, Object> map) throws IOException, TemplateException {
-
         map.put("style", style);
         map.put("headers", headers);
 
-        final Map<String,Object> o = new HashMap<String, Object>(map);
-
-        final Resource r = model.getResource(aggregationUri.toString());
-
-        final CrystalStructure cryst = r.as(CrystalStructure.class);
-        final CrystalEyeEntry entry = r.as(CrystalEyeEntry.class);
-
         final BeansWrapper wrapper = BeansWrapper.getDefaultInstance();
         final TemplateHashModel staticModels = wrapper.getStaticModels();
 
-        o.put("entry", new BeanModel(entry, wrapper));
-        o.put("cryst", new BeanModel(cryst, wrapper));
-        o.put("DC", staticModels.get(DCTerms.class.getName()));
-        o.put("CIF", staticModels.get(Cryst.class.getName()));
-        o.put("IUCR", staticModels.get(CifDict.class.getName()));
+        map.put("DC", staticModels.get(DCTerms.class.getName()));
+        map.put("CIF", staticModels.get(Cryst.class.getName()));
+        map.put("IUCR", staticModels.get(CifDict.class.getName()));
 
-        final StringWriter buffer = new StringWriter();
-
-        final Template template = freemarker.getTemplate("/plugins/crystal/crystal.ftl");
-        template.process(o, buffer);
-
-        return buffer.toString();
+        return super.write(model, aggregationUri, map);
     }
 
 }

File crystallography-handler/src/main/resources/net/chempound/crystal/templates/crystal.ftl

-<#-- @ftlvariable name="cryst" type="net.chempound.crystal.rdf.CrystalStructure" -->
+<#-- @ftlvariable name="_" type="net.chempound.crystal.rdf.CrystalStructure" -->
 <div class="crystaleye-structure">
 
-    <h1><#if cryst.getString(DC.title)??>${cryst.getString(DC.title)?html}<#else>${uri}</#if></h1>
+    <h1><#if _.title??>${_.title}<#else>${uri}</#if></h1>
 
     <div class="jmol">
         <div id="jmol"></div>
 
         <dl class="bib bib-citation">
             <dt>Publisher:</dt>
-            <dd>${cryst.getString(IUCR.journalNameFull)!}</dd>
+            <dd>${_.journalNameFull!}</dd>
             <dt>Journal:</dt>
-            <dd>${cryst.getString(IUCR.journalNameFull)!}</dd>
+            <dd>${_.journalNameFull!}</dd>
             <dt>Year:</dt>
-            <dd>${cryst.getString(IUCR.journalYear)!}</dd>
+            <dd>${_.journalYear!}</dd>
             <dt>Volume/Issue:</dt>
-            <dd>${cryst.getString(IUCR.journalVolume)!} / ${cryst.getString(IUCR.journalIssue)!}</dd>
+            <dd>${_.journalVolume!} / ${_.journalIssue!}</dd>
             <dt>DOI:</dt>
-            <dd><#if entry.doi??><a href="http://dx.doi.org/${entry.doi}">${entry.doi}</a></#if></dd>
+            <dd><#if _.doi??><a href="http://dx.doi.org/${_.doi}">${_.doi}</a></#if></dd>
         </dl>
 
         <dl class="bib bib-author">
             <dt>Contact Author:</dt>
-            <dd>${(cryst.getString(IUCR.publContactAuthorName)!)?html}</dd>
+            <dd>${(_.publContactAuthorName!)?html}</dd>
             <dt>email:</dt>
-            <dd><#if cryst.getString(IUCR.publContactAuthorName)??><a href="mailto:${cryst.getString(IUCR.publContactAuthorEmail)}">${cryst.getString(IUCR.publContactAuthorEmail)}</a></#if></dd>
+            <dd><#if _.publContactAuthorName??><a href="mailto:${_.publContactAuthorEmail}">${_.publContactAuthorEmail}</a></#if></dd>
         </dl>
 
+
         <h2>Data collection parameters</h2>
 
         <dl class="data">
             <dt>Chemical formula sum</dt>
-            <dd property="${IUCR.chemicalFormulaSum}">${cryst.chemicalFormulaSum!}</dd>
+            <dd property="${IUCR.chemicalFormulaSum}">${_.chemicalFormulaSum!}</dd>
             <dt>Chemical formula moiety</dt>
-            <dd property="${IUCR.chemicalFormulaMoiety}">${cryst.chemicalFormulaMoiety!}</dd>
+            <dd property="${IUCR.chemicalFormulaMoiety}">${_.chemicalFormulaMoiety!}</dd>
             <dt>Crystal system</dt>
-            <dd property="${IUCR.spaceGroupCrystalSystem}">${cryst.spaceGroupCrystalSystem!}</dd>
+            <dd property="${IUCR.spaceGroupCrystalSystem}">${_.spaceGroupCrystalSystem!}</dd>
             <dt>Space group H-M</dt>
-            <dd property="${IUCR.symmetrySpaceGroupNameHM}">${cryst.spaceGroupNameHMAlt!}</dd>
+            <dd property="${IUCR.symmetrySpaceGroupNameHM}">${_.spaceGroupNameHMAlt!}</dd>
             <dt>Space group Hall</dt>
-            <dd property="${IUCR.symmetrySpaceGroupNameHall}">${cryst.spaceGroupNameHall!}</dd>
+            <dd property="${IUCR.symmetrySpaceGroupNameHall}">${_.spaceGroupNameHall!}</dd>
             <dt>Data collection temperature</dt>
-            <dd property="${IUCR.diffrnAmbientTemperature}">${cryst.diffrnAmbientTemperature!} K</dd>
+            <dd property="${IUCR.diffrnAmbientTemperature}">${_.diffrnAmbientTemperature!} K</dd>
         </dl>
 
 
 
         <dl class="data">
             <dt>R Factor (Obs)</dt>
-            <dd property="${IUCR.refineLsRFactorGt}">${cryst.refineLsRFactorGt!}</dd>
+            <dd property="${IUCR.refineLsRFactorGt}">${_.refineLsRFactorGt!}</dd>
             <dt>R Factor (All)</dt>
-            <dd property="${IUCR.refineLsRFactorAll}">${cryst.refineLsRFactorAll!}</dd>
+            <dd property="${IUCR.refineLsRFactorAll}">${_.refineLsRFactorAll!}</dd>
             <dt>Weighted R Factor (Obs)</dt>
-            <dd property="${IUCR.refineLsWrFactorGt}">${cryst.refineLsWrFactorGt!}</dd>
+            <dd property="${IUCR.refineLsWrFactorGt}">${_.refineLsWrFactorGt!}</dd>
             <dt>Weighted R Factor (All)</dt>
-            <dd property="${IUCR.refineLsWrFactorRef}">${cryst.refineLsWrFactorRef!}</dd>
+            <dd property="${IUCR.refineLsWrFactorRef}">${_.refineLsWrFactorRef!}</dd>
         </dl>
 
 
-<#--
-        <h2>Crystal Components</h2>
-
-        <ul class="moieties">
-            <li><a href="${moietiesRef}">Moieties</a></li>
-        </ul>
--->
-
         <h2>Result files</h2>
 
         <ul class="files">
-            <li><a href="${entry.cmlFile}">Chemical Markup Language</a></li>
-            <li><#if cifOriginal??><a href="${cifOriginal}" title="CIF file - original">CIF file</a> [<a href="${entry.cifFile}" title="CIF file - local cache">local cache</a>]<#else><a href="${entry.cifFile}" title="CIF file">CIF File</a></#if> </li>
-            <li><a href="${entry.rdfXmlFile}">RDF XML</a> / <a href="${entry.rdfN3File}">RDF N3</a></li>
+            <li><a href="${_.cmlFile}">Chemical Markup Language</a></li>
+            <li><#if cifOriginal??><a href="${cifOriginal}" title="CIF file - original">CIF file</a> [<a href="${_.cifFile}" title="CIF file - local cache">local cache</a>]<#else><a href="${_.cifFile}" title="CIF file">CIF File</a></#if> </li>
+            <li><a href="${_.rdfXmlFile}">RDF XML</a> / <a href="${_.rdfN3File}">RDF N3</a></li>
         </ul>
 
-<#--
-        <h2>Validation</h2>
-
-        <ul class="files">
-            <li><a href="${checkCifFile}">CheckCIF</a></li>
-        </ul>
-
-
-        <h2>Images</h2>
-
-        <ul class="files">
-            <li><a href="${ellipsoidUri}">Ellipsoid</a></li>
-        </ul>
--->
-
     </div>
 
     <div class="clear"></div>
 
         <dl class="identifier">
             <dt>InChI:</dt>
-            <dd>${cryst.inchi}</dd>
+            <dd>${_.inchi}</dd>
             <dt>InChIKey:</dt>
-            <dd>${cryst.inchiKey}
-                <a href="${URI_BASE}/search/?search_provider=structure&inchi_key=${cryst.inchiKey}&submit=search"">[?]</a></dd>
+            <dd>${_.inchiKey}
+                <a href="${URI_BASE}/search/?search_provider=structure&inchi_key=${_.inchiKey}&submit=search"" rel="nofollow">[?]</a></dd>
         </dl>
 
-<#list entry.molecularEntities as entry>
+<#list _.molecularEntities as entry>
         <dl class="identifier">
             <dt>InChI:</dt>
             <dd>${entry.inchi}</dd>
             <dt>InChIKey:</dt>
             <dd>${entry.inchiKey}
-                <a href="${URI_BASE}/search/?search_provider=structure&inchi_key=${entry.inchiKey}&submit=search"">[?]</a></dd>
+                <a href="${URI_BASE}/search/?search_provider=structure&inchi_key=${entry.inchiKey}&submit=search"" rel="nofollow">[?]</a></dd>
         </dl>
 </#list>
         <div class="clear"></div>
         jmolInitialize("/plugins/jmol/jmol/", useSignedApplet);
 
         $("#jmol").jmol({
-            'img': "${entry.imageFile}",
-            'url': "${entry.cifPath}",
+            'img': "${_.imageFile}",
+            'url': "${_.cifPath}",
             'script': "load $ {1 1 1}"
         });
     });

File crystallography-importer/src/main/java/net/chempound/crystal/importer/CifStringParser.java

-/*
- * Copyright 2010-2011 Nick Day, Sam Adams
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package net.chempound.crystal.importer;
-
-import sea36.util.unicode.accents.Acute;
-import sea36.util.unicode.accents.Grave;
-import sea36.util.unicode.blocks.*;
-
-/**
- * @author Sam Adams
- */
-public class CifStringParser {
-
-    public static final String toHtml(final String s) {
-        final StringBuilder html = new StringBuilder();
-
-        boolean sup = false, sub = false;
-
-        for (int i = 0; i < s.length(); i++) {
-
-            final char c = s.charAt(i);
-
-            switch (c) {
-                case '\\':
-                    i += readChar(s, i+1, html);
-                    break;
-
-                case '+':
-                    if ("+-".equals(s.substring(i, i+2))) {
-                        html.append(Latin1Supplement.Plus_Minus_Sign);
-                    }
-                    i += 1;
-                    break;
-
-                case '-':
-                    if ("---".equals(s.substring(i, i+3))) {
-                        // TODO single bond
-                        html.append(GeneralPunctuation.Em_Dash);
-                        i += 2;
-                    }
-                    else if ("-+".equals(s.substring(i, i+2))) {
-                        html.append(MathematicalOperators.Minus_Or_Plus_Sign);
-                        i += 1;
-                    }
-                    else if ("--".equals(s.substring(i, i+2))) {
-                        html.append(GeneralPunctuation.En_Dash);
-                        i += 1;
-                    } else {
-                        html.append(c);
-                    }
-                    break;
-
-                case '<':
-                    if (
-                            "<i>".equals(s.substring(i, i+3))
-                                    || "<b>".equals(s.substring(i, i+3))) {
-                        html.append(s.substring(i, i+3));
-                        i += 2;
-                    } else if (
-                            "</i>".equals(s.substring(i, i+4))
-                                    || "</b>".equals(s.substring(i, i+4))) {
-                        html.append(s.substring(i, i+4));
-                        i += 3;
-                    } else {
-                        throw new IllegalArgumentException("Bad string: "+s);
-                    }
-                    break;
-
-                case '^':    // superscript
-                    if (sup) {
-                        html.append("</sup>");
-                    } else {
-                        html.append("<sub>");
-                    }
-                    sup = !sup;
-                    break;
-
-                case '~':   // subscript
-                    if (sub) {
-                        html.append("</sub>");
-                    } else {
-                        html.append("<sub>");
-                    }
-                    sub = !sub;
-                    break;
-
-                default:
-                    html.append(c);
-            }
-        }
-        return html.toString();
-    }
-
-    private static int readChar(final String s, final int i, final StringBuilder html) {
-        final char c = s.charAt(i);
-
-        switch (c) {
-
-            case '\'':
-                html.append(readAcuteChar(s.charAt(i + 1)));
-                return 2;
-            case '`':
-                html.append(readGraveChar(s.charAt(i + 1)));
-                return 2;
-            case '^':
-                html.append(readCircumflexChar(s.charAt(i + 1)));
-                return 2;
-
-            case ',':
-                throw new IllegalArgumentException("Unsupported cedilla character: "+s.charAt(i + 1));
-            case '"':
-                throw new IllegalArgumentException("Unsupported umlaut character: "+s.charAt(i + 1));
-            case '~':
-                throw new IllegalArgumentException("Unsupported tilde character: "+s.charAt(i + 1));
-            case ';':
-                throw new IllegalArgumentException("Unsupported ogonek character: "+s.charAt(i + 1));
-            case '>':
-                throw new IllegalArgumentException("Unsupported double acute character: "+s.charAt(i + 1));
-            case '=':
-                throw new IllegalArgumentException("Unsupported overbar character: "+s.charAt(i + 1));
-            case '.':
-                throw new IllegalArgumentException("Unsupported overdot character: "+s.charAt(i + 1));
-            case '<':
-                throw new IllegalArgumentException("Unsupported caron character: "+s.charAt(i + 1));
-            case '(':
-                throw new IllegalArgumentException("Unsupported breve character: "+s.charAt(i + 1));
-
-            case '?':
-                if ('i' == s.charAt(i+1)) {
-                    html.append(LatinExtendedA.Latin_Small_Letter_Dotless_I);
-                    return 2;
-                }
-                throw new IllegalArgumentException("Unknown char: "+s.charAt(i+1));
-
-            case '&':
-                if ('s' == s.charAt(i+1)) {
-                    html.append(Latin1Supplement.Latin_Small_Letter_Sharp_S);
-                    return 2;
-                }
-                break;
-
-            case '%':
-                if ('A' == s.charAt(i+1)) {
-                    html.append(Latin1Supplement.Latin_Capital_Letter_A_With_Ring_Above);
-                    return 2;
-                }
-                if ('a' == s.charAt(i+1)) {
-                    html.append(Latin1Supplement.Latin_Small_Letter_A_With_Ring_Above);
-                    return 2;
-                }
-                html.append(Latin1Supplement.Degree_Sign);
-                return 1;
-
-            case '/':
-                if ('O' == s.charAt(i+1)) {
-                    html.append(Latin1Supplement.Latin_Capital_Letter_O_With_Stroke);
-                    return 2;
-                }
-                if ('o' == s.charAt(i+1)) {
-                    html.append(Latin1Supplement.Latin_Small_Letter_O_With_Stroke);
-                    return 2;
-                }
-
-                if ('L' == s.charAt(i+1)) {
-                    html.append(LatinExtendedA.Latin_Capital_Letter_L_With_Stroke);
-                    return 2;
-                }
-                if ('l' == s.charAt(i+1)) {
-                    html.append(LatinExtendedA.Latin_Small_Letter_L_With_Stroke);
-                    return 2;
-                }
-
-                if ('D' == s.charAt(i+1)) {
-                    html.append(Latin1Supplement.Latin_Capital_Letter_Eth);
-                    return 2;
-                }
-                if ('d' == s.charAt(i+1)) {
-                    html.append(Latin1Supplement.Latin_Small_Letter_Eth);
-                    return 2;
-                }
-
-                throw new IllegalArgumentException("Unknown char: "+s.charAt(i+1));
-
-
-            case '\\':
-                // db = double bond
-                // tb = triple bond
-                // ddb = delocalised double bond            * -- these 3 should be followed by space
-                // sim = ~
-                // simeq = @
-                // square = square
-                // rangle = >
-                // langle = <
-
-                if ("times".equals(s.substring(i+1, i+6))) {
-                    html.append(Latin1Supplement.Multiplication_Sign);
-                    return 6;
-                }
-                if ("infty".equals(s.substring(i+1, i+6))) {
-                    html.append(MathematicalOperators.Infinity);
-                    return 6;
-                }
-                if ("neq".equals(s.substring(i+1, i+4))) {
-                    html.append(MathematicalOperators.Not_Equal_To);
-                    return 4;
-                }
-                if ("rightarrow".equals(s.substring(i+1, i+11))) {
-                    html.append(Arrows.Rightwards_Arrow);
-                    return 11;
-                }
-                if ("leftarrow".equals(s.substring(i+1, i+10))) {
-                    html.append(Arrows.Leftwards_Arrow);
-                    return 10;
-                }
-
-                if ("square".equals(s.substring(i+1, i+7))) {
-                    html.append(GeometricShapes.White_Square);
-                    return 7;
-                }
-
-                throw new UnsupportedOperationException();
-
-        }
-
-        final char x = getChar(c);
-        html.append(x);
-        return 1;
-    }
-
-
-
-    private static char readAcuteChar(final char c) {
-        switch (c) {
-            case 'A':
-                return Acute.Capital_Letter_A_With_Acute;
-            case 'C':
-                return Acute.Capital_Letter_C_With_Acute;
-            case 'E':
-                return Acute.Capital_Letter_E_With_Acute;
-            case 'G':
-                return Acute.Capital_Letter_G_With_Acute;
-            case 'I':
-                return Acute.Capital_Letter_I_With_Acute;
-            case 'L':
-                return Acute.Capital_Letter_L_With_Acute;
-            case 'M':
-                return Acute.Capital_Letter_M_With_Acute;
-            case 'N':
-                return Acute.Capital_Letter_N_With_Acute;
-            case 'O':
-                return Acute.Capital_Letter_O_With_Acute;
-            case 'P':
-                return Acute.Capital_Letter_P_With_Acute;
-            case 'R':
-                return Acute.Capital_Letter_R_With_Acute;
-            case 'S':
-                return Acute.Capital_Letter_S_With_Acute;
-            case 'U':
-                return Acute.Capital_Letter_U_With_Acute;
-            case 'W':
-                return Acute.Capital_Letter_W_With_Acute;
-            case 'Y':
-                return Acute.Capital_Letter_Y_With_Acute;
-            case 'Z':
-                return Acute.Capital_Letter_Z_With_Acute;
-
-            case 'a':
-                return Acute.Small_Letter_A_With_Acute;
-            case 'c':
-                return Acute.Small_Letter_C_With_Acute;
-            case 'e':
-                return Acute.Small_Letter_E_With_Acute;
-            case 'g':
-                return Acute.Small_Letter_G_With_Acute;
-            case 'i':
-                return Acute.Small_Letter_I_With_Acute;
-            case 'l':
-                return Acute.Small_Letter_L_With_Acute;
-            case 'm':
-                return Acute.Small_Letter_M_With_Acute;
-            case 'n':
-                return Acute.Small_Letter_N_With_Acute;
-            case 'o':
-                return Acute.Small_Letter_O_With_Acute;
-            case 'p':
-                return Acute.Small_Letter_P_With_Acute;
-            case 'r':
-                return Acute.Small_Letter_R_With_Acute;
-            case 's':
-                return Acute.Small_Letter_S_With_Acute;
-            case 'u':
-                return Acute.Small_Letter_U_With_Acute;
-            case 'w':
-                return Acute.Small_Letter_W_With_Acute;
-            case 'y':
-                return Acute.Small_Letter_Y_With_Acute;
-            case 'z':
-                return Acute.Small_Letter_Z_With_Acute;
-        }
-        throw new IllegalArgumentException("Unsupported acute character: "+c);
-    }
-
-    private static char readGraveChar(final char c) {
-        switch (c) {
-            case 'A':
-                return Grave.Capital_Letter_A_With_Grave;
-            case 'E':
-                return Grave.Capital_Letter_E_With_Grave;
-            case 'I':
-                return Grave.Capital_Letter_I_With_Grave;
-            case 'N':
-                return Grave.Capital_Letter_N_With_Grave;
-            case 'O':
-                return Grave.Capital_Letter_O_With_Grave;
-            case 'U':
-                return Grave.Capital_Letter_U_With_Grave;
-            case 'W':
-                return Grave.Capital_Letter_W_With_Grave;
-            case 'Y':
-                return Grave.Capital_Letter_Y_With_Grave;
-            
-            case 'a':
-                return Grave.Small_Letter_A_With_Grave;
-            case 'e':
-                return Grave.Small_Letter_E_With_Grave;
-            case 'i':
-                return Grave.Small_Letter_I_With_Grave;
-            case 'n':
-                return Grave.Small_Letter_N_With_Grave;
-            case 'o':
-                return Grave.Small_Letter_O_With_Grave;
-            case 'u':
-                return Grave.Small_Letter_U_With_Grave;
-            case 'w':
-                return Grave.Small_Letter_W_With_Grave;
-            case 'y':
-                return Grave.Small_Letter_Y_With_Grave;
-        }
-        throw new IllegalArgumentException("Unsupported grave character: "+c);
-    }
-
-    private static char readCircumflexChar(final char c) {
-
-        throw new IllegalArgumentException("Unsupported circumflex character: "+c);
-    }
-
-
-    private static char getChar(final char c) {
-
-        switch (c) {
-            case 'A':
-                return GreekAndCoptic.Greek_Capital_Letter_Alpha;
-            case 'B':
-                return GreekAndCoptic.Greek_Capital_Letter_Beta;
-            case 'C':
-                return GreekAndCoptic.Greek_Capital_Letter_Chi;
-            case 'D':
-                return GreekAndCoptic.Greek_Capital_Letter_Delta;
-            case 'E':
-                return GreekAndCoptic.Greek_Capital_Letter_Epsilon;
-            case 'F':
-                return GreekAndCoptic.Greek_Capital_Letter_Phi;
-            case 'G':
-                return GreekAndCoptic.Greek_Capital_Letter_Gamma;
-            case 'H':
-                return GreekAndCoptic.Greek_Capital_Letter_Eta;
-            case 'I':
-                return GreekAndCoptic.Greek_Capital_Letter_Iota;
-            case 'K':
-                return GreekAndCoptic.Greek_Capital_Letter_Kappa;
-            case 'L':
-                return GreekAndCoptic.Greek_Capital_Letter_Lamda;
-            case 'M':
-                return GreekAndCoptic.Greek_Capital_Letter_Mu;
-            case 'N':
-                return GreekAndCoptic.Greek_Capital_Letter_Nu;
-            case 'O':
-                return GreekAndCoptic.Greek_Capital_Letter_Omicron;
-            case 'P':
-                return GreekAndCoptic.Greek_Capital_Letter_Pi;
-            case 'Q':
-                return GreekAndCoptic.Greek_Capital_Letter_Theta;
-            case 'R':
-                return GreekAndCoptic.Greek_Capital_Letter_Rho;
-            case 'S':
-                return GreekAndCoptic.Greek_Capital_Letter_Sigma;
-            case 'T':
-                return GreekAndCoptic.Greek_Capital_Letter_Tau;
-            case 'U':
-                return GreekAndCoptic.Greek_Capital_Letter_Upsilon;
-            case 'W':
-                return GreekAndCoptic.Greek_Capital_Letter_Omega;
-            case 'X':
-                return GreekAndCoptic.Greek_Capital_Letter_Xi;
-            case 'Y':
-                return GreekAndCoptic.Greek_Capital_Letter_Psi;
-            case 'Z':
-                return GreekAndCoptic.Greek_Capital_Letter_Zeta;
-
-            case 'a':
-                return GreekAndCoptic.Greek_Small_Letter_Alpha;
-            case 'b':
-                return GreekAndCoptic.Greek_Small_Letter_Beta;
-            case 'c':
-                return GreekAndCoptic.Greek_Small_Letter_Chi;
-            case 'd':
-                return GreekAndCoptic.Greek_Small_Letter_Delta;
-            case 'e':
-                return GreekAndCoptic.Greek_Small_Letter_Epsilon;
-            case 'f':
-                return GreekAndCoptic.Greek_Small_Letter_Phi;
-            case 'g':
-                return GreekAndCoptic.Greek_Small_Letter_Gamma;
-            case 'h':
-                return GreekAndCoptic.Greek_Small_Letter_Eta;
-            case 'i':
-                return GreekAndCoptic.Greek_Small_Letter_Iota;
-            case 'k':
-                return GreekAndCoptic.Greek_Small_Letter_Kappa;
-            case 'l':
-                return GreekAndCoptic.Greek_Small_Letter_Lamda;
-            case 'm':
-                return GreekAndCoptic.Greek_Small_Letter_Mu;
-            case 'n':
-                return GreekAndCoptic.Greek_Small_Letter_Nu;
-            case 'o':
-                return GreekAndCoptic.Greek_Small_Letter_Omicron;
-            case 'p':
-                return GreekAndCoptic.Greek_Small_Letter_Pi;
-            case 'q':
-                return GreekAndCoptic.Greek_Small_Letter_Theta;
-            case 'r':
-                return GreekAndCoptic.Greek_Small_Letter_Rho;
-            case 's':
-                return GreekAndCoptic.Greek_Small_Letter_Sigma;
-            case 't':
-                return GreekAndCoptic.Greek_Small_Letter_Tau;
-            case 'u':
-                return GreekAndCoptic.Greek_Small_Letter_Upsilon;
-            case 'w':
-                return GreekAndCoptic.Greek_Small_Letter_Omega;
-            case 'x':
-                return GreekAndCoptic.Greek_Small_Letter_Xi;
-            case 'y':
-                return GreekAndCoptic.Greek_Small_Letter_Psi;
-            case 'z':
-                return GreekAndCoptic.Greek_Small_Letter_Zeta;
-
-        }
-
-        throw new IllegalArgumentException("Unsupported character: ["+c+"]");
-    }
-
-}

File crystallography-importer/src/main/java/net/chempound/crystal/importer/CmlRdfUtils.java

-package net.chempound.crystal.importer;
-
-import com.hp.hpl.jena.rdf.model.Model;
-import com.hp.hpl.jena.rdf.model.RDFNode;
-import com.hp.hpl.jena.rdf.model.Resource;
-import com.hp.hpl.jena.vocabulary.RDF;
-import net.chempound.rdf.cml.CmlRdf;
-import org.xmlcml.cml.base.CMLElement;
-import org.xmlcml.cml.element.CMLScalar;
-import org.xmlcml.cml.interfacex.HasDictRef;
-
-/**
- * @author Sam Adams
- */
-public class CmlRdfUtils {
-
-
-    public static RDFNode createResource(final CMLScalar scalar, final Model model) {
-        final RDFNode valueNode = createValueNode(scalar, model);
-        final RDFNode unitsNode = createUnitsNode(scalar, model);
-        final RDFNode errorNode = createErrorNode(scalar, model);
-
-        if (unitsNode == null && errorNode == null) {
-            return valueNode;
-        }
-
-        final Resource bnode = model.createResource();
-        bnode.addProperty(RDF.value, valueNode);
-        if (unitsNode != null) {
-            bnode.addProperty(CmlRdf.units, unitsNode);
-        }
-        if (errorNode != null) {
-            bnode.addProperty(CmlRdf.errorValue, errorNode);
-        }
-        return bnode;
-    }
-
-    public static RDFNode createValueNode(final CMLScalar scalar, final Model model) {
-        final String dataType = scalar.getDataType();
-        if ("xsd:string".equals(dataType)) {
-            return model.createTypedLiteral(scalar.getString());
-        }
-        if ("xsd:double".equals(dataType)) {
-            return model.createTypedLiteral(scalar.getDouble());
-        }
-        throw new IllegalArgumentException("Unsupported data type: "+dataType);
-    }
-
-    public static RDFNode createUnitsNode(final CMLScalar scalar, final Model model) {
-        final String units = scalar.getUnits();
-        if (units != null) {
-            final String uri = getDictRefUri(units, scalar);
-            if (uri != null) {
-                return model.createResource(uri);
-            }
-        }
-        return null;
-    }
-
-    public static RDFNode createErrorNode(final CMLScalar scalar, final Model model) {
-        if (scalar.getErrorValueAttribute() != null) {
-            return model.createTypedLiteral(scalar.getErrorValue());
-        }
-        return null;
-    }
-
-    public static String getDictRefUri(final String dictRef, final CMLElement context) {
-        final int i = dictRef.indexOf(':');
-        final String prefix = dictRef.substring(0, i);
-        final String localName = dictRef.substring(i+1);
-        final String uri = context.getNamespaceURI(prefix);
-        if (uri == null) {
-            // TODO
-            return null;
-//            throw new IllegalArgumentException("prefix '"+prefix+"' undefined");
-        }
-        return uri + localName;
-    }
-
-    public static String getDictRefUri(final HasDictRef element) {
-        final String dictRef = element.getDictRef();
-        final CMLElement context = (CMLElement) element;
-        if (dictRef != null) {
-            return getDictRefUri(dictRef, context);
-        }
-        return null;
-    }
-
-}

File crystallography-importer/src/main/java/net/chempound/crystal/importer/CrystalCml2RdfConverter.java

 import com.hp.hpl.jena.vocabulary.RDFS;
 import com.hp.hpl.jena.vocabulary.XSD;
 import net.chempound.chemistry.Cml2RdfConverter;
+import net.chempound.chemistry.CmlRdfUtils;
 import net.chempound.crystal.rdf.ont.ChemAxiom;
 import net.chempound.crystal.rdf.ont.CifDict;
 import net.chempound.crystal.rdf.ont.Cryst;
 import net.chempound.rdf.foaf.Person;
 import nu.xom.Elements;
 import nu.xom.Node;
-import nu.xom.Nodes;
-import nu.xom.XPathContext;
 import org.xmlcml.cml.base.CMLElement;
 import org.xmlcml.cml.base.CMLUtil;
 import org.xmlcml.cml.converters.cif.dict.UnitsDictionary;
 import org.xmlcml.cml.element.*;
 
-import javax.xml.namespace.QName;
 import java.net.URI;
 import java.util.*;
 
-import static net.chempound.chemistry.cml.CmlUtils.findModuleByConvention;
+import static net.chempound.chemistry.cml.CmlUtils.findFirstModuleByConvention;
+import static net.chempound.crystal.importer.CrystalConstants.*;
+import static net.chempound.crystal.importer.CrystalConstants.CELL_ANGLE_GAMMA;
 import static org.xmlcml.cml.base.CMLConstants.CML_XPATH;
 
 /**
 
     private static final Map<String,String> DEPRECATED_MAP;
 
-    private static final QName CRYSTALLOGRAPHY_EXPERIMENT = new QName("http://www.xml-cml.org/convention/", "crystallographyExperiment");
-    private static final QName CRYSTAL_STRUCTURE = new QName("http://www.xml-cml.org/convention/", "crystalStructure");
-
-    public static final String CELL_LENGTH_A = "iucr:cell_length_a";
-    public static final String CELL_LENGTH_B = "iucr:cell_length_b";
-    public static final String CELL_LENGTH_C = "iucr:cell_length_c";
-    public static final String CELL_ANGLE_ALPHA = "iucr:cell_angle_alpha";
-    public static final String CELL_ANGLE_BETA = "iucr:cell_angle_beta";
-    public static final String CELL_ANGLE_GAMMA = "iucr:cell_angle_gamma";
-
     static {
         final Map<String,String> map = new HashMap<String, String>();
         map.put(CifDict.symmetryCellSetting.toString(), CifDict.spaceGroupCrystalSystem.toString());
 
     private boolean normaliseUris = true;
 
-
     public Model createModel(final CMLCml cml, final URI baseUri) {
         final CMLModule module = findCrystallographyExperimentModule(cml);
         return createModel(module, baseUri);
     }
 
     private CMLModule findCrystallographyExperimentModule(final CMLElement cml) {
-        return findModuleByConvention(cml, CRYSTALLOGRAPHY_EXPERIMENT);
+        return findFirstModuleByConvention(cml, CRYSTALLOGRAPHY_EXPERIMENT);
     }
 
-
-
     public Model createModel(final CMLModule experimentModule, final URI baseUri) {
 
-        final Model model = ModelFactory.createDefaultModel();
-
-        model.setNsPrefix("cml", CmlRdf.getURI());
-        model.setNsPrefix("cryst", Cryst.NS);
-        model.setNsPrefix("iucr", CifDict.NS);
-        model.setNsPrefix("cml", CmlRdf.getURI());
-        model.setNsPrefix("chemaxiom", ChemAxiom.getURI());
-        model.setNsPrefix("dct", DCTerms.getURI());
-        model.setNsPrefix("rdf", RDF.getURI());
-        model.setNsPrefix("rdfs", RDFS.getURI());
-        model.setNsPrefix("xsd", XSD.getURI());
+        final Model model = initialiseModel();
 
         final CMLModule crystalStructureModule = findCrystalStructureModule(experimentModule);
 
         return model;
     }
 
+    private Model initialiseModel() {
+        final Model model = ModelFactory.createDefaultModel();
+
+        model.setNsPrefix("cml", CmlRdf.getURI());
+        model.setNsPrefix("cryst", Cryst.NS);
+        model.setNsPrefix("iucr", CifDict.NS);
+        model.setNsPrefix("cml", CmlRdf.getURI());
+        model.setNsPrefix("chemaxiom", ChemAxiom.getURI());
+        model.setNsPrefix("dct", DCTerms.getURI());
+        model.setNsPrefix("rdf", RDF.getURI());
+        model.setNsPrefix("rdfs", RDFS.getURI());
+        model.setNsPrefix("xsd", XSD.getURI());
+
+        return model;
+    }
+
     private void createAuthorListInRdf(final Model model, final Resource thisCryst) {
         final List<String> authors = findAuthors(thisCryst);
         if (authors != null && !authors.isEmpty()) {
     }
 
     private CMLModule findCrystalStructureModule(final CMLModule experimentModule) {
-        return findModuleByConvention(experimentModule, CRYSTAL_STRUCTURE);
+        return findFirstModuleByConvention(experimentModule, CRYSTAL_STRUCTURE);
     }
 
     private void attachIdentifiers(final Resource resource, final Model model, final CMLMolecule molecule) {
         }
     }
 
-
-    private boolean isIucrProperty(final CMLProperty property) {
-        final String dictRef = property.getDictRef();
-        final String prefix = getPrefix(dictRef);
-        final String ns = property.getNamespaceURI(prefix);
-        if (CifDict.NS.equals(ns)) {
-            return true;
-        }
-        return false;
-    }
-
-    private String getPrefix(final String dictRef) {
-        final int i = dictRef.indexOf(':');
-        if (i != -1) {
-            return dictRef.substring(0, i);
-        }
-        return null;
-    }
-
-
-
-    private String getXpathString(final Node node, final String xpath, final XPathContext context) {
-        final Nodes nodes = node.query(xpath, context);
-        if (nodes.size() == 0) {
-            return null;
-        }
-        if (nodes.size() == 1) {
-            return nodes.get(0).getValue();
-        }
-        return null;
-    }
-
-    private String getDictRefPostfix(final String dictRef) {
-        final int idx = dictRef.indexOf(":");
-        return dictRef.substring(idx+1);
-    }
-
-    private String createIucrPredicate(final String dictRef) {
-        final StringBuilder sb = new StringBuilder();
-        boolean prevUnwantedChar = false;
-        for (final char c : dictRef.toCharArray()) {
-            if ((c >= 'a' && c <= 'z')  || (c >= 'A' && c <= 'Z')) {
-                if (prevUnwantedChar) {
-                    sb.append(Character.toUpperCase(c));
-                } else {
-                    sb.append(c);
-                }
-                prevUnwantedChar = false;
-            } else {
-                prevUnwantedChar = true;
-            }
-        }
-        return sb.toString();
-    }
-
     @Override
     protected String getPropertyUri(final String namespaceURI, final String localPart) {
         final String uri = super.getPropertyUri(namespaceURI, localPart);

File crystallography-importer/src/main/java/net/chempound/crystal/importer/CrystalConstants.java

+package net.chempound.crystal.importer;
+
+import javax.xml.namespace.QName;
+
+/**
+ * @author Sam Adams
+ */
+public class CrystalConstants {
+
+    public static final QName CRYSTALLOGRAPHY_EXPERIMENT = new QName("http://www.xml-cml.org/convention/", "crystallographyExperiment");
+    public static final QName CRYSTAL_STRUCTURE = new QName("http://www.xml-cml.org/convention/", "crystalStructure");
+
+    public static final String CELL_LENGTH_A = "iucr:cell_length_a";
+    public static final String CELL_LENGTH_B = "iucr:cell_length_b";
+    public static final String CELL_LENGTH_C = "iucr:cell_length_c";
+    public static final String CELL_ANGLE_ALPHA = "iucr:cell_angle_alpha";
+    public static final String CELL_ANGLE_BETA = "iucr:cell_angle_beta";
+    public static final String CELL_ANGLE_GAMMA = "iucr:cell_angle_gamma";
+
+}

File crystallography-importer/src/main/java/net/chempound/crystal/importer/CrystalStructureImporter.java

 import net.chempound.storage.InMemoryResource;
 import net.chempound.storage.LocalResource;
 import nu.xom.Element;
-import nu.xom.Elements;
-import nu.xom.Node;
 import nu.xom.ParsingException;
-import org.apache.commons.io.FileUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.xmlcml.cif.CIF;
 import org.xmlcml.cif.CIFDataBlock;
 import org.xmlcml.cif.CIFException;
-import org.xmlcml.cml.base.CMLUtil;
 import org.xmlcml.cml.converters.cif.CIFXML2CMLConverter;
 import org.xmlcml.cml.converters.cif.RawCML2CompleteCMLConverter;
 import org.xmlcml.cml.element.CMLCml;
-import org.xmlcml.cml.element.CMLMolecule;
-import org.xmlcml.cml.tools.ConnectionTableTool;
 
 import javax.inject.Inject;
 import java.io.ByteArrayOutputStream;
-import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
 import java.net.URI;
 import java.util.ArrayList;
 import java.util.List;
 
-import static org.xmlcml.cml.base.CMLConstants.CML_XPATH;
-
 /**
  * @author Sam Adams
  */
 
     private static final Logger LOG = LoggerFactory.getLogger(CrystalStructureImporter.class);
 
-    private final ImageGenerator imageGenerator;
-    private final InChIGenerator inchiGenerator;
-
-    private boolean createImages = true;
-
     public CrystalStructureImporter() {
         this(new ImageGenerator(), new InChIGenerator());
     }
 
     @Inject
     public CrystalStructureImporter(final ImageGenerator imageGenerator, final InChIGenerator inChIGenerator) {
-        this.imageGenerator = imageGenerator;
-        this.inchiGenerator = inChIGenerator;
+        super(imageGenerator, inChIGenerator);
     }
 
     public void load(final CIF originalCif) throws Exception {
 
         final LocalResource cmlResource = createCmlResource(completeCml, id+".cml");
 
-        LocalResource imageResource, thumbnailResource;
-
-        if (createImages) {
-            final File imageFile = File.createTempFile("image", "png");
-            try {
-                final File thumbnailFile = File.createTempFile("image_tn", "png");
-                try {
-                    imageGenerator.createImages(cifResource, "cif", imageFile, thumbnailFile);
-                    imageResource = createImageResource(imageFile, id+".png");
-                    thumbnailResource = createImageResource(thumbnailFile, id+"_tn.png");
-                } finally {
-                    FileUtils.deleteQuietly(thumbnailFile);
-                }
-            } finally {
-                FileUtils.deleteQuietly(imageFile);
-            }
-        } else {
-            imageResource = null;
-            thumbnailResource = null;
-        }
+        createImages(cifResource, id, "cif");
 
         LOG.trace("Generating RDF from complete CML");
         final CrystalCml2RdfConverter conv = new CrystalCml2RdfConverter();
         depositRequest.addResource(cmlResource);
         depositRequest.addResource(rdfResource);
 
-        if (imageResource != null) {