Sam Adams avatar Sam Adams committed ce8247a

Initial commit to restructured repositories

Comments (0)

Files changed (28)

+
+# maven
+^target/
+/target/
+
+# intellij
+^.idea/
+\.iml$
+
+# eclipse
+^\.classpath$
+/\.classpath$
+^\.project$
+/\.project$
+^\.settings/
+/\.settings/
+
+# temp files
+~$
+\.old$
+^temp/
+/temp/

crystallography-common/pom.xml

+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>uk.ac.cam.ch.wwmm.chempound.crystallography</groupId>
+        <artifactId>crystallography</artifactId>
+        <version>0.1-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>crystallography-common</artifactId>
+    <name>Chempound : Crystallography (common)</name>
+
+    <dependencies>
+
+        <dependency>
+            <groupId>com.hp.hpl.jena</groupId>
+            <artifactId>jena</artifactId>
+        </dependency>
+
+    </dependencies>
+
+</project>

crystallography-common/src/main/java/uk/ac/cam/ch/wwmm/chempound/crystal/rdf/CifDict.java

+package uk.ac.cam.ch.wwmm.chempound.crystal.rdf;
+
+import com.hp.hpl.jena.rdf.model.Model;
+import com.hp.hpl.jena.rdf.model.ModelFactory;
+import com.hp.hpl.jena.rdf.model.Property;
+
+/**
+ * @author Sam Adams
+ */
+public class CifDict {
+
+    /** <p>The RDF model that holds the vocabulary terms</p> */
+    private static Model m_model = ModelFactory.createDefaultModel();
+
+    /** <p>The namespace of the vocabulary as a string</p> */
+    public static final String NS = "http://www.xml-cml.org/dict/cif/";
+//    public static final String NS = "http://www.iucr.org/cif-rdf-schema#";
+
+
+    public static final Property diffrnAmbientTemperature = m_model.createProperty(NS, "diffrnAmbientTemperature");
+    public static final Property chemicalFormulaMoiety = m_model.createProperty(NS, "chemicalFormulaMoiety");
+    public static final Property chemicalFormulaSum = m_model.createProperty(NS, "chemicalFormulaSum");
+    public static final Property chemicalFormulaWeight = m_model.createProperty(NS, "chemicalFormulaWeight");
+
+    public static final Property symmetryCellSetting = m_model.createProperty(NS, "symmetryCellSetting");
+    public static final Property symmetrySpaceGroupNameHM = m_model.createProperty(NS, "symmetrySpaceGroupNameHM");
+    public static final Property symmetrySpaceGroupNameHall = m_model.createProperty(NS, "symmetrySpaceGroupNameHall");
+
+    public static final Property publContactAuthorName = m_model.createProperty(NS, "publContactAuthorName");
+    public static final Property publContactAuthorEmail = m_model.createProperty(NS, "publContactAuthorEmail");
+    public static final Property publSectionTitle = m_model.createProperty(NS, "publSectionTitle");
+    public static final Property publAuthorName = m_model.createProperty(NS, "publAuthorName");
+
+    public static final Property journalNameFull = m_model.createProperty(NS, "journalNameFull");
+    public static final Property journalYear = m_model.createProperty(NS, "journalYear");
+    public static final Property journalVolume = m_model.createProperty(NS, "journalVolume");
+    public static final Property journalIssue = m_model.createProperty(NS, "journalIssue");
+
+    public static final Property refineLsRFactorGt = m_model.createProperty(NS, "refineLsRFactorGt");
+    public static final Property refineLsRFactorAll = m_model.createProperty(NS, "refineLsRFactorAll");
+
+    public static final Property refineLsWrFactorGt = m_model.createProperty(NS, "refineLsWrFactorGt");
+    public static final Property refineLsWrFactorRef = m_model.createProperty(NS, "refineLsWrFactorRef");
+
+
+}

crystallography-common/src/main/java/uk/ac/cam/ch/wwmm/chempound/crystal/rdf/Cryst.java

+package uk.ac.cam.ch.wwmm.chempound.crystal.rdf;
+
+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 Cryst {
+
+    /** <p>The RDF model that holds the vocabulary terms</p> */
+    private static Model m_model = ModelFactory.createDefaultModel();
+
+    /** <p>The namespace of the vocabulary as a string</p> */
+    public static final String NS = "http://www.xmlcml.org/cryst-rdf-schema#";
+
+
+    public static final Resource CrystallographicExperiment = m_model.createResource(NS+"CrystallographicExperiment");
+    public static final Resource CrystallochemicalStructure = m_model.createResource(NS + "CrystallochemicalStructure");
+    public static final Resource UnitCell = m_model.createResource(NS + "UnitCell");
+
+    public static final Property hasUnitCell = m_model.createProperty(NS, "unitCell");
+
+    public static final Property cellLengthA = m_model.createProperty(NS, "UnitCellLengthA");
+    public static final Property cellLengthB = m_model.createProperty(NS, "UnitCellLengthB");
+    public static final Property cellLengthC = m_model.createProperty(NS, "UnitCellLengthC");
+
+    public static final Property cellAngleAlpha = m_model.createProperty(NS, "UnitCellAngleAlpha");
+    public static final Property cellAngleBeta = m_model.createProperty(NS, "UnitCellAngleBeta");
+    public static final Property cellAngleGamma = m_model.createProperty(NS, "UnitCellAngleGamma");
+
+    public static final Property hasMoiety = m_model.createProperty(NS, "hasMoiety");
+    
+}

crystallography-common/src/main/java/uk/ac/cam/ch/wwmm/chempound/crystal/rdf/CrystalEye.java

+package uk.ac.cam.ch.wwmm.chempound.crystal.rdf;
+
+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 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 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");
+    
+
+    
+
+
+}

crystallography-common/src/main/java/uk/ac/cam/ch/wwmm/chempound/crystal/rdf/Iupac.java

+package uk.ac.cam.ch.wwmm.chempound.crystal.rdf;
+
+import com.hp.hpl.jena.rdf.model.Model;
+import com.hp.hpl.jena.rdf.model.ModelFactory;
+import com.hp.hpl.jena.rdf.model.Resource;
+
+/**
+ * @author Sam Adams
+ */
+public class Iupac {
+
+    /** <p>The RDF model that holds the vocabulary terms</p> */
+    private static Model m_model = ModelFactory.createDefaultModel();
+
+    /** <p>The namespace of the vocabulary as a string</p> */
+    public static final String NS = "http://www.iupac.org/rdf-schema#";
+
+    public static final Resource InChI = m_model.createResource(NS + "InChI");
+
+    public static final Resource InChIKey = m_model.createResource(NS + "InChIKey");
+
+}

crystallography-common/src/main/java/uk/ac/cam/ch/wwmm/chempound/crystal/rdf/Swan.java

+package uk.ac.cam.ch.wwmm.chempound.crystal.rdf;
+
+import com.hp.hpl.jena.rdf.model.Property;
+import com.hp.hpl.jena.rdf.model.ResourceFactory;
+
+/**
+ * @author Sam Adams
+ */
+public class Swan {
+
+    protected static final String uri = "http://swan.mindinformatics.org/spec/1.2/collections.html#";
+
+    public static String getURI() {
+        return uri;
+    }
+
+    public static final Property followedby = ResourceFactory.createProperty(uri, "followedby");
+
+}

crystallography-handler/pom.xml

+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>uk.ac.cam.ch.wwmm.chempound.crystallography</groupId>
+        <artifactId>crystallography</artifactId>
+        <version>0.1-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>crystallography-handler</artifactId>
+    <name>Chempound : Crystallography Handler</name>
+
+    <dependencies>
+
+        <dependency>
+            <groupId>uk.ac.cam.ch.wwmm.chempound.crystallography</groupId>
+            <artifactId>crystallography-common</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>uk.ac.cam.ch.wwmm.chempound</groupId>
+            <artifactId>chempound-webapi</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>uk.ac.cam.ch.wwmm.chempound</groupId>
+            <artifactId>chempound-rdf-cml</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>uk.ac.cam.ch.wwmm.chempound</groupId>
+            <artifactId>chempound-rdf-vocabs</artifactId>
+        </dependency>
+
+    </dependencies>
+
+</project>

crystallography-handler/src/main/java/uk/ac/cam/ch/wwmm/chempound/crystal/display/CrystalContentHandler.java

+package uk.ac.cam.ch.wwmm.chempound.crystal.display;
+
+import com.hp.hpl.jena.rdf.model.Model;
+import uk.ac.cam.ch.wwmm.chempound.webapp.output.ContentHandler;
+import uk.ac.cam.ch.wwmm.chempound.webapp.output.SplashWriter;
+
+import java.net.URI;
+
+/**
+ * @author Sam Adams
+ */
+public class CrystalContentHandler extends ContentHandler {
+
+    public CrystalContentHandler() {
+        super(URI.create("http://www.xmlcml.org/cryst-rdf-schema#CrystallographicExperiment"));
+    }
+
+
+
+    @Override
+    public SplashWriter getWriter(Model model, URI aggregationUri) {
+        return new CrystalWriter(model, aggregationUri, getApplication());
+    }
+
+}

crystallography-handler/src/main/java/uk/ac/cam/ch/wwmm/chempound/crystal/display/CrystalEyeEntry.java

+package uk.ac.cam.ch.wwmm.chempound.crystal.display;
+
+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 org.apache.commons.io.FilenameUtils;
+import uk.ac.cam.ch.wwmm.chempound.crystal.rdf.CrystalEye;
+import uk.ac.cam.ch.wwmm.chempound.rdf.dc.DublinCore;
+import uk.ac.cam.ch.wwmm.chempound.rdf.Bibo;
+import uk.ac.cam.ch.wwmm.chempound.rdf.ore.Aggregation;
+import uk.ac.cam.ch.wwmm.chempound.util.MimeType;
+
+import java.net.URI;
+
+/**
+ * @author Sam Adams
+ */
+public class CrystalEyeEntry extends Aggregation {
+
+    public CrystalEyeEntry(Resource resource) {
+        super(resource);
+    }
+
+    public CrystalEyeEntry(URI uri, Model model) {
+        super(uri, model);
+        addType(CrystalEye.CrystalEyeEntry);
+    }
+
+    public CrystalStructure getCrystalStructure() {
+        Resource r = getResource(CrystalEye.crystal);
+        return r == null ? null : new CrystalStructure(r);
+    }
+
+    public void setCrystalStructure(CrystalStructure crystal) {
+        addProperty(CrystalEye.crystal, crystal);
+    }
+
+    public static CrystalEyeEntry find(Model model) {
+        ResIterator it = model.listResourcesWithProperty(RDF.type, CrystalEye.CrystalEyeEntry);
+        if (it.hasNext()) {
+            return new CrystalEyeEntry(it.next());
+        }
+        return null;
+    }
+
+    public URI getImageFile() {
+        Resource r = getResource(FOAF.img);
+        return r == null ? null : URI.create(r.getURI());
+    }
+
+    private URI getFile(String type) {
+        for (Resource r : getAggregatedResources()) {
+            MimeType mt = DublinCore.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 (Resource r : getResourceMaps()) {
+            if (r.getURI().endsWith("html")) {
+                return URI.create(r.getURI());
+            }
+        }
+        return null;
+    }
+
+
+    public URI getRdfXmlFile() {
+        for (Resource r : getResourceMaps()) {
+            if (r.getURI().endsWith("rdf")) {
+                return URI.create(r.getURI());
+            }
+        }
+        return null;
+    }
+
+    public URI getRdfN3File() {
+        for (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() {
+        URI uri = getCmlFile();
+        return FilenameUtils.getName(uri.toString());
+    }
+
+    public String getCifPath() {
+        URI html = getHtmlFile();
+        URI cif = getCifFile();
+        URI uri = html.resolve("./").relativize(cif);
+        return uri.toString();
+    }
+
+    public String getCmlPath() {
+        URI html = getHtmlFile();
+        URI cml = getCmlFile();
+        URI uri = html.resolve("./").relativize(cml);
+        return uri.toString();
+    }
+
+
+    public void setDoi(String doi) {
+        addProperty(Bibo.doi, doi);
+    }
+
+    public String getDoi() {
+        return getString(Bibo.doi);
+    }
+
+}

crystallography-handler/src/main/java/uk/ac/cam/ch/wwmm/chempound/crystal/display/CrystalStructure.java

+package uk.ac.cam.ch.wwmm.chempound.crystal.display;
+
+import com.hp.hpl.jena.rdf.model.*;
+import com.hp.hpl.jena.vocabulary.RDF;
+import com.hp.hpl.jena.vocabulary.RDFS;
+import uk.ac.cam.ch.wwmm.chempound.rdf.cml.CmlRdf;
+import uk.ac.cam.ch.wwmm.chempound.crystal.rdf.Cryst;
+import uk.ac.cam.ch.wwmm.chempound.rdf.DelegatingResource;
+import uk.ac.cam.ch.wwmm.chempound.rdf.Bibo;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author Sam Adams
+ */
+public class CrystalStructure extends DelegatingResource {
+
+    private UnitCell unitCell;
+
+    public CrystalStructure(Resource resource) {
+        super(resource);
+    }
+
+    public UnitCell getUnitCell() {
+        if (unitCell == null) {
+            Resource r = getResource(Cryst.hasUnitCell);
+            if (r == null) {
+                r = getModel().createResource(getURI() + "#unitCell");
+                addProperty(Cryst.hasUnitCell, r);
+            }
+            unitCell = new UnitCell(r);
+        }
+        return unitCell;
+    }
+
+
+    public List<Moiety> getMoieties() {
+        List<Moiety> list = new ArrayList<Moiety>();
+        for (StmtIterator it = listProperties(Cryst.hasMoiety); it.hasNext();) {
+            Statement s = it.next();
+            list.add(new Moiety(s.getResource()));
+        }
+        return list;
+    }
+
+
+    public String getString(String url) {
+        Statement s = getProperty(getModel().getProperty(url));
+        if (s == null) {
+            return null;
+        }
+        if (s.getObject().isLiteral()) {
+            return s.getString();
+        }
+        Statement s2 = s.getObject().asResource().getProperty(RDF.value);
+        return s2 == null ? null : s2.getString();
+    }
+
+    public static CrystalStructure find(Model model) {
+        ResIterator it = model.listResourcesWithProperty(RDF.type, Cryst.CrystallochemicalStructure);
+        if (it.hasNext()) {
+            return new CrystalStructure(it.next());
+        }
+        return null;
+    }
+
+
+    public String getInchi() {
+        Resource r = getIdentifier(CmlRdf.inchi);
+        return r == null ? null : getValue(r);
+    }
+
+    public String getInchiKey() {
+        Resource r = getIdentifier(CmlRdf.inchiKey);
+        return r == null ? null : getValue(r);
+    }
+
+    private String getValue(Resource r) {
+        Statement s = r.getProperty(RDF.value);
+        if (s == null) {
+            s = r.getProperty(RDFS.label);
+        }
+        return s == null ? null : s.getString();
+    }
+
+
+    private Resource getIdentifier(Property type) {
+        return getResource(type);
+//        for (StmtIterator it = resource.listProperties(type); it.hasNext();) {
+//            Statement s = it.next();
+//            Resource r = s.getResource();
+//            if (r.hasProperty(RDF.type, type)) {
+//                return r;
+//            }
+//        }
+//        return null;
+    }
+
+
+    public void setDoi(String doi) {
+        addProperty(Bibo.doi, doi);
+    }
+    
+}

crystallography-handler/src/main/java/uk/ac/cam/ch/wwmm/chempound/crystal/display/CrystalWriter.java

+package uk.ac.cam.ch.wwmm.chempound.crystal.display;
+
+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 freemarker.template.TemplateModelException;
+import org.apache.commons.io.IOUtils;
+import org.restlet.data.MediaType;
+import uk.ac.cam.ch.wwmm.chempound.crystal.rdf.CifDict;
+import uk.ac.cam.ch.wwmm.chempound.crystal.rdf.Cryst;
+import uk.ac.cam.ch.wwmm.chempound.rdf.DCTerms;
+import uk.ac.cam.ch.wwmm.chempound.webapp.ChempoundWebApp;
+import uk.ac.cam.ch.wwmm.chempound.webapp.output.SplashWriter;
+
+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 CrystalWriter implements SplashWriter {
+
+    private static final String style;
+    private static final String headers;
+
+    static {
+        style = read("/uk/ac/cam/ch/wwmm/chempound/templates/crystal/style.css");
+        headers = read("/uk/ac/cam/ch/wwmm/chempound/templates/crystal/headers.html");
+    }
+
+    private static String read(String s) {
+        InputStream in = CrystalWriter.class.getResourceAsStream(s);
+        try {
+            return IOUtils.toString(in);
+        } catch (IOException e) {
+            throw new RuntimeException("Error reading file: "+s, e);
+        } finally {
+            IOUtils.closeQuietly(in);
+        }
+    }
+
+    private Model model;
+    private URI aggregationUri;
+    private ChempoundWebApp app;
+
+
+
+    public CrystalWriter(Model model, URI aggregationUri, ChempoundWebApp app) {
+        this.model = model;
+        this.aggregationUri = aggregationUri;
+        this.app = app;
+    }
+
+    @Override
+    public String write(Map<String, Object> map) throws IOException, TemplateException {
+
+        map.put("style", style);
+        map.put("headers", headers);
+
+        Map<String,Object> o = new HashMap<String, Object>(map);
+
+        Resource r = this.model.getResource(aggregationUri.toString());
+
+        CrystalStructure cryst = new CrystalStructure(r);
+        CrystalEyeEntry entry = new CrystalEyeEntry(r);
+
+        BeansWrapper wrapper = BeansWrapper.getDefaultInstance();
+        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()));
+
+        StringWriter buffer = new StringWriter();
+
+        Template template = app.getTemplate("crystal/crystal.ftl");
+        template.process(o, buffer);
+
+        return buffer.toString();
+    }
+
+}

crystallography-handler/src/main/java/uk/ac/cam/ch/wwmm/chempound/crystal/display/Moiety.java

+package uk.ac.cam.ch.wwmm.chempound.crystal.display;
+
+import com.hp.hpl.jena.rdf.model.Model;
+import com.hp.hpl.jena.rdf.model.Resource;
+import uk.ac.cam.ch.wwmm.chempound.rdf.DelegatingResource;
+
+import java.net.URI;
+
+/**
+ * @author Sam Adams
+ */
+public class Moiety extends DelegatingResource {
+
+    public Moiety(Resource resource) {
+        super(resource);
+    }
+
+    public Moiety(URI uri, Resource type, Model model) {
+        super(uri, type, model);
+    }
+
+    
+
+}

crystallography-handler/src/main/java/uk/ac/cam/ch/wwmm/chempound/crystal/display/UnitCell.java

+package uk.ac.cam.ch.wwmm.chempound.crystal.display;
+
+import com.hp.hpl.jena.rdf.model.Resource;
+import uk.ac.cam.ch.wwmm.chempound.crystal.rdf.Cryst;
+import uk.ac.cam.ch.wwmm.chempound.rdf.DelegatingResource;
+
+/**
+ * @author Sam Adams
+ */
+public class UnitCell extends DelegatingResource {
+
+    public UnitCell(Resource resource) {
+        super(resource);
+    }
+
+    public Double getCellLengthA() {
+        return getDouble(Cryst.cellLengthA);
+    }
+
+    public Double getCellLengthB() {
+        return getDouble(Cryst.cellLengthB);
+    }
+
+    public Double getCellLengthC() {
+        return getDouble(Cryst.cellLengthC);
+    }
+
+    public Double getCellAngleAlpha() {
+        return getDouble(Cryst.cellAngleAlpha);
+    }
+
+    public Double getCellAngleBeta() {
+        return getDouble(Cryst.cellAngleBeta);
+    }
+
+    public Double getCellAngleGamma() {
+        return getDouble(Cryst.cellAngleGamma);
+    }
+
+}

crystallography-handler/src/main/java/uk/ac/cam/ch/wwmm/chempound/crystal/search/CrystalSearchProvider.java

+package uk.ac.cam.ch.wwmm.chempound.crystal.search;
+
+import com.hp.hpl.jena.rdf.model.Property;
+import com.hp.hpl.jena.vocabulary.RDF;
+import org.apache.commons.io.IOUtils;
+import uk.ac.cam.ch.wwmm.chempound.crystal.rdf.CifDict;
+import uk.ac.cam.ch.wwmm.chempound.crystal.rdf.Cryst;
+import uk.ac.cam.ch.wwmm.chempound.rdf.cml.CmlRdf;
+import uk.ac.cam.ch.wwmm.chempound.webapp.search.SearchProvider;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Map;
+
+/**
+ * @author Sam Adams
+ */
+public class CrystalSearchProvider implements SearchProvider {
+
+    @Override
+    public String getId() {
+        return "crystal";
+    }
+
+    @Override
+    public String getName() {
+        return "Crystal Structure";
+    }
+
+    @Override
+    public String getSearchForm() {
+        InputStream in = getClass().getResourceAsStream("search-crystal.html");
+        try {
+            return IOUtils.toString(in, "UTF-8");
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        } finally {
+            IOUtils.closeQuietly(in);
+        }
+    }
+
+    public String createSparqlQuery(Map<String,String> form) {
+
+        SparqlBuilder queryString = new SparqlBuilder();
+
+        processUnitCell(form, "cell_length_a", "cell_length_a_type", Cryst.cellLengthA, queryString);
+        processUnitCell(form, "cell_length_b", "cell_length_b_type", Cryst.cellLengthB, queryString);
+        processUnitCell(form, "cell_length_c", "cell_length_c_type", Cryst.cellLengthC, queryString);
+
+        processUnitCell(form, "cell_angle_alpha", "cell_angle_alpha", Cryst.cellAngleAlpha, queryString);
+        processUnitCell(form, "cell_angle_beta", "cell_angle_beta", Cryst.cellAngleBeta, queryString);
+        processUnitCell(form, "cell_angle_gamma", "cell_angle_gamma", Cryst.cellAngleGamma, queryString);
+
+        processFormulaWeight(form, queryString);
+
+        processInchiKey(form, queryString);
+
+        return queryString.getSparql();
+
+    }
+
+    private void processUnitCell(Map<String,String> form, String field, String type, Property prop, SparqlBuilder queryString) {
+        String value = form.get(field);
+        if (value != null && value.length() > 0) {
+            String op = form.get(type);
+            queryString.addUnitCell(prop, value, op);
+        }
+    }
+
+    private void processFormulaWeight(Map<String,String> form, SparqlBuilder queryString) {
+        String value = form.get("formula_weight");
+        if (value != null && value.length() > 0) {
+            String op = form.get("formula_weight_type");
+            queryString.addFormulaWeight(value, op);
+        }
+    }
+
+    private void processInchiKey(Map<String,String> form, SparqlBuilder queryString) {
+        String value = form.get("inchi_key");
+        if (value != null && value.length() > 0) {
+            queryString.addInchiKey(value);
+        }
+    }
+
+    private static String getOp(String s) {
+        if ("gt".equals(s)) {
+            return ">";
+        }
+        if ("lt".equals(s)) {
+            return "<";
+        }
+        if ("eq".equals(s)) {
+            return "=";
+        }
+        throw new IllegalArgumentException();
+    }
+
+    static class SparqlBuilder {
+
+        private StringBuilder queryString;
+        private int i = 0;
+
+        SparqlBuilder() {
+            this.queryString = new StringBuilder();
+            this.queryString.append("SELECT ?item {\n");
+            this.queryString.append("  ?item a <"+Cryst.CrystallographicExperiment+"> . \n");
+        }
+
+        public String getSparql() {
+            return queryString.toString() + " }\n";
+        }
+
+
+        public void addInchiKey(String value) {
+            queryString.append(" ?item <"+ CmlRdf.inchiKey +"> ?id"+i+ " .\n");
+//            queryString.append(" ?id"+i+" <"+ RDF.type +"> <"+ CmlRdf.Identifier +">"+ " .\n");
+            queryString.append(" ?id"+i+" <"+ RDF.value +"> ?ik"+i+ " .\n");
+            queryString.append(" FILTER (?ik"+i+" = \""+ value +"\")\n");
+            i++;
+        }
+
+        public void addFormulaWeight(String value, String type) {
+            queryString.append(" ?item"+" <"+ CifDict.chemicalFormulaWeight +"> ?cln"+i+ " .\n");
+            queryString.append(" ?cln"+i+" <"+ RDF.value +"> ?cl"+i+ " .\n");
+            queryString.append(" FILTER (?cl"+i+" "+getOp(type)+" "+value+")\n");
+            i++;
+        }
+
+        public void addUnitCell(Property prop, String value, String op) {
+            queryString.append(" ?item <"+ Cryst.hasUnitCell +"> ?uc"+i+ " .\n");
+            queryString.append(" ?uc"+i+" <"+ prop +"> ?cln"+i+ " .\n");
+            queryString.append(" ?cln"+i+" <"+ RDF.value +"> ?cl"+i+ " .\n");
+            queryString.append(" FILTER (?cl"+i+" "+getOp(op)+" "+value+")\n");
+            i++;
+        }
+
+    }
+
+}

crystallography-handler/src/main/resources/uk/ac/cam/ch/wwmm/chempound/crystal/search/search-crystal.html

+<div class="search-crystal">
+    <p>
+        <label>cell length a:</label>
+        <input name="cell_length_a" type="text" />
+        <select name="cell_length_a_type">
+            <option>gt</option>
+            <option>lt</option>
+            <option>eq</option>
+        </select>
+    </p>
+    <p>
+        <label>cell length b:</label>
+        <input name="cell_length_b" type="text" />
+        <select name="cell_length_b_type">
+            <option>gt</option>
+            <option>lt</option>
+            <option>eq</option>
+        </select>
+    </p>
+    <p>
+        <label>cell length c:</label>
+        <input name="cell_length_c" type="text" />
+        <select name="cell_length_c_type">
+            <option>gt</option>
+            <option>lt</option>
+            <option>eq</option>
+        </select>
+    </p>
+    <p>
+        <label>cell angle alpha:</label>
+        <input name="cell_angle_alpha" type="text" />
+        <select name="cell_angle_alpha_type">
+            <option>gt</option>
+            <option>lt</option>
+            <option>eq</option>
+        </select>
+    </p>
+    <p>
+        <label>cell angle beta:</label>
+        <input name="cell_angle_beta" type="text" />
+        <select name="cell_angle_beta_type">
+            <option>gt</option>
+            <option>lt</option>
+            <option>eq</option>
+        </select>
+    </p>
+    <p>
+        <label>cell angle gamma:</label>
+        <input name="cell_angle_gamma" type="text" />
+        <select name="cell_angle_gamma_type">
+            <option>gt</option>
+            <option>lt</option>
+            <option>eq</option>
+        </select>
+    </p>
+    <p>
+        <label>formula weight:</label>
+        <input name="formula_weight" type="text" />
+        <select name="formula_weight_type">
+            <option>gt</option>
+            <option>lt</option>
+            <option>eq</option>
+        </select>
+    </p>
+    <p>
+        <label>InChIKey:</label>
+        <input name="inchi_key" type="text" />
+    </p>
+</div>

crystallography-handler/src/main/resources/uk/ac/cam/ch/wwmm/chempound/templates/crystal/crystal.ftl

+<div class="crystaleye-structure">
+
+    <h1><#if cryst.getString(DC.title)??>${cryst.getString(DC.title)?html}<#else>${uri}</#if></h1>
+
+    <div class="jmol">
+        <div id="jmol"></div>
+    </div>
+
+    <div class="data">
+
+        <dl class="bib bib-citation">
+            <dt>Publisher:</dt>
+            <dd>${cryst.getString(IUCR.journalNameFull)!}</dd>
+            <dt>Journal:</dt>
+            <dd>${cryst.getString(IUCR.journalNameFull)!}</dd>
+            <dt>Year:</dt>
+            <dd>${cryst.getString(IUCR.journalYear)!}</dd>
+            <dt>Volume/Issue:</dt>
+            <dd>${cryst.getString(IUCR.journalVolume)!} / ${cryst.getString(IUCR.journalIssue)!}</dd>
+            <dt>DOI:</dt>
+            <dd><#if entry.doi??><a href="http://dx.doi.org/${entry.doi}">${entry.doi}</a></#if></dd>
+        </dl>
+
+        <dl class="bib bib-author">
+            <dt>Contact Author:</dt>
+            <dd>${(cryst.getString(IUCR.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>
+        </dl>
+
+        <h2>Data collection parameters</h2>
+
+        <dl class="data">
+            <dt>Chemical formula sum</dt>
+            <dd property="${IUCR.chemicalFormulaSum}">${cryst.getString(IUCR.chemicalFormulaSum)!}</dd>
+            <dt>Chemical formula moiety</dt>
+            <dd property="${IUCR.chemicalFormulaMoiety}">${cryst.getString(IUCR.chemicalFormulaMoiety)!}</dd>
+            <dt>Crystal system</dt>
+            <dd property="${IUCR.symmetryCellSetting}">${cryst.getString(IUCR.symmetryCellSetting)!}</dd>
+            <dt>Space group H-M</dt>
+            <dd property="${IUCR.symmetrySpaceGroupNameHM}">${cryst.getString(IUCR.symmetrySpaceGroupNameHM)!}</dd>
+            <dt>Space group Hall</dt>
+            <dd property="${IUCR.symmetrySpaceGroupNameHall}">${cryst.getString(IUCR.symmetrySpaceGroupNameHall)!}</dd>
+            <dt>Data collection temperature</dt>
+            <dd property="${IUCR.diffrnAmbientTemperature}">${cryst.getString(IUCR.diffrnAmbientTemperature)!} K</dd>
+        </dl>
+
+
+        <h2>Refinement results</h2>
+
+        <dl class="data">
+            <dt>R Factor (Obs)</dt>
+            <dd property="${IUCR.refineLsRFactorGt}">${cryst.getString(IUCR.refineLsRFactorGt)!}</dd>
+            <dt>R Factor (All)</dt>
+            <dd property="${IUCR.refineLsRFactorAll}">${cryst.getString(IUCR.refineLsRFactorAll)!}</dd>
+            <dt>Weighted R Factor (Obs)</dt>
+            <dd property="${IUCR.refineLsWrFactorGt}">${cryst.getString(IUCR.refineLsWrFactorGt)!}</dd>
+            <dt>Weighted R Factor (All)</dt>
+            <dd property="${IUCR.refineLsWrFactorRef}">${cryst.getString(IUCR.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>
+        </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>
+
+    <div class="identifiers">
+
+        <h2>Identifiers</h2>
+
+        <dl class="identifier">
+            <dt>InChI:</dt>
+            <dd>${cryst.inchi}</dd>
+            <dt>InChIKey:</dt>
+            <dd>${cryst.inchiKey}</dd>
+        </dl>
+
+<#list entry.molecularEntities as entry>
+        <dl class="identifier">
+            <dt>InChI:</dt>
+            <dd>${entry.inchi}</dd>
+            <dt>InChIKey:</dt>
+            <dd>${entry.inchiKey}</dd>
+        </dl>
+</#list>
+        <div class="clear"></div>
+
+    </div>
+
+</div>
+
+<script type="text/javascript">
+    $(document).ready(function() {
+        var useSignedApplet = true;
+        jmolInitialize("/plugins/jmol/jmol/", useSignedApplet);
+
+        $("#jmol").jmol({
+            'img': "${entry.imageFile}",
+            'url': "${entry.cifPath}",
+            'script': "load $ {1 1 1}"
+        });
+    });
+</script>

crystallography-handler/src/main/resources/uk/ac/cam/ch/wwmm/chempound/templates/crystal/headers.html

+    <!-- import jmol -->
+    <script src="/plugins/jmol/jmol/Jmol.js" type="text/javascript"></script>
+
+    <!-- import jquery -->
+    <script src="/plugins/jquery/jquery-1.4.2.js" type="text/javascript"></script>
+
+    <!-- import jmol-jquery -->
+    <script src="/plugins/jmol/jquery.jmol.js" type="text/javascript"></script>
+    <!-- import style sheet -->
+    <link href="/plugins/jmol/jquery.jmol.css" type="text/css" rel="stylesheet" />

crystallography-handler/src/main/resources/uk/ac/cam/ch/wwmm/chempound/templates/crystal/style.css

+    div.data {
+        width: 580px;
+    }
+
+    div.data dl {
+        margin: 0.5em 0;
+        float:left;
+        width: 520px;
+        padding: 0;
+        clear: left;
+    }
+
+    dl.bib dt {
+        clear: left;
+        float: left;
+        font-weight: bold;
+        width: 140px;
+        margin: 0;
+        padding: 5px;
+    }
+    dl.bib dd {
+        float: left;
+        width: 360px;
+        margin: 0;
+        padding: 5px;
+    }
+
+    dl.data dt {
+        clear: left;
+        float: left;
+        font-weight: bold;
+        width: 220px;
+        margin: 0;
+        padding: 5px;
+    }
+    dl.data dd {
+        float: left;
+        width: 280px;
+        margin: 0;
+        padding: 5px;
+    }
+
+
+    dl.identifier dt {
+        clear: left;
+        float: left;
+        font-weight: bold;
+        width: 100px;
+        margin: 0;
+        padding: 5px;
+    }
+    dl.identifier dd {
+        float: left;
+        width: 800px;
+        margin: 0;
+        padding: 5px;
+        word-wrap: break-word;
+    }
+
+    #body h1 {
+        font-size: 140%;
+        padding: 0.2em 0.2em;
+        margin: 0.2em;
+    }
+
+    #body h2 {
+        padding: 0.75em 0 0.25em 0;
+        margin: 0.5em 0 0.25em 0;
+        font-size: 125%;
+        clear: left;
+    }
+
+    dl.identifier dt {
+        color: #808080;
+        font-size: small;
+    }
+
+    dl.identifier dd {
+        color: #a0a0a0;
+        font-size: small;
+    }
+
+    div.jmol {
+        float: right;
+        width: 400px;
+        padding: 0;
+        margin: 0;
+    }
+    div #jmol {
+        height: 400px;
+        background: #d0d0d0;
+    }
+
+    div#jmol img {
+        width: 400px;
+        height: 400px;
+        margin: 0;
+        padding: 0;
+    }

crystallography-importer/pom.xml

+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>uk.ac.cam.ch.wwmm.chempound.crystallography</groupId>
+        <artifactId>crystallography</artifactId>
+        <version>0.1-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>crystallography-importer</artifactId>
+    <name>Chempound : Crystallography Importer</name>
+
+    <dependencies>
+
+        <dependency>
+            <groupId>uk.ac.cam.ch.wwmm.chempound.crystallography</groupId>
+            <artifactId>crystallography-common</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>uk.ac.cam.ch.wwmm.chempound.chemistry</groupId>
+            <artifactId>chemistry-common</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>uk.ac.cam.ch.wwmm.chempound</groupId>
+            <artifactId>chempound-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>uk.ac.cam.ch.wwmm.chempound</groupId>
+            <artifactId>chempound-rdf-vocabs</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>uk.ac.cam.ch.wwmm.chempound</groupId>
+            <artifactId>chempound-rdf-cml</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>cml</groupId>
+            <artifactId>jumbo</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>cml</groupId>
+            <artifactId>jumbo-converters-cif</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>sea36</groupId>
+            <artifactId>JmolImageGenerator</artifactId>
+            <version>1.0-SNAPSHOT</version>
+        </dependency>
+
+        <dependency>
+            <groupId>cml</groupId>
+            <artifactId>jumbo</artifactId>
+            <version>6.0.0-SNAPSHOT</version>
+        </dependency>
+        <dependency>
+            <groupId>cml</groupId>
+            <artifactId>cmlxom</artifactId>
+            <version>[3.0.0-SNAPSHOT]</version>
+        </dependency>
+        <dependency>
+            <groupId>net.sf.jni-inchi</groupId>
+            <artifactId>jni-inchi</artifactId>
+            <version>0.7</version>
+        </dependency>
+
+        <dependency>
+            <groupId>sea36.util</groupId>
+            <artifactId>unicode</artifactId>
+            <version>1.0-SNAPSHOT</version>
+        </dependency>
+
+    </dependencies>
+
+</project>

crystallography-importer/src/main/java/uk/ac/cam/ch/wwmm/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 uk.ac.cam.ch.wwmm.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(String s) {
+        StringBuilder html = new StringBuilder();
+
+        boolean sup = false, sub = false;
+
+        for (int i = 0; i < s.length(); i++) {
+
+            char c = s.charAt(i);
+            if (c == '\\') {
+                i += readChar(s, i+1, html);
+            }
+            else if (c == '+') {
+                if ("+-".equals(s.substring(i, i+2))) {
+                    html.append(Latin1Supplement.Plus_Minus_Sign);
+                }
+                i += 1;
+            }
+            else if (c == '-') {
+                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);
+                }
+            }
+            else if (c == '<') {
+                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);
+                }
+            }
+            else if (c == '^') { // superscript
+                if (sup) {
+                    html.append("</sup>");
+                } else {
+                    html.append("<sub>");
+                }
+                sup = !sup;
+            }
+            else if (c == '~') { // subscript
+                if (sub) {
+                    html.append("</sub>");
+                } else {
+                    html.append("<sub>");
+                }
+                sub = !sub;
+            }
+            else {
+                html.append(c);
+            }
+        }
+        return html.toString();
+    }
+
+    private static int readChar(String s, int i, StringBuilder html) {
+        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();
+
+        }
+
+        char x = getChar(c);
+        html.append(x);
+        return 1;
+    }
+
+
+
+    private static char readAcuteChar(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(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(char c) {
+
+        throw new IllegalArgumentException("Unsupported circumflex character: "+c);
+    }
+
+
+    private static char getChar(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;