diff --git a/.gitignore b/.gitignore index b3de3b279..c6f53fe5a 100644 --- a/.gitignore +++ b/.gitignore @@ -17,3 +17,8 @@ utilities/sdb_to_tdb/.work **/target **/overlays + +# Eclipse artifacts +**/.settings +**/.classpath +**/.project diff --git a/.travis.yml b/.travis.yml index 04c61e69a..ce9b09b50 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,13 +1,20 @@ language: java +dist: trusty sudo: false jdk: + - openjdk8 - oraclejdk8 env: # Give Maven 1GB of memory to work with - MAVEN_OPTS=-Xmx1024M +cache: + directories: + - .autoconf + - $HOME/.m2 + install: - "echo 'Skipping install stage, dependencies will be downloaded during build and test stages.'" diff --git a/api/pom.xml b/api/pom.xml index 0ae50bbd3..8d799cc18 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -7,13 +7,13 @@ org.vivoweb vitro-api - 1.9.0-SNAPSHOT + 1.10.0-SNAPSHOT jar org.vivoweb vitro-project - 1.9.0-SNAPSHOT + 1.10.0-SNAPSHOT .. @@ -39,7 +39,7 @@ org.vivoweb vitro-dependencies - 1.9.0-SNAPSHOT + 1.10.0-SNAPSHOT pom diff --git a/api/src/main/java/edu/cornell/mannlib/semservices/bo/BaseObject.java b/api/src/main/java/edu/cornell/mannlib/semservices/bo/BaseObject.java new file mode 100644 index 000000000..ce4d8aa66 --- /dev/null +++ b/api/src/main/java/edu/cornell/mannlib/semservices/bo/BaseObject.java @@ -0,0 +1,25 @@ +/* $This file is distributed under the terms of the license in /doc/license.txt$ */ +package edu.cornell.mannlib.semservices.bo; + +public class BaseObject { + /** + * Simple JavaBean domain object with an id property. + * Used as a base class for objects needing this property. + */ + private Integer id; + + public void setId(Integer id) { + this.id = id; + } + + public Integer getId() { + return id; + } + + public boolean isNew() { + return (this.id == null); + } + + + +} diff --git a/api/src/main/java/edu/cornell/mannlib/semservices/bo/Concept.java b/api/src/main/java/edu/cornell/mannlib/semservices/bo/Concept.java new file mode 100644 index 000000000..434f82ea4 --- /dev/null +++ b/api/src/main/java/edu/cornell/mannlib/semservices/bo/Concept.java @@ -0,0 +1,163 @@ +/* $This file is distributed under the terms of the license in /doc/license.txt$ */ + +package edu.cornell.mannlib.semservices.bo; + +import java.util.ArrayList; +import java.util.List; + +public class Concept { + + private String definedBy; + private String conceptId; + private String bestMatch; + private String label; + private String type; + private String definition; + private String uri; + private String schemeURI; + private List broaderURIList; + private List narrowerURIList; + private List exactMatchURIList; + private List closeMatchURIList; + private List altLabelList; + + /** + * default constructor + */ + public Concept() { + this.broaderURIList = new ArrayList(); + this.narrowerURIList = new ArrayList(); + this.exactMatchURIList = new ArrayList(); + this.closeMatchURIList = new ArrayList(); + } + + /** + * @return the conceptId + */ + public String getConceptId() { + return conceptId; + } + /** + * @param conceptId the conceptId to set + */ + public void setConceptId(String conceptId) { + this.conceptId = conceptId; + } + /** + * @return the label + */ + public String getLabel() { + return label; + } + /** + * @param label the label to set + */ + public void setLabel(String label) { + this.label = label; + } + /** + * @return the type + */ + public String getType() { + return type; + } + /** + * @param type the type to set + */ + public void setType(String type) { + this.type = type; + } + /** + * @return the definition + */ + public String getDefinition() { + return definition; + } + /** + * @param definition the definition to set + */ + public void setDefinition(String definition) { + this.definition = definition; + } + /** + * @return the uri + */ + public String getUri() { + return uri; + } + /** + * @param uri the uri to set + */ + public void setUri(String uri) { + this.uri = uri; + } + /** + * @return the definedBy + */ + public String getDefinedBy() { + return definedBy; + } + /** + * @param definedBy the definedBy to set + */ + public void setDefinedBy(String definedBy) { + this.definedBy = definedBy; + } + /** + * @return the schemeURI + */ + public String getSchemeURI() { + return schemeURI; + } + /** + * @param schemeURI the schemeURI to set + */ + public void setSchemeURI(String schemeURI) { + this.schemeURI = schemeURI; + } + /** + * @return the bestMatch + */ + public String getBestMatch() { + return bestMatch; + } + /** + * @param bestMatch the bestMatch to set + */ + public void setBestMatch(String bestMatch) { + this.bestMatch = bestMatch; + } +public List getBroaderURIList() { + return broaderURIList; +} +public void setBroaderURIList(List broaderURIList) { + this.broaderURIList = broaderURIList; +} +public List getNarrowerURIList() { + return narrowerURIList; +} +public void setNarrowerURIList(List narrowerURIList) { + this.narrowerURIList = narrowerURIList; +} +public List getExactMatchURIList() { + return exactMatchURIList; +} +public void setExactMatchURIList(List exactMatchURIList) { + this.exactMatchURIList = exactMatchURIList; +} +public List getCloseMatchURIList() { + return closeMatchURIList; +} +public void setCloseMatchURIList(List closeMatchURIList) { + this.closeMatchURIList = closeMatchURIList; +} + +public List getAltLabelList() { + return altLabelList; +} + +public void setAltLabelList(List altLabelList) { + this.altLabelList = altLabelList; +} + +} diff --git a/api/src/main/java/edu/cornell/mannlib/semservices/bo/ConceptInfo.java b/api/src/main/java/edu/cornell/mannlib/semservices/bo/ConceptInfo.java new file mode 100644 index 000000000..3ee08d520 --- /dev/null +++ b/api/src/main/java/edu/cornell/mannlib/semservices/bo/ConceptInfo.java @@ -0,0 +1,32 @@ +/* $This file is distributed under the terms of the license in /doc/license.txt$ */ + +package edu.cornell.mannlib.semservices.bo; + + +import java.util.List; + + +public class ConceptInfo extends SemanticServicesInfoBase { + + private List conceptList; + /** + * + */ + public ConceptInfo() { + super(); + } + + /** + * @return the conceptList + */ + public List getConceptList() { + return conceptList; + } + + /** + * @param conceptList the conceptList to set + */ + public void setConceptList(List conceptList) { + this.conceptList = conceptList; + } +} diff --git a/api/src/main/java/edu/cornell/mannlib/semservices/bo/SemanticServicesError.java b/api/src/main/java/edu/cornell/mannlib/semservices/bo/SemanticServicesError.java new file mode 100644 index 000000000..449f5c233 --- /dev/null +++ b/api/src/main/java/edu/cornell/mannlib/semservices/bo/SemanticServicesError.java @@ -0,0 +1,75 @@ +/* $This file is distributed under the terms of the license in /doc/license.txt$ */ + +package edu.cornell.mannlib.semservices.bo; + +public class SemanticServicesError { + private String message; + private String exception; + private String severity; + + /** + * + */ + public SemanticServicesError() { + super(); + } + + + + /** + * @param exception Exception description + * @param message Error message + * @param severity Severity + */ + public SemanticServicesError(String exception, String message, String severity) { + super(); + this.exception = exception; + this.message = message; + this.severity = severity; + } + + + + /** + * @return the message + */ + public String getMessage() { + return message; + } + + /** + * @param message the message to set + */ + public void setMessage(String message) { + this.message = message; + } + + /** + * @return the exception + */ + public String getException() { + return exception; + } + + /** + * @param exception the exception to set + */ + public void setException(String exception) { + this.exception = exception; + } + + /** + * @return the severity + */ + public String getSeverity() { + return severity; + } + + /** + * @param severity the severity to set + */ + public void setSeverity(String severity) { + this.severity = severity; + } + +} diff --git a/api/src/main/java/edu/cornell/mannlib/semservices/bo/SemanticServicesInfoBase.java b/api/src/main/java/edu/cornell/mannlib/semservices/bo/SemanticServicesInfoBase.java new file mode 100644 index 000000000..ac24a3ecc --- /dev/null +++ b/api/src/main/java/edu/cornell/mannlib/semservices/bo/SemanticServicesInfoBase.java @@ -0,0 +1,29 @@ +/* $This file is distributed under the terms of the license in /doc/license.txt$ */ + +package edu.cornell.mannlib.semservices.bo; + +public class SemanticServicesInfoBase { + + private SemanticServicesError semanticServicesError; + /** + * + */ + public SemanticServicesInfoBase() { + super(); + // TODO Auto-generated constructor stub + } + + /** + * @return the semanticServicesError + */ + public SemanticServicesError getSemanticServicesError() { + return semanticServicesError; + } + /** + * @param semanticServicesError the semanticServicesError to set + */ + public void setSemanticServicesError(SemanticServicesError semanticServicesError) { + this.semanticServicesError = semanticServicesError; + } + +} diff --git a/api/src/main/java/edu/cornell/mannlib/semservices/exceptions/ConceptsNotFoundException.java b/api/src/main/java/edu/cornell/mannlib/semservices/exceptions/ConceptsNotFoundException.java new file mode 100644 index 000000000..1264905eb --- /dev/null +++ b/api/src/main/java/edu/cornell/mannlib/semservices/exceptions/ConceptsNotFoundException.java @@ -0,0 +1,15 @@ +/* $This file is distributed under the terms of the license in /doc/license.txt$ */ + +package edu.cornell.mannlib.semservices.exceptions; + +public class ConceptsNotFoundException extends Exception { + /** + * An exception that indicates a service could not find a Concept + */ + private static final long serialVersionUID = -4729465393290022840L; + public ConceptsNotFoundException() { } + public ConceptsNotFoundException(String message) { super(message); } + public ConceptsNotFoundException(Throwable cause) { super(cause); } + public ConceptsNotFoundException(String message, Throwable cause) { super(message, cause); } + +} diff --git a/api/src/main/java/edu/cornell/mannlib/semservices/service/ExternalConceptService.java b/api/src/main/java/edu/cornell/mannlib/semservices/service/ExternalConceptService.java new file mode 100644 index 000000000..8a675a415 --- /dev/null +++ b/api/src/main/java/edu/cornell/mannlib/semservices/service/ExternalConceptService.java @@ -0,0 +1,27 @@ +/* $This file is distributed under the terms of the license in /doc/license.txt$ */ + +package edu.cornell.mannlib.semservices.service; + +import java.util.List; + +import edu.cornell.mannlib.semservices.bo.Concept; + +public interface ExternalConceptService { + + /** + * @param term Term + */ + List processResults(String term) throws Exception; + + /** + * @param term Term + * @throws Exception + */ + List getConcepts(String term) throws Exception; + + /** + * @param uri URI + */ + List getConceptsByURIWithSparql(String uri) throws Exception; + +} diff --git a/api/src/main/java/edu/cornell/mannlib/semservices/util/MetadataNamespaceContext.java b/api/src/main/java/edu/cornell/mannlib/semservices/util/MetadataNamespaceContext.java new file mode 100644 index 000000000..b9717e0c2 --- /dev/null +++ b/api/src/main/java/edu/cornell/mannlib/semservices/util/MetadataNamespaceContext.java @@ -0,0 +1,26 @@ +/* $This file is distributed under the terms of the license in /doc/license.txt$ */ + +package edu.cornell.mannlib.semservices.util; +import java.util.Iterator; +import javax.xml.XMLConstants; +import javax.xml.namespace.NamespaceContext; + +public class MetadataNamespaceContext implements NamespaceContext { + public String getNamespaceURI(String prefix) { + if (prefix == null) throw new NullPointerException("Null prefix"); + else if ("mix".equals(prefix)) return "http://www.loc.gov/mix/"; + else if ("xml".equals(prefix)) return XMLConstants.XML_NS_URI; + return XMLConstants.NULL_NS_URI; + } + + // This method isn't necessary for XPath processing. + public String getPrefix(String uri) { + throw new UnsupportedOperationException(); + } + + // This method isn't necessary for XPath processing either. + public Iterator getPrefixes(String uri) { + throw new UnsupportedOperationException(); + } + +} diff --git a/api/src/main/java/edu/cornell/mannlib/semservices/util/SKOSUtils.java b/api/src/main/java/edu/cornell/mannlib/semservices/util/SKOSUtils.java new file mode 100644 index 000000000..1afe48966 --- /dev/null +++ b/api/src/main/java/edu/cornell/mannlib/semservices/util/SKOSUtils.java @@ -0,0 +1,266 @@ +/* $This file is distributed under the terms of the license in /doc/license.txt$ */ + +/* We are no longer using the SKOS API since Vitro has moved to V 4.0 of OWL API which does not appear to be compatible. + This file will contain methods used for reading SKOS as XML and parsing it for the properties + we want to extract*/ + +package edu.cornell.mannlib.semservices.util; + +import java.io.BufferedReader; +import java.io.InputStreamReader; +import java.io.StringReader; +import java.io.StringWriter; +import java.net.URL; +import java.util.ArrayList; +import java.util.List; + +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import org.apache.jena.rdf.model.Model; +import org.apache.jena.rdf.model.ModelFactory; +import org.apache.jena.rdf.model.NodeIterator; +import org.apache.jena.rdf.model.RDFNode; +import org.apache.jena.rdf.model.ResourceFactory; +import org.apache.jena.rdf.model.Statement; +import org.apache.jena.rdf.model.StmtIterator; + +import edu.cornell.mannlib.semservices.bo.Concept; + +public class SKOSUtils { + protected final static Log log = LogFactory.getLog(SKOSUtils.class); + + public static String getConceptXML(String conceptUriString) { + URL conceptURL = null; + try { + conceptURL = new URL(conceptUriString); + } catch (Exception e) { + log.error("Exception occurred in instantiating URL for " + + conceptUriString, e); + // If the url is having trouble, just return null for the concept + return null; + } + log.debug("loading concept uri " + conceptUriString); + + String results = null; + try { + + StringWriter sw = new StringWriter(); + + BufferedReader in = new BufferedReader(new InputStreamReader( + conceptURL.openStream())); + String inputLine; + while ((inputLine = in.readLine()) != null) { + sw.write(inputLine); + } + in.close(); + + results = sw.toString(); + log.debug(results); + } catch (Exception ex) { + log.error("Error occurred in getting concept from the URL " + + conceptUriString, ex); + return null; + } + return results; + } + + // Downloading the XML from the URI itself + // No language tag support here but can be specified if need be at this + // level as well + public static Concept createConceptUsingXMLFromURL(Concept concept, + String conceptURLString, String langTagValue, boolean addNotes) { + String results = getConceptXML(conceptURLString); + if (StringUtils.isEmpty(results)) { + return null; + } + + // return createConceptUsingXML(concept, results, langTagValue); + return createConceptUsingXMLModel(concept, results, langTagValue, + addNotes); + + } + + // Because of the fact the xml returns matches by tag name, and the XML may + // look like + // where conceptURI is the concept that is the subject of skos:narrower, we + // need to ensure we are not returning the same uri as that of the main + // concept + public static List removeConceptURIFromList(List uris, + String conceptURI) { + // remove will return a boolean if the value exists in the list and is + // removed + // if/when it returns false, the URI is not in the list + while (uris.remove(conceptURI)) { + } + ; + return uris; + } + + /** + * The above code, although functional, does not take advantage of the fact + * that we can actually read and query the RDF in precisely the manner we + * wish. + */ + + public static Concept createConceptUsingXMLModel(Concept concept, + String results, String langTagValue, boolean addNotes) { + + try { + String conceptURI = concept.getUri(); + + // Load Model from RDF + StringReader reader = new StringReader(results); + Model model = ModelFactory.createDefaultModel(); + model.read(reader, null, "RDF/XML"); + + // Execute the following query to get the information we want for + // this resource + + // Preferred label + List labelLiterals = getPrefLabelsFromModel(conceptURI, + model, langTagValue); + if (labelLiterals.size() > 0) { + concept.setLabel(labelLiterals.get(0)); + } else { + // This is an error because there should be at least one label + // returned + log.debug("The number of preferred labels is not greater than zero"); + } + + // Alternate label + + List altLabelList = getAltLabelsFromModel(conceptURI, + model, langTagValue); + concept.setAltLabelList(altLabelList); + + // Broder, narrower, exact match, and close match properties + + List broaderURIList = getBroaderURIsFromModel(conceptURI, + model); + // broaderURIList = removeConceptURIFromList(broaderURIList, + // conceptURI); + concept.setBroaderURIList(broaderURIList); + List narrowerURIList = getNarrowerURIsFromModel(conceptURI, + model); + // narrowerURIList = removeConceptURIFromList(narrowerURIList, + // conceptURI); + concept.setNarrowerURIList(narrowerURIList); + + List exactMatchURIList = getExactMatchURIsFromModel( + conceptURI, model); + // exactMatchURIList = removeConceptURIFromList(exactMatchURIList, + // conceptURI); + concept.setExactMatchURIList(exactMatchURIList); + List closeMatchURIList = getCloseMatchURIsFromModel( + conceptURI, model); + // closeMatchURIList = removeConceptURIFromList(closeMatchURIList, + // conceptURI); + concept.setCloseMatchURIList(closeMatchURIList); + + // Notes may exist, in which case they should be employed + if (addNotes) { + List notes = getNotesFromModel(conceptURI, model, + langTagValue); + if (notes.size() > 0) { + concept.setDefinition(notes.get(0)); + } + } + + } catch (Exception e) { + log.error("error occurred in parsing " + results, e); + } + + return concept; + + } + + private static List getPrefLabelsFromModel(String conceptURI, + Model model, String langTagValue) { + String propertyURI = "http://www.w3.org/2004/02/skos/core#prefLabel"; + return getLabelsFromModel(conceptURI, propertyURI, model, langTagValue); + } + + private static List getAltLabelsFromModel(String conceptURI, + Model model, String langTagValue) { + String propertyURI = "http://www.w3.org/2004/02/skos/core#altLabel"; + return getLabelsFromModel(conceptURI, propertyURI, model, langTagValue); + } + + private static List getLabelsFromModel(String conceptURI, + String propertyURI, Model model, String langTagValue) { + List labels = new ArrayList(); + StmtIterator statements = model.listStatements( + ResourceFactory.createResource(conceptURI), + ResourceFactory.createProperty(propertyURI), (RDFNode) null); + while (statements.hasNext()) { + Statement statement = statements.nextStatement(); + RDFNode node = statement.getObject(); + if (node != null && node.isLiteral()) { + String label = node.asLiteral().getString(); + if (StringUtils.isNotEmpty(langTagValue)) { + String language = node.asLiteral().getLanguage(); + if (language != null && language.equals(langTagValue)) { + labels.add(label); + } + } else { + labels.add(label); + } + } + + } + return labels; + } + + private static List getNotesFromModel(String conceptURI, + Model model, String langTagValue) { + String propertyURI = "http://www.w3.org/2004/02/skos/core#note"; + return getLabelsFromModel(conceptURI, propertyURI, model, langTagValue); + } + + private static List getCloseMatchURIsFromModel(String conceptURI, + Model model) { + String propertyURI = "http://www.w3.org/2004/02/skos/core#closeMatch"; + return getRelatedURIsFromModel(conceptURI, propertyURI, model); + + } + + private static List getExactMatchURIsFromModel(String conceptURI, + Model model) { + String propertyURI = "http://www.w3.org/2004/02/skos/core#exactMatch"; + return getRelatedURIsFromModel(conceptURI, propertyURI, model); + } + + private static List getNarrowerURIsFromModel(String conceptURI, + Model model) { + String propertyURI = "http://www.w3.org/2004/02/skos/core#narrower"; + return getRelatedURIsFromModel(conceptURI, propertyURI, model); + } + + private static List getBroaderURIsFromModel(String conceptURI, + Model model) { + String propertyURI = "http://www.w3.org/2004/02/skos/core#broader"; + return getRelatedURIsFromModel(conceptURI, propertyURI, model); + } + + private static List getRelatedURIsFromModel(String conceptURI, + String propertyURI, Model model) { + List URIs = new ArrayList(); + NodeIterator nodeIterator = model.listObjectsOfProperty( + ResourceFactory.createResource(conceptURI), + ResourceFactory.createProperty(propertyURI)); + + while (nodeIterator.hasNext()) { + RDFNode node = nodeIterator.nextNode(); + if (node.isResource() && node.asResource().getURI() != null) { + String URI = node.asResource().getURI(); + URIs.add(URI); + } + } + + return URIs; + } + +} \ No newline at end of file diff --git a/api/src/main/java/edu/cornell/mannlib/semservices/util/XMLUtils.java b/api/src/main/java/edu/cornell/mannlib/semservices/util/XMLUtils.java new file mode 100644 index 000000000..b5342c5c8 --- /dev/null +++ b/api/src/main/java/edu/cornell/mannlib/semservices/util/XMLUtils.java @@ -0,0 +1,361 @@ +/* $This file is distributed under the terms of the license in /doc/license.txt$ */ + +package edu.cornell.mannlib.semservices.util; + +import java.io.BufferedWriter; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStreamWriter; +import java.io.StringReader; +import java.io.StringWriter; +import java.io.Writer; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.transform.OutputKeys; +import javax.xml.transform.Source; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerConfigurationException; +import javax.xml.transform.TransformerException; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.TransformerFactoryConfigurationError; +import javax.xml.transform.stream.StreamResult; +import javax.xml.transform.stream.StreamSource; +import javax.xml.xpath.XPath; +import javax.xml.xpath.XPathConstants; +import javax.xml.xpath.XPathExpressionException; +import javax.xml.xpath.XPathFactory; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.w3c.dom.Document; +import org.w3c.dom.DocumentType; +import org.w3c.dom.NamedNodeMap; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; + +/** + * Convenience Class to parse XML strings to DOM Document for XML contents + * retrieval. + */ +public class XMLUtils { + private static DocumentBuilder parser; + public static Writer writer; + static private String indent = ""; + protected static final Log logger = LogFactory.getLog(XMLUtils.class); + + + /** + * @throws ParserConfigurationException + */ + public static DocumentBuilder getDocumentBuilder() + throws ParserConfigurationException { + if (parser == null) { + // JPT: Remove xerces use + DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory + .newInstance(); + documentBuilderFactory.setNamespaceAware(true); + documentBuilderFactory.setValidating(false); + parser = documentBuilderFactory.newDocumentBuilder(); + } + + return parser; + } + + /** + * @param xmlString XML String + * @throws IOException + * @throws SAXException + * @throws ParserConfigurationException + */ + public synchronized static Document parse(String xmlString) + throws IOException, SAXException, ParserConfigurationException { + StringReader reader = new StringReader(xmlString); + InputSource inputSource = new InputSource(reader); + return getDocumentBuilder().parse(inputSource); + } + + /** + * @param stream Input stream + * @throws IOException + * @throws SAXException + * @throws ParserConfigurationException + */ + public synchronized static Document parse(InputStream stream) + throws IOException, SAXException, ParserConfigurationException { + return getDocumentBuilder().parse(stream); + } + + /** + * @param document DOM Document + * @param name Name + */ + public static String getElementByName(Document document, String name) { + NodeList nodes = document.getElementsByTagName(name); + String s = null; + for (int i=0; i < nodes.getLength() ; i++) { + Node node = nodes.item(i); + s = node.getTextContent().trim(); + } + return s; + } + + /** + * @param doc DOM Document + * @throws IOException + */ + @SuppressWarnings("deprecation") + public static void serializeDoc(Document doc) throws IOException { + org.apache.xml.serialize.XMLSerializer serializer = new org.apache.xml.serialize.XMLSerializer(); + serializer.setOutputByteStream(System.out); + serializer.serialize(doc); + } + + @SuppressWarnings("deprecation") + public static String serializeDoctoString(Document doc) throws IOException { + org.apache.xml.serialize.XMLSerializer serializer = new org.apache.xml.serialize.XMLSerializer(); + ByteArrayOutputStream bout = new ByteArrayOutputStream(); + + serializer.setOutputByteStream(bout); + serializer.serialize(doc); + return bout.toString(); + } + + /** + * @param xml XML String + */ + public static void prettyPrint(String xml) { + Source xmlInput = new StreamSource(new StringReader(xml)); + StreamResult xmlOutput = new StreamResult(new StringWriter()); + Transformer transformer = null; + try { + transformer = TransformerFactory.newInstance().newTransformer(); + } catch (TransformerConfigurationException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } catch (TransformerFactoryConfigurationError e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + //transformer.setOutputProperty(OutputKeys.DOCTYPE_SYSTEM, "testing.dtd"); + transformer.setOutputProperty(OutputKeys.INDENT, "yes"); + transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2"); + try { + transformer.transform(xmlInput, xmlOutput); + } catch (TransformerException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + String formattedxml=xmlOutput.getWriter().toString(); + System.out.println(formattedxml); + + } + + /** + * @param xml XML String + */ + public static String prettyPrintToString(String xml) { + Source xmlInput = new StreamSource(new StringReader(xml)); + StreamResult xmlOutput = new StreamResult(new StringWriter()); + Transformer transformer = null; + try { + transformer = TransformerFactory.newInstance().newTransformer(); + } catch (TransformerConfigurationException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } catch (TransformerFactoryConfigurationError e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + //transformer.setOutputProperty(OutputKeys.DOCTYPE_SYSTEM, "testing.dtd"); + transformer.setOutputProperty(OutputKeys.INDENT, "yes"); + transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2"); + try { + transformer.transform(xmlInput, xmlOutput); + } catch (TransformerException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + String formattedxml=xmlOutput.getWriter().toString(); + return formattedxml; + + } + + /** + * @param node DOM Node + */ + public static void displayNodeInfo(Node node) { + switch (node.getNodeType()) { + case Node.DOCUMENT_NODE: + System.out.println("Document Node "); + break; + case Node.ELEMENT_NODE: + System.out.println("Element Node: "+ node.getNodeName()); + break; + case Node.TEXT_NODE: + System.out.println("Text Node: "+ node.getNodeName()); + break; + case Node.CDATA_SECTION_NODE: + System.out.println("CDATA Section Node: "); + break; + case Node.COMMENT_NODE: + System.out.println("Comment Node "); + break; + case Node.PROCESSING_INSTRUCTION_NODE: + System.out.println("Processing Instruction Node "); + break; + case Node.ENTITY_REFERENCE_NODE: + System.out.println("Entity Reference Node "); + break; + case Node.DOCUMENT_TYPE_NODE: + System.out.println("Document Type Node "); + break; + } + } + + /** + * @param node DOM Node + * @throws IOException + */ + public static void serializeNode(Node node) throws IOException { + if (writer == null) writer = new BufferedWriter(new OutputStreamWriter(System.out)); + + switch (node.getNodeType()) { + case Node.DOCUMENT_NODE: + Document doc = (Document) node; + writer.write("\n"); + + NodeList nodes = node.getChildNodes(); + if (nodes != null) + for (int i = 0; i < nodes.getLength(); i++) + serializeNode(nodes.item(i)); + break; + case Node.ELEMENT_NODE: + String name = node.getNodeName(); + writer.write("<" + name); + NamedNodeMap attributes = node.getAttributes(); + for (int i = 0; i < attributes.getLength(); i++) { + Node current = attributes.item(i); + writer.write(" " + current.getNodeName() + "=\""); + print(current.getNodeValue()); + writer.write("\""); + } + writer.write(">"); + + NodeList children = node.getChildNodes(); + if (children != null) { + //if ((children.item(0) != null) && (children.item(0).getNodeType() == Node.ELEMENT_NODE)) + // writer.write("\n"); + + for (int i = 0; i < children.getLength(); i++) + serializeNode(children.item(i)); + if ((children.item(0) != null) + && (children.item(children.getLength() - 1).getNodeType() == Node.ELEMENT_NODE)) + writer.write(""); + } + + writer.write(""); + break; + case Node.TEXT_NODE: + print(node.getNodeValue()); + break; + case Node.CDATA_SECTION_NODE: + writer.write("CDATA"); + print(node.getNodeValue()); + writer.write(""); + break; + case Node.COMMENT_NODE: + writer.write("\n"); + break; + case Node.PROCESSING_INSTRUCTION_NODE: + writer.write("\n"); + break; + case Node.ENTITY_REFERENCE_NODE: + writer.write("&" + node.getNodeName() + ";"); + break; + case Node.DOCUMENT_TYPE_NODE: + DocumentType docType = (DocumentType) node; + String publicId = docType.getPublicId(); + String systemId = docType.getSystemId(); + String internalSubset = docType.getInternalSubset(); + writer.write("\n"); + break; + } + writer.flush(); + } + + /** + * @param s String + * @throws IOException + */ + private static void print(String s) throws IOException { + if (s == null) + return; + for (int i = 0, len = s.length(); i < len; i++) { + char c = s.charAt(i); + switch (c) { + case '<': + writer.write("<"); + break; + case '>': + writer.write(">"); + break; + case '&': + writer.write("&"); + break; + case '\r': + writer.write(" "); + break; + default: + writer.write(c); + } + } + } + + /** + * @param obj (either a Document or a Node) + * @param expression Expression + * @return string contents + */ + public static Node getNodeWithXpath(Object obj, String expression) { + Object root = null; + if (obj instanceof Document) { + Document doc = (Document) obj; + root = doc.getDocumentElement(); + } else { + root = (Node) obj; + } + + XPath xpath = XPathFactory.newInstance().newXPath(); + xpath.setNamespaceContext(new MetadataNamespaceContext()); + Node result = null; + + try { + result = ((Node) xpath.evaluate(expression, root, XPathConstants.NODE)); + return result; + } catch (XPathExpressionException e) { + logger.error("XPathExpressionException ", e); + return null; + } + } + +} diff --git a/api/src/main/java/edu/cornell/mannlib/vedit/tags/DynamicFieldsTag.java b/api/src/main/java/edu/cornell/mannlib/vedit/tags/DynamicFieldsTag.java index d9920ae8e..835fc0117 100644 --- a/api/src/main/java/edu/cornell/mannlib/vedit/tags/DynamicFieldsTag.java +++ b/api/src/main/java/edu/cornell/mannlib/vedit/tags/DynamicFieldsTag.java @@ -27,7 +27,7 @@ import edu.cornell.mannlib.vedit.beans.FormObject; import edu.cornell.mannlib.vedit.beans.DynamicField; import edu.cornell.mannlib.vedit.beans.DynamicFieldRow; import org.apache.commons.codec.binary.Base64; -import org.apache.commons.lang.StringEscapeUtils; +import org.apache.commons.lang3.StringEscapeUtils; import edu.cornell.mannlib.vedit.tags.EditTag; public class DynamicFieldsTag extends EditTag { diff --git a/api/src/main/java/edu/cornell/mannlib/vedit/tags/EditTag.java b/api/src/main/java/edu/cornell/mannlib/vedit/tags/EditTag.java index 5ff13b6b3..6ebf3f647 100644 --- a/api/src/main/java/edu/cornell/mannlib/vedit/tags/EditTag.java +++ b/api/src/main/java/edu/cornell/mannlib/vedit/tags/EditTag.java @@ -11,7 +11,7 @@ import javax.servlet.jsp.JspWriter; import edu.cornell.mannlib.vedit.beans.EditProcessObject; import edu.cornell.mannlib.vedit.beans.FormObject; -import org.apache.commons.lang.StringEscapeUtils; +import org.apache.commons.lang3.StringEscapeUtils; public class EditTag extends TagSupport { private String name = null; diff --git a/api/src/main/java/edu/cornell/mannlib/vedit/tags/ErrorTag.java b/api/src/main/java/edu/cornell/mannlib/vedit/tags/ErrorTag.java index 5b9f533dc..b61ff9884 100644 --- a/api/src/main/java/edu/cornell/mannlib/vedit/tags/ErrorTag.java +++ b/api/src/main/java/edu/cornell/mannlib/vedit/tags/ErrorTag.java @@ -7,7 +7,7 @@ import javax.servlet.jsp.tagext.TagSupport; import javax.servlet.jsp.JspWriter; import edu.cornell.mannlib.vedit.beans.FormObject; import edu.cornell.mannlib.vedit.tags.EditTag; -import org.apache.commons.lang.StringEscapeUtils; +import org.apache.commons.lang3.StringEscapeUtils; /** This tag allows validation error messages to be displayed on a form JSP **/ public class ErrorTag extends EditTag { @@ -29,7 +29,7 @@ public class ErrorTag extends EditTag { } if (errors != null){ - out.print(StringEscapeUtils.escapeHtml((String) errors)); + out.print(StringEscapeUtils.ESCAPE_HTML4.translate((String) errors)); } } catch(Exception ex) { diff --git a/api/src/main/java/edu/cornell/mannlib/vedit/tags/OptionTag.java b/api/src/main/java/edu/cornell/mannlib/vedit/tags/OptionTag.java index 8036c3082..13b7696f2 100644 --- a/api/src/main/java/edu/cornell/mannlib/vedit/tags/OptionTag.java +++ b/api/src/main/java/edu/cornell/mannlib/vedit/tags/OptionTag.java @@ -12,7 +12,7 @@ import javax.servlet.jsp.JspWriter; import edu.cornell.mannlib.vedit.beans.Option; import edu.cornell.mannlib.vedit.tags.EditTag; -import org.apache.commons.lang.StringEscapeUtils; +import org.apache.commons.lang3.StringEscapeUtils; public class OptionTag extends EditTag { private String name = null; @@ -29,11 +29,11 @@ public class OptionTag extends EditTag { opt.setValue(""); if (opt.getBody() == null) opt.setBody(""); - out.print("\n"); } } @@ -54,7 +54,7 @@ public class OptionTag extends EditTag { OrderedMapIterator ogKey = optGroups.orderedMapIterator(); while (ogKey.hasNext()) { String optGroupName = (String) ogKey.next(); - out.println(""); + out.println(""); outputOptionsMarkup((List)optGroups.get(optGroupName),out); out.println(""); } diff --git a/api/src/main/java/edu/cornell/mannlib/vedit/tags/ValueTag.java b/api/src/main/java/edu/cornell/mannlib/vedit/tags/ValueTag.java index 92619693d..48c38be1e 100644 --- a/api/src/main/java/edu/cornell/mannlib/vedit/tags/ValueTag.java +++ b/api/src/main/java/edu/cornell/mannlib/vedit/tags/ValueTag.java @@ -8,7 +8,7 @@ import javax.servlet.jsp.JspException; import javax.servlet.jsp.tagext.TagSupport; import javax.servlet.jsp.JspWriter; import edu.cornell.mannlib.vedit.beans.FormObject; -import org.apache.commons.lang.StringEscapeUtils; +import org.apache.commons.lang3.StringEscapeUtils; import edu.cornell.mannlib.vedit.tags.EditTag; public class ValueTag extends EditTag { @@ -35,7 +35,7 @@ public class ValueTag extends EditTag { if (values != null){ String value = (String) values.get(name); if (value != null) - out.print(StringEscapeUtils.escapeHtml(value)); + out.print(StringEscapeUtils.ESCAPE_HTML4.translate(value)); } else { System.out.println("ValueTag unable to get HashMap of form values"); } diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/application/ApplicationImpl.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/application/ApplicationImpl.java index 9fbcfa6ad..cf2d67a99 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/application/ApplicationImpl.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/application/ApplicationImpl.java @@ -25,7 +25,6 @@ import edu.cornell.mannlib.vitro.webapp.startup.ComponentStartupStatusImpl; import edu.cornell.mannlib.vitro.webapp.startup.StartupStatus; import edu.cornell.mannlib.vitro.webapp.triplesource.impl.BasicCombinedTripleSource; import edu.cornell.mannlib.vitro.webapp.utils.configuration.Property; -import edu.cornell.mannlib.vitro.webapp.utils.configuration.Validation; /** * The basic implementation of the Application interface. @@ -69,15 +68,9 @@ public class ApplicationImpl implements Application { return searchEngine; } - @Property(uri = "http://vitro.mannlib.cornell.edu/ns/vitro/ApplicationSetup#hasSearchEngine") + @Property(uri = "http://vitro.mannlib.cornell.edu/ns/vitro/ApplicationSetup#hasSearchEngine", minOccurs = 1, maxOccurs = 1) public void setSearchEngine(SearchEngine se) { - if (searchEngine == null) { - searchEngine = se; - } else { - throw new IllegalStateException( - "Configuration includes multiple SearchEngine instances: " - + searchEngine + ", and " + se); - } + searchEngine = se; } @Override @@ -85,15 +78,9 @@ public class ApplicationImpl implements Application { return searchIndexer; } - @Property(uri = "http://vitro.mannlib.cornell.edu/ns/vitro/ApplicationSetup#hasSearchIndexer") + @Property(uri = "http://vitro.mannlib.cornell.edu/ns/vitro/ApplicationSetup#hasSearchIndexer", minOccurs = 1, maxOccurs = 1) public void setSearchIndexer(SearchIndexer si) { - if (searchIndexer == null) { - searchIndexer = si; - } else { - throw new IllegalStateException( - "Configuration includes multiple SearchIndexer instances: " - + searchIndexer + ", and " + si); - } + searchIndexer = si; } @Override @@ -101,15 +88,9 @@ public class ApplicationImpl implements Application { return imageProcessor; } - @Property(uri = "http://vitro.mannlib.cornell.edu/ns/vitro/ApplicationSetup#hasImageProcessor") + @Property(uri = "http://vitro.mannlib.cornell.edu/ns/vitro/ApplicationSetup#hasImageProcessor", minOccurs = 1, maxOccurs = 1) public void setImageProcessor(ImageProcessor ip) { - if (imageProcessor == null) { - imageProcessor = ip; - } else { - throw new IllegalStateException( - "Configuration includes multiple ImageProcessor instances: " - + imageProcessor + ", and " + ip); - } + imageProcessor = ip; } @Override @@ -117,15 +98,9 @@ public class ApplicationImpl implements Application { return fileStorage; } - @Property(uri = "http://vitro.mannlib.cornell.edu/ns/vitro/ApplicationSetup#hasFileStorage") + @Property(uri = "http://vitro.mannlib.cornell.edu/ns/vitro/ApplicationSetup#hasFileStorage", minOccurs = 1, maxOccurs = 1) public void setFileStorage(FileStorage fs) { - if (fileStorage == null) { - fileStorage = fs; - } else { - throw new IllegalStateException( - "Configuration includes multiple FileStorage instances: " - + fileStorage + ", and " + fs); - } + fileStorage = fs; } @Override @@ -133,15 +108,9 @@ public class ApplicationImpl implements Application { return contentTripleSource; } - @Property(uri = "http://vitro.mannlib.cornell.edu/ns/vitro/ApplicationSetup#hasContentTripleSource") + @Property(uri = "http://vitro.mannlib.cornell.edu/ns/vitro/ApplicationSetup#hasContentTripleSource", minOccurs = 1, maxOccurs = 1) public void setContentTripleSource(ContentTripleSource source) { - if (contentTripleSource == null) { - contentTripleSource = source; - } else { - throw new IllegalStateException( - "Configuration includes multiple instances of ContentTripleSource: " - + contentTripleSource + ", and " + source); - } + contentTripleSource = source; } @Override @@ -149,15 +118,9 @@ public class ApplicationImpl implements Application { return configurationTripleSource; } - @Property(uri = "http://vitro.mannlib.cornell.edu/ns/vitro/ApplicationSetup#hasConfigurationTripleSource") + @Property(uri = "http://vitro.mannlib.cornell.edu/ns/vitro/ApplicationSetup#hasConfigurationTripleSource", minOccurs = 1, maxOccurs = 1) public void setConfigurationTripleSource(ConfigurationTripleSource source) { - if (configurationTripleSource == null) { - configurationTripleSource = source; - } else { - throw new IllegalStateException( - "Configuration includes multiple instances of ConfigurationTripleSource: " - + configurationTripleSource + ", and " + source); - } + configurationTripleSource = source; } @Override @@ -165,47 +128,9 @@ public class ApplicationImpl implements Application { return tboxReasonerModule; } - @Property(uri = "http://vitro.mannlib.cornell.edu/ns/vitro/ApplicationSetup#hasTBoxReasonerModule") + @Property(uri = "http://vitro.mannlib.cornell.edu/ns/vitro/ApplicationSetup#hasTBoxReasonerModule", minOccurs = 1, maxOccurs = 1) public void setTBoxReasonerModule(TBoxReasonerModule module) { - if (tboxReasonerModule == null) { - tboxReasonerModule = module; - } else { - throw new IllegalStateException( - "Configuration includes multiple instances of TBoxReasonerModule: " - + tboxReasonerModule + ", and " + module); - } - } - - @Validation - public void validate() throws Exception { - if (searchEngine == null) { - throw new IllegalStateException( - "Configuration did not include a SearchEngine."); - } - if (searchIndexer == null) { - throw new IllegalStateException( - "Configuration did not include a SearchIndexer."); - } - if (imageProcessor == null) { - throw new IllegalStateException( - "Configuration did not include an ImageProcessor."); - } - if (fileStorage == null) { - throw new IllegalStateException( - "Configuration did not include a FileStorage."); - } - if (contentTripleSource == null) { - throw new IllegalStateException( - "Configuration did not include a ContentTripleSource."); - } - if (configurationTripleSource == null) { - throw new IllegalStateException( - "Configuration did not include a ConfigurationTripleSource."); - } - if (tboxReasonerModule == null) { - throw new IllegalStateException( - "Configuration did not include a TBoxReasonerModule."); - } + tboxReasonerModule = module; } @Override diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/auth/identifier/common/IsBlacklisted.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/auth/identifier/common/IsBlacklisted.java index 8cb3f7e19..19968094b 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/auth/identifier/common/IsBlacklisted.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/auth/identifier/common/IsBlacklisted.java @@ -12,7 +12,7 @@ import java.util.Set; import javax.servlet.ServletContext; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/config/ConfigurationPropertiesSmokeTests.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/config/ConfigurationPropertiesSmokeTests.java index 31e21da32..e642cc9a6 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/config/ConfigurationPropertiesSmokeTests.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/config/ConfigurationPropertiesSmokeTests.java @@ -10,7 +10,7 @@ import javax.servlet.ServletContext; import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/FedoraDatastreamController.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/FedoraDatastreamController.java deleted file mode 100644 index 110f6f873..000000000 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/FedoraDatastreamController.java +++ /dev/null @@ -1,713 +0,0 @@ -/* $This file is distributed under the terms of the license in /doc/license.txt$ */ - -package edu.cornell.mannlib.vitro.webapp.controller; - -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.InputStream; -import java.net.MalformedURLException; -import java.security.MessageDigest; -import java.text.SimpleDateFormat; -import java.util.List; -import java.util.Properties; - -import javax.servlet.ServletContext; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import edu.cornell.mannlib.vitro.webapp.utils.JSPPageHandler; -import org.apache.commons.fileupload.FileItem; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.joda.time.DateTime; - -import org.apache.jena.datatypes.xsd.XSDDatatype; -import org.apache.jena.ontology.DatatypeProperty; -import org.apache.jena.ontology.ObjectProperty; -import org.apache.jena.ontology.OntModel; -import org.apache.jena.rdf.model.Resource; -import org.apache.jena.rdf.model.Statement; -import org.apache.jena.vocabulary.RDFS; -import org.apache.jena.vocabulary.XSD; -import com.ibm.icu.util.Calendar; - -import edu.cornell.mannlib.vedit.beans.LoginStatusBean; -import edu.cornell.mannlib.vitro.webapp.beans.DataProperty; -import edu.cornell.mannlib.vitro.webapp.beans.DataPropertyStatement; -import edu.cornell.mannlib.vitro.webapp.beans.DataPropertyStatementImpl; -import edu.cornell.mannlib.vitro.webapp.beans.Individual; -import edu.cornell.mannlib.vitro.webapp.beans.ObjectPropertyStatement; -import edu.cornell.mannlib.vitro.webapp.config.ConfigurationProperties; -import edu.cornell.mannlib.vitro.webapp.dao.IndividualDao; -import edu.cornell.mannlib.vitro.webapp.dao.VitroVocabulary; -import edu.cornell.mannlib.vitro.webapp.dao.WebappDaoFactory; -import edu.cornell.mannlib.vitro.webapp.modelaccess.ModelAccess; -import fedora.client.FedoraClient; -import fedora.common.Constants; -import fedora.server.management.FedoraAPIM; -import fedora.server.types.gen.Datastream; - - -/** - * Handles a request to change a datastream in a fedora repository. - * Some of this code is copied from N3MultiPartUpload.java - * - * @author bdc34 - * - */ -public class FedoraDatastreamController extends VitroHttpServlet implements Constants{ - private static String FEDORA_PROPERTIES = "/WEB-INF/fedora.properties"; - private static String DEFAULT_DSID = "DS1"; - - private String fedoraUrl = null; - private String adminUser = null; - private String adminPassword = null; - private String pidNamespace = null; - private String configurationStatus = "

Fedora configuration not yet loaded

"; - private boolean configured = false; - private boolean connected = false; - - private static final int DEFAULT_MAX_SIZE = 1024 * 1024 * 50;//Shoudl this be changed to 1 GB to be consistent - private static final String DEFAULT_BASE_DIR = "/usr/local/vitrofiles"; - private static String baseDirectoryForFiles = DEFAULT_BASE_DIR; - private static int maxFileSize = DEFAULT_MAX_SIZE; - - protected String contentTypeProperty = VitroVocabulary.CONTENT_TYPE; - protected String fileSizeProperty = VitroVocabulary.FILE_SIZE; - protected String fileNameProperty = VitroVocabulary.FILE_NAME; - protected String fileLocationProperty = VitroVocabulary.FILE_LOCATION; - protected String fileLabelProperty = RDFS.label.getURI(); - protected String checksumNodeProperty = "";//Object property linking file to check sum node object - protected String checksumNodeDateTimeProperty = ""; - protected String checksumNodeValueProperty = ""; - protected String checksumDataProperty = ""; //is there a vitro equivalent? - - protected String deleteNs = ""; - protected String individualPrefix = ""; - protected String fedoraNs = VitroVocabulary.VITRO_FEDORA; - - - /** - * The get will present a form to the user. - */ - @Override - public void doGet(HttpServletRequest req, HttpServletResponse res) - throws IOException, ServletException { - try { - super.doGet(req, res); - log.debug("In doGet"); - - VitroRequest vreq = new VitroRequest(req); - OntModel sessionOntModel = ModelAccess.on(getServletContext()).getOntModel(); - - synchronized (FedoraDatastreamController.class) { - if( fedoraUrl == null ){ - setup( sessionOntModel, getServletContext() ); - if( fedoraUrl == null ) - throw new FdcException("Connection to the file repository is " + - "not setup correctly. Could not read fedora.properties file"); - }else{ - if( !canConnectToFedoraServer() ){ - fedoraUrl = null; - throw new FdcException("Could not connect to Fedora."); - } - } - } - FedoraClient fedora; - try { fedora = new FedoraClient(fedoraUrl,adminUser,adminPassword); } - catch (MalformedURLException e) { - throw new FdcException("Malformed URL for fedora Repository location: " + fedoraUrl); - } - - FedoraAPIM apim; - try { apim = fedora.getAPIM(); } catch (Exception e) { - throw new FdcException("could not create fedora APIM:" + e.getMessage()); - } - - //check if logged in - - //get URI for file individual - if( req.getParameter("uri") == null || "".equals(req.getParameter("uri"))) - throw new FdcException("No file uri specified in request"); - - boolean isDelete = (req.getParameter("delete") != null && "true".equals(req.getParameter("delete"))); - - String fileUri = req.getParameter("uri"); - //check if file individual has a fedora:PID for a data stream - IndividualDao iwDao = vreq.getWebappDaoFactory().getIndividualDao(); - Individual entity = iwDao.getIndividualByURI(fileUri); - - - - if( entity == null ) - throw new FdcException( "No entity found in system for file uri " + fileUri); - //System.out.println("Entity == null:" + (entity == null)); - //get the fedora PID - //System.out.println("entity data property " + entity.getDataPropertyMap().get(VitroVocabulary.FEDORA_PID)); - if( entity.getDataPropertyMap().get(VitroVocabulary.FEDORA_PID ) == null ) - throw new FdcException( "No fedora:pid found in system for file uri " + fileUri); - List stmts = entity.getDataPropertyMap().get(VitroVocabulary.FEDORA_PID).getDataPropertyStatements(); - if( stmts == null || stmts.size() == 0) - throw new FdcException( "No fedora:pid found in system for file uri " + fileUri); - String pid = null; - for(DataPropertyStatement stmt : stmts){ - if( stmt.getData() != null && stmt.getData().length() > 0){ - pid = stmt.getData(); - break; - } - } - //System.out.println("pid is " + pid + " and comparison is " + (pid == null)); - if( pid == null ) - throw new FdcException( "No fedora:pid found in system for file uri " + fileUri); - req.setAttribute("pid", pid); - req.setAttribute("fileUri", fileUri); - //get current file name to use on form - req.setAttribute("fileName", entity.getName()); - - if(isDelete) - { - //Execute a 'deletion', i.e. unlink dataset and file, without removing file - //Also save deletion as a deleteEvent entity which can later be queried - - String datasetUri = null; - //Get dataset uri by getting the fromDataSet property - edu.cornell.mannlib.vitro.webapp.beans.ObjectProperty fromDataSet = entity.getObjectPropertyMap().get(fedoraNs + "fromDataSet"); - if(fromDataSet != null) - { - List fromDsStmts = fromDataSet.getObjectPropertyStatements(); - if(fromDsStmts.size() > 0) { - datasetUri = fromDsStmts.get(0).getObjectURI(); - //System.out.println("object uri should be " + datasetUri); - } else { - //System.out.println("No matching dataset uri could be found"); - } - } else { - //System.out.println("From dataset is null"); - } - - req.setAttribute("dataseturi", datasetUri); - boolean success = deleteFile(req, entity, iwDao, sessionOntModel); - req.setAttribute("deletesuccess", (success)?"success":"error"); - JSPPageHandler.renderBasicPage(req, res, "/edit/fileDeleteConfirm.jsp"); - } - else{ - //check if the data stream exists in the fedora repository - Datastream ds = apim.getDatastream(pid,DEFAULT_DSID,null); - if( ds == null ) - throw new FdcException("There was no datastream in the " + - "repository for " + pid + " " + DEFAULT_DSID); - req.setAttribute("dsid", DEFAULT_DSID); - - - - //forward to form - JSPPageHandler.renderBasicPage(req, res, "/fileupload/datastreamModification.jsp"); - } - }catch(FdcException ex){ - req.setAttribute("errors", ex.getMessage()); - JSPPageHandler.renderPlainPage(req, res, "/edit/fileUploadError.jsp"); - return; - } - } - - @Override - public long maximumMultipartFileSize() { - return maxFileSize; - } - - @Override - public boolean stashFileSizeException() { - return true; - } - - @Override - public void doPost(HttpServletRequest rawRequest, HttpServletResponse res) - throws ServletException, IOException { - try{ - VitroRequest req = new VitroRequest(rawRequest); - if (req.hasFileSizeException()) { - throw new FdcException("Size limit exceeded: " + req.getFileSizeException().getLocalizedMessage()); - } - if (!req.isMultipart()) { - throw new FdcException("Must POST a multipart encoded request"); - } - - //check if fedora is on line - OntModel sessionOntModel = ModelAccess.on(getServletContext()).getOntModel(); - synchronized (FedoraDatastreamController.class) { - if( fedoraUrl == null ){ - setup( sessionOntModel, getServletContext() ); - if( fedoraUrl == null ) - throw new FdcException("Connection to the file repository is " + - "not setup correctly. Could not read fedora.properties file"); - }else{ - if( !canConnectToFedoraServer() ){ - fedoraUrl = null; - throw new FdcException("Could not connect to Fedora."); - } - } - } - FedoraClient fedora; - try { fedora = new FedoraClient(fedoraUrl,adminUser,adminPassword); } - catch (MalformedURLException e) { - throw new FdcException("Malformed URL for fedora Repository location: " + fedoraUrl); - } - FedoraAPIM apim; - try { apim = fedora.getAPIM(); } catch (Exception e) { - throw new FdcException("could not create fedora APIM:" + e.getMessage()); - } - - //get the parameters from the request - String pId=req.getParameter("pid"); - String dsId=req.getParameter("dsid"); - String fileUri=req.getParameter("fileUri"); - - boolean useNewName=false; - if( "true".equals(req.getParameter("useNewName"))){ - useNewName = true; - } - if( pId == null || pId.length() == 0 ) - throw new FdcException("Your form submission did not contain " + - "enough information to complete your request.(Missing pid parameter)"); - if( dsId == null || dsId.length() == 0 ) - throw new FdcException("Your form submission did not contain " + - "enough information to complete your request.(Missing dsid parameter)"); - if( fileUri == null || fileUri.length() == 0 ) - throw new FdcException("Your form submission did not contain " + - "enough information to complete your request.(Missing fileUri parameter)"); - - FileItem fileRes = req.getFileItem("fileRes"); - if( fileRes == null ) - throw new FdcException("Your form submission did not contain " + - "enough information to complete your request.(Missing fileRes)"); - - //check if file individual has a fedora:PID for a data stream - VitroRequest vreq = new VitroRequest(rawRequest); - IndividualDao iwDao = vreq.getWebappDaoFactory().getIndividualDao(); - Individual fileEntity = iwDao.getIndividualByURI(fileUri); - - //check if logged in - //TODO: check if logged in - - //check if user is allowed to edit datastream - //TODO:check if can edit datastream - - //check if digital object and data stream exist in fedora - Datastream ds = apim.getDatastream(pId,dsId,null); - if( ds == null ) - throw new FdcException("There was no datastream in the " + - "repository for " + pId + " " + DEFAULT_DSID); - - //upload to temp holding area - String originalName = fileRes.getName(); - String name = originalName.replaceAll("[,+\\\\/$%^&*#@!<>'\"~;]", "_"); - name = name.replace("..", "_"); - name = name.trim().toLowerCase(); - - String saveLocation = baseDirectoryForFiles + File.separator + name; - String savedName = name; - int next = 0; - boolean foundUnusedName = false; - while (!foundUnusedName) { - File test = new File(saveLocation); - if (test.exists()) { - next++; - savedName = name + '(' + next + ')'; - saveLocation = baseDirectoryForFiles + File.separator + savedName; - } else { - foundUnusedName = true; - } - } - - File uploadedFile = new File(saveLocation); - - try { - fileRes.write(uploadedFile); - } catch (Exception ex) { - log.error("Unable to save POSTed file. " + ex.getMessage()); - throw new FdcException("Unable to save file to the disk. " - + ex.getMessage()); - } - - //upload to temp area on fedora - File file = new File(saveLocation); - String uploadFileUri = fedora.uploadFile( file ); - // System.out.println("Fedora upload temp = upload file uri is " + uploadFileUri); - String md5 = md5hashForFile( file ); - md5 = md5.toLowerCase(); - - //make change to data stream on fedora - apim.modifyDatastreamByReference(pId, dsId, - null, null, - fileRes.getContentType(), null, - uploadFileUri, - "MD5", null, - null, false); - - String checksum = - apim.compareDatastreamChecksum(pId,dsId,null); - - //update properties like checksum, file size, and content type - - WebappDaoFactory wdf = vreq.getWebappDaoFactory(); - DataPropertyStatement dps = null; - DataProperty contentType = wdf.getDataPropertyDao().getDataPropertyByURI(this.contentTypeProperty); - if(contentType != null) - { - wdf.getDataPropertyStatementDao().deleteDataPropertyStatementsForIndividualByDataProperty(fileEntity, contentType); - dps = new DataPropertyStatementImpl(); - dps.setIndividualURI(fileEntity.getURI()); - dps.setDatapropURI(contentType.getURI()); - dps.setData(fileRes.getContentType()); - wdf.getDataPropertyStatementDao().insertNewDataPropertyStatement(dps); - } - - DataProperty fileSize = wdf.getDataPropertyDao().getDataPropertyByURI(this.fileSizeProperty); - if(fileSize != null) - { - wdf.getDataPropertyStatementDao().deleteDataPropertyStatementsForIndividualByDataProperty(fileEntity, fileSize); - dps = new DataPropertyStatementImpl(); - dps.setIndividualURI(fileEntity.getURI()); - dps.setDatapropURI(fileSize.getURI()); - dps.setData(Long.toString(fileRes.getSize())); - wdf.getDataPropertyStatementDao().insertNewDataPropertyStatement(dps); - //System.out.println("Updated file size with " + fileRes.getSize()); - } - - DataProperty checksumDp = wdf.getDataPropertyDao().getDataPropertyByURI(this.checksumDataProperty); - if(checksumDp != null) - { - //System.out.println("Checksum data property is also not null"); - wdf.getDataPropertyStatementDao().deleteDataPropertyStatementsForIndividualByDataProperty(fileEntity, checksumDp); - dps = new DataPropertyStatementImpl(); - dps.setIndividualURI(fileEntity.getURI()); - dps.setDatapropURI(checksumDp.getURI()); - dps.setData(checksum); - wdf.getDataPropertyStatementDao().insertNewDataPropertyStatement(dps); - } - - //I'm leaving if statement out for now as the above properties are obviously being replaced as well - //if( "true".equals(useNewName)){ - //Do we need to encapuslate in this if OR is this path always for replacing a file - //TODO: Put in check to see if file name has changed and only execute these statements if file name has changed - DataProperty fileNameProperty = wdf.getDataPropertyDao().getDataPropertyByURI(this.fileNameProperty); - if(fileNameProperty != null) { - wdf.getDataPropertyStatementDao().deleteDataPropertyStatementsForIndividualByDataProperty(fileEntity, fileNameProperty); - dps = new DataPropertyStatementImpl(); - dps.setIndividualURI(fileEntity.getURI()); - dps.setDatapropURI(fileNameProperty.getURI()); - dps.setData(originalName); //This follows the pattern of the original file upload - the name returned from the uploaded file object - wdf.getDataPropertyStatementDao().insertNewDataPropertyStatement(dps); - //System.out.println("File name property is not null = " + fileNameProperty.getURI() + " updating to " + originalName); - } else { - //System.out.println("file name property is null"); - } - - //Need to also update the check sum node - how would we do that - //Find checksum node related to this particular file uri, then go ahead and update two specific fields - - ListcsNodeStatements = fileEntity.getObjectPropertyMap().get(this.checksumNodeProperty).getObjectPropertyStatements(); - if(csNodeStatements.size() == 0) { - System.out.println("No object property statements correspond to this property"); - } else { - ObjectPropertyStatement cnodeStatement = csNodeStatements.get(0); - String cnodeUri = cnodeStatement.getObjectURI(); - //System.out.println("Checksum node uri is " + cnodeUri); - - Individual checksumNodeObject = iwDao.getIndividualByURI(cnodeUri); - - DataProperty checksumDateTime = wdf.getDataPropertyDao().getDataPropertyByURI(this.checksumNodeDateTimeProperty); - if(checksumDateTime != null) { - String newDatetime = sessionOntModel.createTypedLiteral(new DateTime()).getString(); - //Review how to update date time - wdf.getDataPropertyStatementDao().deleteDataPropertyStatementsForIndividualByDataProperty(checksumNodeObject, checksumDateTime); - dps = new DataPropertyStatementImpl(); - dps.setIndividualURI(checksumNodeObject.getURI()); - dps.setDatapropURI(checksumDateTime.getURI()); - dps.setData(newDatetime); - wdf.getDataPropertyStatementDao().insertNewDataPropertyStatement(dps); - - } - DataProperty checksumNodeValue = wdf.getDataPropertyDao().getDataPropertyByURI(this.checksumDataProperty); - if(checksumNodeValue != null) { - wdf.getDataPropertyStatementDao().deleteDataPropertyStatementsForIndividualByDataProperty(checksumNodeObject, checksumNodeValue); - dps = new DataPropertyStatementImpl(); - dps.setIndividualURI(checksumNodeObject.getURI()); - dps.setDatapropURI(checksumNodeValue.getURI()); - dps.setData(checksum); //Same as fileName above - change if needed - wdf.getDataPropertyStatementDao().insertNewDataPropertyStatement(dps); - } - - } - - //Assumes original entity name is equal to the location - as occurs with regular file upload - String originalEntityName = fileEntity.getName(); - if(originalEntityName != originalName) { - //System.out.println("Setting file entity to name of uploaded file"); - fileEntity.setName(originalName); - } else { - //System.out.println("Conditional for file entity name and uploaded name is saying same"); - } - iwDao.updateIndividual(fileEntity); - //} - - req.setAttribute("fileUri", fileUri); - req.setAttribute("originalFileName", fileEntity.getName()); - req.setAttribute("checksum", checksum); - if( "true".equals(useNewName)){ - req.setAttribute("useNewName", "true"); - req.setAttribute("newFileName", originalName); - }else{ - req.setAttribute("newFileName", fileEntity.getName()); - } - - //forward to form - JSPPageHandler.renderBasicPage(req, res, "/fileupload/datastreamModificationSuccess.jsp"); - }catch(FdcException ex){ - rawRequest.setAttribute("errors", ex.getMessage()); - JSPPageHandler.renderPlainPage(rawRequest, res, "/edit/fileUploadError.jsp"); - return; - } - } - - //Delete method - public boolean deleteFile(HttpServletRequest req, Individual entity, IndividualDao iwDao, OntModel sessionOntModel) { - boolean success = false; - String fileUri = entity.getURI(); - //Create uri based on milliseconds etc.? - Calendar c = Calendar.getInstance(); - long timeMs = c.getTimeInMillis(); - //Cuirrent date - SimpleDateFormat dateTime = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS"); - String formattedDeleteDate = dateTime.format(c.getTime()); - String deleteEventName = "deleteEvent" + timeMs; - //System.out.println("Delete event name is " +deleteEventName + " - delete time is " + formattedDeleteDate); - - //Get current user - String userURI = LoginStatusBean.getBean(req).getUserURI(); - //System.out.println("Current logged in user uri is " + userURI); - - //Update model - sessionOntModel.enterCriticalSection(true); - - try { - - //Dataset Uri - String datasetUri = (String) req.getAttribute("dataseturi"); - //System.out.println("Dataset uri is " + datasetUri); - //Remove the actual relationships: dsr:hasFile and fedora:fromDataSet - ObjectProperty hasFileProperty = sessionOntModel.getObjectProperty(fedoraNs + "hasFile"); - - ObjectProperty fromDatasetProperty = sessionOntModel.getObjectProperty(fedoraNs + "fromDataSet"); - if(hasFileProperty != null) { - //System.out.println("Has file property does exist"); - sessionOntModel.remove(sessionOntModel.createStatement(sessionOntModel.getResource(datasetUri), hasFileProperty, sessionOntModel.getResource(fileUri))); - } else{ - //System.out.println("Has file property does not exist"); - } - - if(fromDatasetProperty != null) { - //System.out.println("From dataset property exists "); - sessionOntModel.remove(sessionOntModel.createStatement(sessionOntModel.getResource(fileUri), fromDatasetProperty, sessionOntModel.getResource(datasetUri))); - } else{ - //System.out.println("From dataset property does not exist"); - } - - - //Create delete event entity and update with the correct information - //Type of Event - Resource deleteEventType = sessionOntModel.createResource(deleteNs + "DeleteEvent"); - //Individual event - Resource eventIndividual = sessionOntModel.createResource(individualPrefix + deleteEventName); - //Event is of type DeleteEvent - Statement rType = sessionOntModel.createStatement(eventIndividual, org.apache.jena.vocabulary.RDF.type, deleteEventType); - sessionOntModel.add(rType); - //Add properties to individual - deleteDateTime, deletedBy, forDataSet, forFile - DatatypeProperty dateTimeProp = sessionOntModel.createDatatypeProperty(deleteNs + "deleteDateTime"); - dateTimeProp.setRange(XSD.dateTime); - - ObjectProperty deletedByProp = sessionOntModel.createObjectProperty(deleteNs + "deletedBy"); - ObjectProperty forDatasetProp = sessionOntModel.createObjectProperty(deleteNs + "forDataset"); - ObjectProperty forFileProp = sessionOntModel.createObjectProperty(deleteNs + "forFile"); - //Need to make sure date time property is set to correct xsd:DateTime - //XSDDateTime now = new XSDDateTime(c); - //XSDDateTime now = new XSDDateTime(java.util.Calendar.getInstance()); - eventIndividual.addProperty(dateTimeProp, sessionOntModel.createTypedLiteral(formattedDeleteDate, XSDDatatype.XSDdateTime)); - //eventIndividual.addProperty(dateTimeProp, sessionOntModel.createTypedLiteral(now, XSDDatatype.XSDdateTime)); - eventIndividual.addProperty(deletedByProp, sessionOntModel.getResource(userURI)); - if(datasetUri != null){ - //System.out.println("Dataset uri is " + datasetUri); - eventIndividual.addProperty(forDatasetProp, sessionOntModel.getResource(datasetUri)); - } - eventIndividual.addProperty(forFileProp, sessionOntModel.getResource(fileUri)); - success = true; - - } finally { - sessionOntModel.leaveCriticalSection(); - } - return success; - } - - @Override - public void init() throws ServletException { - super.init(); - - ConfigurationProperties configProperties = ConfigurationProperties - .getBean(getServletContext()); - baseDirectoryForFiles = configProperties.getProperty( - "n3.baseDirectoryForFiles", DEFAULT_BASE_DIR); - - String maxSize = configProperties.getProperty("n3.maxSize", Long - .toString(DEFAULT_MAX_SIZE)); - try { - maxFileSize = Integer.parseInt(maxSize); - } catch (NumberFormatException nfe) { - log.error(nfe); - maxFileSize = DEFAULT_MAX_SIZE; - } - } - - public void setup(OntModel model, ServletContext context) { - this.configurationStatus = ""; - StringBuffer status = new StringBuffer(""); - - if( connected && configured ) - return; - - Properties props = new Properties(); - String path = context.getRealPath(FEDORA_PROPERTIES); - try{ - InputStream in = new FileInputStream(new File( path )); - props.load( in ); - fedoraUrl = props.getProperty("fedoraUrl"); - adminUser = props.getProperty("adminUser"); - adminPassword = props.getProperty("adminPassword"); - pidNamespace = props.getProperty("pidNamespace"); - - if( fedoraUrl == null || adminUser == null || adminPassword == null ){ - if( fedoraUrl == null ){ - log.error("'fedoraUrl' not found in properties file"); - status.append("

'fedoraUrl' not found in properties file.

\n"); - } - if( adminUser == null ) { - log.error("'adminUser' was not found in properties file, the " + - "user name of the fedora admin is needed to access the " + - "fedora API-M services."); - status.append("

'adminUser' was not found in properties file, the " + - "user name of the fedora admin is needed to access the " + - "fedora API-M services.

\n"); - } - if( adminPassword == null ){ - log.error("'adminPassword' was not found in properties file, the " + - "admin password is needed to access the fedora API-M services."); - status.append("

'adminPassword' was not found in properties file, the " + - "admin password is needed to access the fedora API-M services.

\n"); - } - if( pidNamespace == null ){ - log.error("'pidNamespace' was not found in properties file, the " + - "PID namespace indicates which namespace to use when creating " + - "new fedor digital objects."); - status.append("

'pidNamespace' was not found in properties file, the " + - "PID namespace indicates which namespace to use when creating " + - "new fedor digital objects.

\n"); - } - fedoraUrl = null; adminUser = null; adminPassword = null; - configured = false; - } else { - configured = true; - } - }catch(FileNotFoundException e) { - log.error("No fedora.properties file found,"+ - "it should be located at " + path); - status.append("

Fedora configuration failed.

\n"); - status.append("

No fedora.properties file found,"+ - "it should be located at " + path + "

\n"); - configured = false; - return; - }catch(Exception ex){ - status.append("

Fedora configuration failed.

\n"); - status.append("

Exception while loading" + path + "

\n"); - status.append("

" + ex.getMessage() + "

\n"); - log.error("could not load fedora properties", ex); - fedoraUrl = null; adminUser = null; adminPassword = null; - configured = false; - return; - } - - - status.append(RELOAD_MSG); - this.configurationStatus += status.toString(); -// else{ -// status.append("

Fedora configuration file ").append(path).append(" was loaded

"); -// status.append("

fedoraUrl: ").append(fedoraUrl).append("

\n"); -// checkFedoraServer(); -// } - } - - private boolean canConnectToFedoraServer( ){ - try{ - FedoraClient fc = new FedoraClient(fedoraUrl,adminUser, adminPassword); - String fedoraVersion = fc.getServerVersion(); - if( fedoraVersion != null && fedoraVersion.length() > 0 ){ - configurationStatus += "

Fedora server is live and is running " + - "fedora version " + fedoraVersion + "

\n"; - connected = true; - return true; - } else { - configurationStatus += "

Unable to reach fedora server

\n"; - connected = false; - return false; - } - }catch (Exception e) { - configurationStatus += "

There was an error while checking the " + - "fedora server version

\n

"+ e.getMessage() + "

\n"; - connected = false; - return false; - } - } - - public boolean isConfigured(){ return configured; } - public boolean isConnected(){ return connected; } - - private class FdcException extends Exception { - public FdcException(String message) { - super(message); - } - } - - private static final String RELOAD_MSG = - "

The fedora configuartion file will be reloaded if " + - "you edit the properties file and check the status.

\n"; - - public static String md5hashForFile(File file){ - try { - InputStream fin = new FileInputStream(file); - java.security.MessageDigest md5er = - MessageDigest.getInstance("MD5"); - byte[] buffer = new byte[1024]; - int read; - do { - read = fin.read(buffer); - if (read > 0) - md5er.update(buffer, 0, read); - } while (read != -1); - fin.close(); - byte[] digest = md5er.digest(); - if (digest == null) - return null; - String strDigest = "0x"; - for (int i = 0; i < digest.length; i++) { - strDigest += Integer.toString((digest[i] & 0xff) - + 0x100, 16).substring(1); - } - return strDigest; - } catch (Exception e) { - return null; - } - } - - private static final Log log = LogFactory.getLog(FedoraDatastreamController.class.getName()); -} diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/accounts/UserAccountsSelector.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/accounts/UserAccountsSelector.java index 0c684134c..af803122a 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/accounts/UserAccountsSelector.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/accounts/UserAccountsSelector.java @@ -20,9 +20,9 @@ import org.apache.jena.rdf.model.Resource; import edu.cornell.mannlib.vitro.webapp.beans.UserAccount; import edu.cornell.mannlib.vitro.webapp.beans.UserAccount.Status; import edu.cornell.mannlib.vitro.webapp.controller.accounts.UserAccountsOrdering.Field; -import edu.cornell.mannlib.vitro.webapp.utils.SparqlQueryRunner; -import edu.cornell.mannlib.vitro.webapp.utils.SparqlQueryRunner.QueryParser; -import edu.cornell.mannlib.vitro.webapp.utils.SparqlQueryUtils; +import edu.cornell.mannlib.vitro.webapp.utils.sparql.SparqlQueryUtils; +import edu.cornell.mannlib.vitro.webapp.utils.sparqlrunner.ResultSetParser; +import edu.cornell.mannlib.vitro.webapp.utils.sparqlrunner.SparqlQueryRunner; /** * Pull some UserAccounts from the model, based on a set of criteria. @@ -111,8 +111,9 @@ public class UserAccountsSelector { .replace("%offset%", offset()); log.debug("main query: " + qString); - List accounts = new SparqlQueryRunner(model) - .executeSelect(new MainQueryParser(), qString); + List accounts = SparqlQueryRunner + .createSelectQueryContext(model, qString).execute() + .parse(new MainQueryParser()); log.debug("query returns: " + accounts); return accounts; } @@ -126,8 +127,8 @@ public class UserAccountsSelector { .replace("%filterClauses%", filterClauses()); log.debug("count query: " + qString); - int count = new SparqlQueryRunner(model).executeSelect( - new CountQueryParser(), qString); + int count = SparqlQueryRunner.createSelectQueryContext(model, qString) + .execute().parse(new CountQueryParser()); log.debug("result count: " + count); return count; } @@ -139,8 +140,9 @@ public class UserAccountsSelector { PREFIX_LINES).replace("%uri%", uri); log.debug("permissions query: " + qString); - Set permissions = new SparqlQueryRunner(model) - .executeSelect(new PermissionsQueryParser(), qString); + Set permissions = SparqlQueryRunner + .createSelectQueryContext(model, qString).execute() + .parse(new PermissionsQueryParser()); log.debug("permissions for '" + uri + "': " + permissions); account.setPermissionSetUris(permissions); } @@ -214,7 +216,8 @@ public class UserAccountsSelector { return String.valueOf(offset); } - private static class MainQueryParser extends QueryParser> { + private static class MainQueryParser extends + ResultSetParser> { @Override protected List defaultValue() { return Collections.emptyList(); @@ -274,7 +277,7 @@ public class UserAccountsSelector { } } - private static class CountQueryParser extends QueryParser { + private static class CountQueryParser extends ResultSetParser { @Override protected Integer defaultValue() { return 0; @@ -299,7 +302,7 @@ public class UserAccountsSelector { } private static class PermissionsQueryParser extends - QueryParser> { + ResultSetParser> { @Override protected Set defaultValue() { return Collections.emptySet(); diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/accounts/manageproxies/ProxyRelationshipSelector.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/accounts/manageproxies/ProxyRelationshipSelector.java index 22ba17d6c..95e210dd7 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/accounts/manageproxies/ProxyRelationshipSelector.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/accounts/manageproxies/ProxyRelationshipSelector.java @@ -16,9 +16,9 @@ import org.apache.jena.query.ResultSet; import edu.cornell.mannlib.vitro.webapp.controller.accounts.manageproxies.ProxyRelationshipSelectionBuilder.ItemInfo; import edu.cornell.mannlib.vitro.webapp.controller.accounts.manageproxies.ProxyRelationshipSelectionBuilder.Relationship; import edu.cornell.mannlib.vitro.webapp.controller.accounts.manageproxies.ProxyRelationshipSelectionCriteria.ProxyRelationshipView; -import edu.cornell.mannlib.vitro.webapp.utils.SparqlQueryRunner; -import edu.cornell.mannlib.vitro.webapp.utils.SparqlQueryRunner.QueryParser; -import edu.cornell.mannlib.vitro.webapp.utils.SparqlQueryUtils; +import edu.cornell.mannlib.vitro.webapp.utils.sparql.SparqlQueryUtils; +import edu.cornell.mannlib.vitro.webapp.utils.sparqlrunner.ResultSetParser; +import edu.cornell.mannlib.vitro.webapp.utils.sparqlrunner.SparqlQueryRunner; /** * A class which will accept a ProxyRelationshipSelectionCriteria and produce a @@ -94,8 +94,9 @@ public class ProxyRelationshipSelector { PREFIX_LINES); qString = replaceFilterClauses(qString); - int count = new SparqlQueryRunner(context.userAccountsModel) - .executeSelect(new CountQueryParser(), qString); + int count = SparqlQueryRunner + .createSelectQueryContext(context.userAccountsModel, qString) + .execute().parse(new CountQueryParser()); log.debug("result count: " + count); builder.count = count; @@ -136,9 +137,9 @@ public class ProxyRelationshipSelector { .replace("%offset%", offset()); qString = replaceFilterClauses(qString); - List relationships = new SparqlQueryRunner( - context.userAccountsModel).executeSelect( - new ProxyBasicsParser(), qString); + List relationships = SparqlQueryRunner + .createSelectQueryContext(context.userAccountsModel, qString) + .execute().parse(new ProxyBasicsParser()); log.debug("getProxyBasics returns: " + relationships); builder.relationships.addAll(relationships); } @@ -177,8 +178,9 @@ public class ProxyRelationshipSelector { .replace("%matchingProperty%", context.matchingProperty) .replace("%externalAuthId%", proxy.externalAuthId); - ItemInfo expansion = new SparqlQueryRunner(context.unionModel) - .executeSelect(new ExpandProxyParser(), qString); + ItemInfo expansion = SparqlQueryRunner + .createSelectQueryContext(context.unionModel, qString) + .execute().parse(new ExpandProxyParser()); proxy.classLabel = expansion.classLabel; proxy.imageUrl = expansion.imageUrl; } @@ -199,9 +201,10 @@ public class ProxyRelationshipSelector { String qString = QUERY_RELATIONSHIPS.replace("%prefixes%", PREFIX_LINES).replace("%proxyUri%", proxy.uri); - List profileUris = new SparqlQueryRunner( - context.userAccountsModel).executeSelect( - new RelationshipsParser(), qString); + List profileUris = SparqlQueryRunner + .createSelectQueryContext(context.userAccountsModel, + qString).execute() + .parse(new RelationshipsParser()); for (String profileUri : profileUris) { r.profileInfos @@ -235,8 +238,9 @@ public class ProxyRelationshipSelector { String qString = QUERY_EXPAND_PROFILE.replace("%prefixes%", PREFIX_LINES).replace("%profileUri%", profile.uri); - ItemInfo expansion = new SparqlQueryRunner(context.unionModel) - .executeSelect(new ExpandProfileParser(), qString); + ItemInfo expansion = SparqlQueryRunner + .createSelectQueryContext(context.unionModel, qString) + .execute().parse(new ExpandProfileParser()); profile.label = expansion.label; profile.classLabel = expansion.classLabel; profile.imageUrl = expansion.imageUrl; @@ -285,7 +289,7 @@ public class ProxyRelationshipSelector { // ---------------------------------------------------------------------- private static class ProxyBasicsParser extends - QueryParser> { + ResultSetParser> { @Override protected List defaultValue() { return Collections.emptyList(); @@ -318,7 +322,7 @@ public class ProxyRelationshipSelector { } } - private static class CountQueryParser extends QueryParser { + private static class CountQueryParser extends ResultSetParser { @Override protected Integer defaultValue() { return 0; @@ -342,7 +346,7 @@ public class ProxyRelationshipSelector { } } - private static class ExpandProxyParser extends QueryParser { + private static class ExpandProxyParser extends ResultSetParser { @Override protected ItemInfo defaultValue() { return new ItemInfo(); @@ -367,7 +371,8 @@ public class ProxyRelationshipSelector { } } - private static class RelationshipsParser extends QueryParser> { + private static class RelationshipsParser extends + ResultSetParser> { @Override protected List defaultValue() { return Collections.emptyList(); @@ -388,7 +393,7 @@ public class ProxyRelationshipSelector { } } - private static class ExpandProfileParser extends QueryParser { + private static class ExpandProfileParser extends ResultSetParser { @Override protected ItemInfo defaultValue() { return new ItemInfo(); diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/accounts/manageproxies/ajax/BasicProxiesGetter.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/accounts/manageproxies/ajax/BasicProxiesGetter.java index 85db98834..f1c3d0c3e 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/accounts/manageproxies/ajax/BasicProxiesGetter.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/accounts/manageproxies/ajax/BasicProxiesGetter.java @@ -24,8 +24,8 @@ import edu.cornell.mannlib.vitro.webapp.controller.ajax.AbstractAjaxResponder; import edu.cornell.mannlib.vitro.webapp.controller.freemarker.UrlBuilder; import edu.cornell.mannlib.vitro.webapp.dao.VitroVocabulary; import edu.cornell.mannlib.vitro.webapp.modelaccess.ModelAccess; -import edu.cornell.mannlib.vitro.webapp.utils.SparqlQueryRunner; -import edu.cornell.mannlib.vitro.webapp.utils.SparqlQueryUtils; +import edu.cornell.mannlib.vitro.webapp.utils.sparql.SparqlQueryUtils; +import edu.cornell.mannlib.vitro.webapp.utils.sparqlrunner.SparqlQueryRunner; import edu.cornell.mannlib.vitro.webapp.web.images.PlaceholderUtil; /** @@ -76,10 +76,10 @@ public class BasicProxiesGetter extends AbstractAjaxResponder { String cleanTerm = SparqlQueryUtils.escapeForRegex(term); String queryStr = QUERY_BASIC_PROXIES.replace("%term%", cleanTerm); - JSONArray jsonArray = new SparqlQueryRunner(userAccountsModel) - .executeSelect( - new BasicProxyInfoParser(placeholderImageUrl), - queryStr); + JSONArray jsonArray = SparqlQueryRunner + .createSelectQueryContext(userAccountsModel, queryStr) + .execute() + .parse(new BasicProxyInfoParser(placeholderImageUrl)); String response = jsonArray.toString(); log.debug(response); diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/accounts/user/UserAccountsFirstTimeExternalPage.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/accounts/user/UserAccountsFirstTimeExternalPage.java index c48ba8691..ffffc1b1a 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/accounts/user/UserAccountsFirstTimeExternalPage.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/accounts/user/UserAccountsFirstTimeExternalPage.java @@ -9,7 +9,7 @@ import java.util.Map; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpSession; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import edu.cornell.mannlib.vitro.webapp.auth.permissions.PermissionSets; import edu.cornell.mannlib.vitro.webapp.beans.UserAccount; diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/admin/RestrictLoginsController.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/admin/RestrictLoginsController.java index a3f04be48..e80d7f2d5 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/admin/RestrictLoginsController.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/admin/RestrictLoginsController.java @@ -16,6 +16,7 @@ import edu.cornell.mannlib.vitro.webapp.controller.freemarker.FreemarkerHttpServ import edu.cornell.mannlib.vitro.webapp.controller.freemarker.UrlBuilder; import edu.cornell.mannlib.vitro.webapp.controller.freemarker.responsevalues.ResponseValues; import edu.cornell.mannlib.vitro.webapp.controller.freemarker.responsevalues.TemplateResponseValues; +import edu.cornell.mannlib.vitro.webapp.i18n.I18n; /** * Offer the user the ability to apply a RestrictedAuthenticator or revert to a @@ -80,7 +81,7 @@ public class RestrictLoginsController extends FreemarkerHttpServlet { boolean restricted = figureCurrentlyState() == State.RESTRICTED; Map body = new HashMap(); - body.put("title", "Restrict Logins"); + body.put("title", I18n.text(vreq, "restrict_logins")); body.put("restricted", restricted); if (!MESSAGE_NO_MESSAGE.equals(messageCode)) { body.put(messageCode, Boolean.TRUE); diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/admin/SparqlQueryController.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/admin/SparqlQueryController.java index da4fc589f..bba1e0865 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/admin/SparqlQueryController.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/admin/SparqlQueryController.java @@ -16,7 +16,7 @@ import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -36,9 +36,9 @@ import edu.cornell.mannlib.vitro.webapp.dao.OntologyDao; import edu.cornell.mannlib.vitro.webapp.modelaccess.ModelAccess; import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFService; import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFServiceException; -import edu.cornell.mannlib.vitro.webapp.utils.SparqlQueryUtils; import edu.cornell.mannlib.vitro.webapp.utils.http.AcceptHeaderParsingException; import edu.cornell.mannlib.vitro.webapp.utils.http.NotAcceptableException; +import edu.cornell.mannlib.vitro.webapp.utils.sparql.SparqlQueryUtils; /** * Present the SPARQL Query form, and execute the queries. diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/admin/StartupStatusController.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/admin/StartupStatusController.java index 991f53792..ae295ed35 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/admin/StartupStatusController.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/admin/StartupStatusController.java @@ -11,6 +11,7 @@ import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest; import edu.cornell.mannlib.vitro.webapp.controller.freemarker.FreemarkerHttpServlet; import edu.cornell.mannlib.vitro.webapp.controller.freemarker.responsevalues.ResponseValues; import edu.cornell.mannlib.vitro.webapp.controller.freemarker.responsevalues.TemplateResponseValues; +import edu.cornell.mannlib.vitro.webapp.i18n.I18n; import edu.cornell.mannlib.vitro.webapp.startup.StartupStatus; /** @@ -27,7 +28,7 @@ public class StartupStatusController extends FreemarkerHttpServlet { protected ResponseValues processRequest(VitroRequest vreq) { Map body = new HashMap(); - body.put("title", "Startup Status"); + body.put("title", I18n.text(vreq, "startup_status")); body.put("status", StartupStatus.getBean(getServletContext())); body.put("contextPath", getContextPath()); body.put("applicationName", getApplicationName(vreq)); diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/ajax/AbstractAjaxResponder.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/ajax/AbstractAjaxResponder.java index 3c80b8070..3faa4c968 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/ajax/AbstractAjaxResponder.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/ajax/AbstractAjaxResponder.java @@ -24,7 +24,7 @@ import org.apache.jena.query.ResultSet; import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest; import edu.cornell.mannlib.vitro.webapp.dao.IndividualDao; import edu.cornell.mannlib.vitro.webapp.dao.UserAccountsDao; -import edu.cornell.mannlib.vitro.webapp.utils.SparqlQueryRunner; +import edu.cornell.mannlib.vitro.webapp.utils.sparqlrunner.ResultSetParser; /** * A base class for AJAX responder objects, to be instantiated and invoked by @@ -82,7 +82,7 @@ public abstract class AbstractAjaxResponder { */ protected String assembleJsonResponse(List> maps) { JSONArray jsonArray = new JSONArray(); - for (Map map: maps) { + for (Map map : maps) { jsonArray.put(map); } return jsonArray.toString(); @@ -93,7 +93,7 @@ public abstract class AbstractAjaxResponder { * implement "parseSolutionRow()" */ protected abstract static class JsonArrayParser extends - SparqlQueryRunner.QueryParser { + ResultSetParser { @Override protected JSONArray defaultValue() { return new JSONArray(); diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/api/SparqlUpdateApiController.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/api/SparqlUpdateApiController.java index 7feecbc1e..b045b118c 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/api/SparqlUpdateApiController.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/api/SparqlUpdateApiController.java @@ -14,7 +14,7 @@ import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/api/sparqlquery/SparqlQueryApiExecutor.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/api/sparqlquery/SparqlQueryApiExecutor.java index 13a274d36..e440e9131 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/api/sparqlquery/SparqlQueryApiExecutor.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/api/sparqlquery/SparqlQueryApiExecutor.java @@ -10,9 +10,9 @@ import org.apache.jena.query.QueryParseException; import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFService; import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFServiceException; -import edu.cornell.mannlib.vitro.webapp.utils.SparqlQueryUtils; import edu.cornell.mannlib.vitro.webapp.utils.http.AcceptHeaderParsingException; import edu.cornell.mannlib.vitro.webapp.utils.http.NotAcceptableException; +import edu.cornell.mannlib.vitro.webapp.utils.sparql.SparqlQueryUtils; /** * The base class for the SPARQL query API. diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/authenticate/LoginRedirector.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/authenticate/LoginRedirector.java index 4b3dd4769..05197a731 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/authenticate/LoginRedirector.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/authenticate/LoginRedirector.java @@ -11,7 +11,7 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/datatools/dumprestore/DumpRestoreController.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/datatools/dumprestore/DumpRestoreController.java index f9ca53732..38eb933e2 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/datatools/dumprestore/DumpRestoreController.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/datatools/dumprestore/DumpRestoreController.java @@ -11,7 +11,7 @@ import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import edu.cornell.mannlib.vitro.webapp.auth.permissions.SimplePermission; import edu.cornell.mannlib.vitro.webapp.auth.policy.PolicyHelper; diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/edit/PrimitiveDelete.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/edit/PrimitiveDelete.java index 9e4b0b389..8b520de78 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/edit/PrimitiveDelete.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/edit/PrimitiveDelete.java @@ -6,7 +6,7 @@ import static javax.servlet.http.HttpServletResponse.SC_UNAUTHORIZED; import javax.servlet.http.HttpServletResponse; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/edit/ReorderController.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/edit/ReorderController.java index 160a65cd4..0130aec51 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/edit/ReorderController.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/edit/ReorderController.java @@ -32,6 +32,8 @@ import edu.cornell.mannlib.vitro.webapp.dao.DataPropertyStatementDao; import edu.cornell.mannlib.vitro.webapp.dao.WebappDaoFactory; import edu.cornell.mannlib.vitro.webapp.dao.jena.event.EditEvent; +import java.io.IOException; + /** * This controller receives Ajax requests for reordering a list of individuals. * Parameters: @@ -102,10 +104,13 @@ public class ReorderController extends VitroAjaxController { // This may not be the most efficient way. Should we instead build up a Model of retractions and additions, so // we only hit the database once? reorderIndividuals(individualUris, vreq, rankPredicate); - - - response.setStatus(SC_OK); - + + response.setStatus(SC_OK); + try { + response.getWriter().write("{}"); + } catch (IOException e) { + e.printStackTrace(); + } } private void reorderIndividuals(String[] individualUris, VitroRequest vreq, String rankPredicate) { diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/edit/listing/AllClassGroupsListingController.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/edit/listing/AllClassGroupsListingController.java index 971b0b301..6423387d4 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/edit/listing/AllClassGroupsListingController.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/edit/listing/AllClassGroupsListingController.java @@ -11,7 +11,7 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import edu.cornell.mannlib.vitro.webapp.utils.JSPPageHandler; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import edu.cornell.mannlib.vedit.controller.BaseEditController; import edu.cornell.mannlib.vitro.webapp.auth.permissions.SimplePermission; diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/edit/listing/PropertyGroupsListingController.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/edit/listing/PropertyGroupsListingController.java index 7769e521c..092c1396b 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/edit/listing/PropertyGroupsListingController.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/edit/listing/PropertyGroupsListingController.java @@ -14,7 +14,7 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import edu.cornell.mannlib.vitro.webapp.utils.JSPPageHandler; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/freemarker/ContactFormController.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/freemarker/ContactFormController.java index 82b4eea0d..239940e54 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/freemarker/ContactFormController.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/freemarker/ContactFormController.java @@ -5,7 +5,7 @@ package edu.cornell.mannlib.vitro.webapp.controller.freemarker; import java.util.HashMap; import java.util.Map; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/freemarker/DumpTestController.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/freemarker/DumpTestController.java index 4c09a40ab..4ba3ee4e6 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/freemarker/DumpTestController.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/freemarker/DumpTestController.java @@ -13,7 +13,7 @@ import java.util.List; import java.util.Map; import java.util.Set; -import org.apache.commons.lang.time.DateUtils; +import org.apache.commons.lang3.time.DateUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/freemarker/FreemarkerComponentGenerator.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/freemarker/FreemarkerComponentGenerator.java index e01b14cd9..29785c64f 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/freemarker/FreemarkerComponentGenerator.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/freemarker/FreemarkerComponentGenerator.java @@ -7,7 +7,7 @@ import java.util.Map; import javax.servlet.ServletContext; import javax.servlet.http.HttpServletRequest; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/freemarker/FreemarkerHttpServlet.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/freemarker/FreemarkerHttpServlet.java index 5a8a7fa20..5d97c7b98 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/freemarker/FreemarkerHttpServlet.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/freemarker/FreemarkerHttpServlet.java @@ -17,7 +17,7 @@ import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/freemarker/IndividualListController.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/freemarker/IndividualListController.java index 728a4c77c..d200bc95d 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/freemarker/IndividualListController.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/freemarker/IndividualListController.java @@ -9,7 +9,7 @@ import java.util.List; import java.util.Map; import edu.cornell.mannlib.vitro.webapp.web.templatemodels.individuallist.ListedIndividualBuilder; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -22,6 +22,7 @@ import edu.cornell.mannlib.vitro.webapp.controller.freemarker.responsevalues.Res import edu.cornell.mannlib.vitro.webapp.controller.freemarker.responsevalues.TemplateResponseValues; import edu.cornell.mannlib.vitro.webapp.controller.individuallist.IndividualListResults; import edu.cornell.mannlib.vitro.webapp.dao.IndividualDao; +import edu.cornell.mannlib.vitro.webapp.i18n.I18n; import edu.cornell.mannlib.vitro.webapp.modules.searchEngine.SearchEngineException; import edu.cornell.mannlib.vitro.webapp.modules.searchEngine.SearchQuery; import edu.cornell.mannlib.vitro.webapp.utils.searchengine.SearchQueryUtils; @@ -94,7 +95,7 @@ public class IndividualListController extends FreemarkerHttpServlet { vclass.getURI(), page, alpha, - vreq.getWebappDaoFactory().getIndividualDao()); + vreq); body.putAll(vcResults.asFreemarkerMap()); List inds = vcResults.getEntities(); @@ -148,12 +149,12 @@ public class IndividualListController extends FreemarkerHttpServlet { return SearchQueryUtils.getPageParameter(request); } - public static IndividualListResults getResultsForVClass(String vclassURI, int page, String alpha, IndividualDao indDao) + public static IndividualListResults getResultsForVClass(String vclassURI, int page, String alpha, VitroRequest vreq) throws SearchException{ try{ List classUris = Collections.singletonList(vclassURI); - IndividualListQueryResults results = buildAndExecuteVClassQuery(classUris, alpha, page, INDIVIDUALS_PER_PAGE, indDao); - return getResultsForVClassQuery(results, page, INDIVIDUALS_PER_PAGE, alpha); + IndividualListQueryResults results = buildAndExecuteVClassQuery(classUris, alpha, page, INDIVIDUALS_PER_PAGE, vreq.getWebappDaoFactory().getIndividualDao()); + return getResultsForVClassQuery(results, page, INDIVIDUALS_PER_PAGE, alpha, vreq); } catch (SearchEngineException e) { String msg = "An error occurred retrieving results for vclass query"; log.error(msg, e); @@ -165,31 +166,31 @@ public class IndividualListController extends FreemarkerHttpServlet { } } - public static IndividualListResults getResultsForVClassIntersections(List vclassURIs, int page, int pageSize, String alpha, IndividualDao indDao) { + public static IndividualListResults getResultsForVClassIntersections(List vclassURIs, int page, int pageSize, String alpha, VitroRequest vreq) { try{ - IndividualListQueryResults results = buildAndExecuteVClassQuery(vclassURIs, alpha, page, pageSize, indDao); - return getResultsForVClassQuery(results, page, pageSize, alpha); + IndividualListQueryResults results = buildAndExecuteVClassQuery(vclassURIs, alpha, page, pageSize, vreq.getWebappDaoFactory().getIndividualDao()); + return getResultsForVClassQuery(results, page, pageSize, alpha, vreq); } catch(Throwable th) { log.error("Error retrieving individuals corresponding to intersection multiple classes." + vclassURIs.toString(), th); return IndividualListResults.EMPTY; } } - public static IndividualListResults getRandomResultsForVClass(String vclassURI, int page, int pageSize, IndividualDao indDao) { + public static IndividualListResults getRandomResultsForVClass(String vclassURI, int page, int pageSize, VitroRequest vreq) { try{ List classUris = Collections.singletonList(vclassURI); - IndividualListQueryResults results = buildAndExecuteRandomVClassQuery(classUris, page, pageSize, indDao); - return getResultsForVClassQuery(results, page, pageSize, ""); + IndividualListQueryResults results = buildAndExecuteRandomVClassQuery(classUris, page, pageSize, vreq.getWebappDaoFactory().getIndividualDao()); + return getResultsForVClassQuery(results, page, pageSize, "", vreq); } catch(Throwable th) { log.error("An error occurred retrieving random results for vclass query", th); return IndividualListResults.EMPTY; } } - private static IndividualListResults getResultsForVClassQuery(IndividualListQueryResults results, int page, int pageSize, String alpha) { + private static IndividualListResults getResultsForVClassQuery(IndividualListQueryResults results, int page, int pageSize, String alpha, VitroRequest vreq) { long hitCount = results.getHitCount(); if ( hitCount > pageSize ){ - return new IndividualListResults(hitCount, results.getIndividuals(), alpha, true, makePagesList(hitCount, pageSize, page)); + return new IndividualListResults(hitCount, results.getIndividuals(), alpha, true, makePagesList(hitCount, pageSize, page, vreq)); }else{ return new IndividualListResults(hitCount, results.getIndividuals(), alpha, false, Collections.emptyList()); } @@ -221,7 +222,7 @@ public class IndividualListController extends FreemarkerHttpServlet { } - public static List makePagesList( long size, int pageSize, int selectedPage ) { + public static List makePagesList( long size, int pageSize, int selectedPage , VitroRequest vreq) { List records = new ArrayList( MAX_PAGES + 1 ); int requiredPages = (int) (size/pageSize) ; @@ -234,7 +235,7 @@ public class IndividualListController extends FreemarkerHttpServlet { for(int page = 1; page < requiredPages && page <= MAX_PAGES ; page++ ){ records.add( new PageRecord( "page=" + page, Integer.toString(page), Integer.toString(page), selectedPage == page ) ); } - records.add( new PageRecord( "page="+ (MAX_PAGES+1), Integer.toString(MAX_PAGES+1), "more...", false)); + records.add( new PageRecord( "page="+ (MAX_PAGES+1), Integer.toString(MAX_PAGES+1), I18n.text(vreq, "paging_link_more"), false)); }else if( requiredPages > MAX_PAGES && selectedPage+1 > MAX_PAGES && selectedPage < requiredPages - MAX_PAGES){ //the selected pages is in the middle of the list of page int startPage = selectedPage - MAX_PAGES / 2; @@ -242,7 +243,7 @@ public class IndividualListController extends FreemarkerHttpServlet { for(int page = startPage; page <= endPage ; page++ ){ records.add( new PageRecord( "page=" + page, Integer.toString(page), Integer.toString(page), selectedPage == page ) ); } - records.add( new PageRecord( "page="+ (endPage+1), Integer.toString(endPage+1), "more...", false)); + records.add( new PageRecord( "page="+ (endPage+1), Integer.toString(endPage+1), I18n.text(vreq, "paging_link_more"), false)); }else if ( requiredPages > MAX_PAGES && selectedPage > requiredPages - MAX_PAGES ){ //the selected page is in the end of the list int startPage = requiredPages - MAX_PAGES; diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/freemarker/ListClassGroupsController.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/freemarker/ListClassGroupsController.java index 75a728df9..097212e0e 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/freemarker/ListClassGroupsController.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/freemarker/ListClassGroupsController.java @@ -9,7 +9,7 @@ import java.util.Map; import net.sf.json.util.JSONUtils; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/freemarker/ListPropertyGroupsController.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/freemarker/ListPropertyGroupsController.java index 5ac98a5bd..35d37468d 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/freemarker/ListPropertyGroupsController.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/freemarker/ListPropertyGroupsController.java @@ -10,7 +10,7 @@ import java.util.Map; import net.sf.json.util.JSONUtils; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/freemarker/PageController.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/freemarker/PageController.java index f772537c7..7b89e08e1 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/freemarker/PageController.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/freemarker/PageController.java @@ -14,7 +14,7 @@ import javax.servlet.ServletContext; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -27,6 +27,7 @@ import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest; import edu.cornell.mannlib.vitro.webapp.controller.freemarker.responsevalues.ResponseValues; import edu.cornell.mannlib.vitro.webapp.controller.freemarker.responsevalues.TemplateResponseValues; import edu.cornell.mannlib.vitro.webapp.dao.DisplayVocabulary; +import edu.cornell.mannlib.vitro.webapp.i18n.I18n; import edu.cornell.mannlib.vitro.webapp.utils.dataGetter.DataGetter; import edu.cornell.mannlib.vitro.webapp.utils.dataGetter.DataGetterUtils; /** @@ -188,23 +189,23 @@ public class PageController extends FreemarkerHttpServlet{ private ResponseValues doError(VitroRequest vreq) { Map body = new HashMap(); - body.put("title","Page could not be created"); - body.put("errorMessage", "There was an error while creating the page, please check the logs."); + body.put("title", I18n.text(vreq, "page_not_created")); + body.put("errorMessage", I18n.text(vreq, "page_not_created_msg")); return new TemplateResponseValues(Template.TITLED_ERROR_MESSAGE.toString(), body, HttpServletResponse.SC_INTERNAL_SERVER_ERROR); } private ResponseValues doNotFound(VitroRequest vreq) { Map body = new HashMap(); - body.put("title","Page Not Found"); - body.put("errorMessage", "The page was not found in the system."); + body.put("title", I18n.text(vreq, "page_not_found")); + body.put("errorMessage", I18n.text(vreq, "page_not_found_msg")); return new TemplateResponseValues(Template.TITLED_ERROR_MESSAGE.toString(), body, HttpServletResponse.SC_NOT_FOUND); } private ResponseValues doNoPageSpecified(VitroRequest vreq) { Map body = new HashMap(); - body.put("title","No page URI specified"); - body.put("errorMessage", "Could not generate page beacause it was unclear what page was being requested. A URL mapping may be missing."); + body.put("title",I18n.text(vreq, "page_uri_missing")); + body.put("errorMessage", I18n.text(vreq, "page_uri_missing_msg")); return new TemplateResponseValues(Template.TITLED_ERROR_MESSAGE.toString(), body, HttpServletResponse.SC_NOT_FOUND); } diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/freemarker/StaticPageController.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/freemarker/StaticPageController.java index b4c115f6a..ed02c9810 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/freemarker/StaticPageController.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/freemarker/StaticPageController.java @@ -5,12 +5,14 @@ package edu.cornell.mannlib.vitro.webapp.controller.freemarker; import java.util.HashMap; import java.util.Map; +import org.apache.commons.lang3.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest; import edu.cornell.mannlib.vitro.webapp.controller.freemarker.responsevalues.ResponseValues; import edu.cornell.mannlib.vitro.webapp.controller.freemarker.responsevalues.TemplateResponseValues; +import edu.cornell.mannlib.vitro.webapp.i18n.I18n; /* * Servlet that only specifies a template, without putting any data @@ -33,7 +35,7 @@ public class StaticPageController extends FreemarkerHttpServlet { String requestedUrl = vreq.getServletPath(); String title = null; if (requestedUrl.equals("/login")) { - title = "Log in to " + siteName; + title = StringUtils.capitalize(I18n.text(vreq, "log_in")) + " - " + siteName; } return title; } diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/freemarker/UrlBuilder.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/freemarker/UrlBuilder.java index b4b6a8164..efbe66f2c 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/freemarker/UrlBuilder.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/freemarker/UrlBuilder.java @@ -71,7 +71,7 @@ public class UrlBuilder { public enum Css { CUSTOM_FORM("/edit/forms/css/customForm.css"), - JQUERY_UI("/js/jquery-ui/css/smoothness/jquery-ui-1.8.9.custom.css"); + JQUERY_UI("/js/jquery-ui/css/smoothness/jquery-ui-1.12.1.css"); private final String path; @@ -90,8 +90,9 @@ public class UrlBuilder { public enum JavaScript { CUSTOM_FORM_UTILS("/js/customFormUtils.js"), - JQUERY("/js/jquery.js"), - JQUERY_UI("/js/jquery-ui/js/jquery-ui-1.8.9.custom.min.js"), + JQUERY("/js/jquery-1.12.4.min.js"), + JQUERY_MIGRATE("/js/jquery-migrate-1.4.1.js"), + JQUERY_UI("/js/jquery-ui/js/jquery-ui-1.12.1.min.js"), UTILS("/js/utils.js"); private final String path; diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/grefine/JSONReconcileServlet.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/grefine/JSONReconcileServlet.java index 29b005548..ec867cf0b 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/grefine/JSONReconcileServlet.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/grefine/JSONReconcileServlet.java @@ -15,7 +15,7 @@ import javax.servlet.ServletOutputStream; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.json.JSONArray; @@ -470,18 +470,10 @@ public class JSONReconcileServlet extends VitroHttpServlet { return label; } - public String getJsonLabel() { - return JSONObject.quote(label); - } - public String getUri() { return uri; } - public String getJsonUri() { - return JSONObject.quote(uri); - } - Map toMap() { Map map = new HashMap(); map.put("label", label); diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/individual/IndividualController.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/individual/IndividualController.java index 4c14f4a9c..5abf27a70 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/individual/IndividualController.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/individual/IndividualController.java @@ -21,6 +21,7 @@ import edu.cornell.mannlib.vitro.webapp.controller.freemarker.responsevalues.Exc import edu.cornell.mannlib.vitro.webapp.controller.freemarker.responsevalues.RedirectResponseValues; import edu.cornell.mannlib.vitro.webapp.controller.freemarker.responsevalues.ResponseValues; import edu.cornell.mannlib.vitro.webapp.controller.freemarker.responsevalues.TemplateResponseValues; +import edu.cornell.mannlib.vitro.webapp.i18n.I18n; /** * Handles requests for entity information. @@ -74,7 +75,7 @@ public class IndividualController extends FreemarkerHttpServlet { * If we can't figure out what individual you want, or if there * is no such individual, show an informative error page. */ - return doNotFound(); + return doNotFound(vreq); case BYTESTREAM_REDIRECT: /* * If the Individual requested is a FileBytestream, redirect @@ -116,10 +117,10 @@ public class IndividualController extends FreemarkerHttpServlet { new IndividualRequestAnalysisContextImpl(vreq)).analyze(); } - private ResponseValues doNotFound() { + private ResponseValues doNotFound(VitroRequest vreq) { Map body = new HashMap(); - body.put("title", "Individual Not Found"); - body.put("errorMessage", "The individual was not found in the system."); + body.put("title", I18n.text(vreq, "individual_not_found")); + body.put("errorMessage", I18n.text(vreq, "individual_not_found_msg")); return new TemplateResponseValues(TEMPLATE_HELP, body, HttpServletResponse.SC_NOT_FOUND); diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/individual/IndividualRdfAssembler.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/individual/IndividualRdfAssembler.java index b2df86aa5..e55e8a7d1 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/individual/IndividualRdfAssembler.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/individual/IndividualRdfAssembler.java @@ -10,7 +10,7 @@ import java.util.Set; import javax.servlet.ServletContext; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/individuallist/IndividualListResultsUtils.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/individuallist/IndividualListResultsUtils.java index e06f35af9..bb4c333a5 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/individuallist/IndividualListResultsUtils.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/individuallist/IndividualListResultsUtils.java @@ -8,7 +8,7 @@ import java.util.ArrayList; import java.util.List; import java.util.Map; -import org.apache.commons.lang.ArrayUtils; +import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.json.JSONArray; diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/jena/JenaCsv2RdfController.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/jena/JenaCsv2RdfController.java index f15808f24..20ad69c7d 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/jena/JenaCsv2RdfController.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/jena/JenaCsv2RdfController.java @@ -141,7 +141,7 @@ public class JenaCsv2RdfController extends JenaIngestController { } public Model doExecuteCsv2Rdf(VitroRequest vreq, FileItem fileStream, String filePath) throws Exception { - char[] quoteChars = {'"'}; + char quoteChar = '"'; String namespace = ""; String tboxNamespace = vreq.getParameter("tboxNamespace"); String typeName = vreq.getParameter("typeName"); @@ -162,7 +162,7 @@ public class JenaCsv2RdfController extends JenaIngestController { separatorChar = '\t'; } - Csv2Rdf c2r = new Csv2Rdf(separatorChar, quoteChars,namespace,tboxNamespace,typeName); + Csv2Rdf c2r = new Csv2Rdf(separatorChar, quoteChar,namespace,tboxNamespace,typeName); InputStream is = null; diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/jena/JenaIngestController.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/jena/JenaIngestController.java index 81e36b0e4..e80a29835 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/jena/JenaIngestController.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/jena/JenaIngestController.java @@ -33,7 +33,7 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import edu.cornell.mannlib.vitro.webapp.utils.JSPPageHandler; -import org.apache.commons.lang.StringEscapeUtils; +import org.apache.commons.lang3.StringEscapeUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.semanticweb.owlapi.reasoner.InconsistentOntologyException; @@ -73,12 +73,12 @@ import edu.cornell.mannlib.vitro.webapp.rdfservice.ChangeSet; import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFService; import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFServiceException; import edu.cornell.mannlib.vitro.webapp.rdfservice.adapters.VitroModelFactory; -import edu.cornell.mannlib.vitro.webapp.utils.SparqlQueryUtils; import edu.cornell.mannlib.vitro.webapp.utils.jena.JenaIngestUtils; import edu.cornell.mannlib.vitro.webapp.utils.jena.JenaIngestUtils.MergeResult; import edu.cornell.mannlib.vitro.webapp.utils.jena.JenaIngestWorkflowProcessor; import edu.cornell.mannlib.vitro.webapp.utils.jena.JenaOutputUtils; import edu.cornell.mannlib.vitro.webapp.utils.jena.WorkflowOntology; +import edu.cornell.mannlib.vitro.webapp.utils.sparql.SparqlQueryUtils; public class JenaIngestController extends BaseEditController { private static final Log log = LogFactory.getLog(JenaIngestController.class); @@ -861,7 +861,7 @@ public class JenaIngestController extends BaseEditController { Individual ind = jenaOntModel.getIndividual(savedQueryURIStr); log.debug("Using query "+savedQueryURIStr); queryStr = ( (Literal) ind.getPropertyValue(queryStrProp)).getLexicalForm(); - queryStr = StringEscapeUtils.unescapeHtml(queryStr); // !!! We need to turn off automatic HTML-escaping for data property editing. + queryStr = StringEscapeUtils.UNESCAPE_HTML4.translate(queryStr); // !!! We need to turn off automatic HTML-escaping for data property editing. } finally { jenaOntModel.leaveCriticalSection(); } diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/json/JsonProducer.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/json/JsonProducer.java index a1a5e2c0d..3cf430120 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/json/JsonProducer.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/json/JsonProducer.java @@ -7,7 +7,7 @@ import java.util.List; import javax.servlet.ServletException; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.json.JSONArray; diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/json/JsonServlet.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/json/JsonServlet.java index c479f5a09..a1c9f27e3 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/json/JsonServlet.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/json/JsonServlet.java @@ -109,7 +109,7 @@ public class JsonServlet extends VitroHttpServlet { vclassURIs, page, INDIVIDUALS_PER_PAGE, alpha, - vreq.getWebappDaoFactory().getIndividualDao()); + vreq); } catch(Exception ex) { log.error("Error in retrieval of search results for VClass " + vclassURIs.toString(), ex); return IndividualListResults.EMPTY; @@ -144,7 +144,7 @@ public class JsonServlet extends VitroHttpServlet { vclassURI, page, pageSize, - vreq.getWebappDaoFactory().getIndividualDao()); + vreq); } catch(Exception ex) { log.error("Error in retrieval of search results for VClass " + vclassURI, ex); return IndividualListResults.EMPTY; diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/dao/VitroVocabulary.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/dao/VitroVocabulary.java index 1b9054933..0234a8913 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/dao/VitroVocabulary.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/dao/VitroVocabulary.java @@ -33,7 +33,7 @@ public class VitroVocabulary { public static final String OWL_ONTOLOGY = OWL+"Ontology"; public static final String OWL_THING = OWL+"Thing"; - public static final String AFN = "http://jena.hpl.hp.com/ARQ/function#"; + public static final String AFN = "http://jena.apache.org/ARQ/function#"; public static final String label = vitroURI + "label"; diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/dao/jena/DataPropertyDaoJena.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/dao/jena/DataPropertyDaoJena.java index ddf1396a7..a34c6bf41 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/dao/jena/DataPropertyDaoJena.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/dao/jena/DataPropertyDaoJena.java @@ -13,7 +13,7 @@ import java.util.List; import java.util.Map; import edu.cornell.mannlib.vitro.webapp.rdfservice.ResultSetConsumer; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/dao/jena/FauxPropertyDaoJena.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/dao/jena/FauxPropertyDaoJena.java index 61a3ac6db..87b52830c 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/dao/jena/FauxPropertyDaoJena.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/dao/jena/FauxPropertyDaoJena.java @@ -2,9 +2,9 @@ package edu.cornell.mannlib.vitro.webapp.dao.jena; +import static edu.cornell.mannlib.vitro.webapp.utils.sparqlrunner.SparqlQueryRunner.createSelectQueryContext; +import static edu.cornell.mannlib.vitro.webapp.utils.sparqlrunner.SparqlQueryRunner.queryHolder; import static org.apache.jena.rdf.model.ResourceFactory.createResource; -import static edu.cornell.mannlib.vitro.webapp.utils.SparqlQueryRunner.bindValues; -import static edu.cornell.mannlib.vitro.webapp.utils.SparqlQueryRunner.uriValue; import java.util.ArrayList; import java.util.Collection; @@ -16,7 +16,6 @@ import java.util.Set; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; - import org.apache.jena.ontology.ObjectProperty; import org.apache.jena.ontology.OntModel; import org.apache.jena.ontology.OntModelSpec; @@ -33,11 +32,11 @@ import edu.cornell.mannlib.vitro.webapp.beans.FauxProperty; import edu.cornell.mannlib.vitro.webapp.dao.FauxPropertyDao; import edu.cornell.mannlib.vitro.webapp.dao.InsertException; import edu.cornell.mannlib.vitro.webapp.dao.VitroVocabulary; -import edu.cornell.mannlib.vitro.webapp.utils.SparqlQueryRunner; -import edu.cornell.mannlib.vitro.webapp.utils.SparqlQueryRunner.QueryParser; import edu.cornell.mannlib.vitro.webapp.utils.jena.criticalsection.LockableOntModel; import edu.cornell.mannlib.vitro.webapp.utils.jena.criticalsection.LockableOntModelSelector; import edu.cornell.mannlib.vitro.webapp.utils.jena.criticalsection.LockedOntModel; +import edu.cornell.mannlib.vitro.webapp.utils.sparqlrunner.QueryHolder; +import edu.cornell.mannlib.vitro.webapp.utils.sparqlrunner.ResultSetParser; /** * TODO @@ -522,7 +521,7 @@ public class FauxPropertyDaoJena extends JenaBaseDao implements FauxPropertyDao + "} \n"; // private static class ParserLocateConfigContext extends - QueryParser> { + ResultSetParser> { private final String domainUri; private final String baseUri; private final String rangeUri; @@ -561,30 +560,29 @@ public class FauxPropertyDaoJena extends JenaBaseDao implements FauxPropertyDao LockableOntModel lockableDisplayModel, String domainUri, String baseUri, String rangeUri) { try (LockedOntModel displayModel = lockableDisplayModel.read()) { - String queryString; + QueryHolder qHolder; if (domainUri == null || domainUri.trim().isEmpty() || domainUri.equals(OWL.Thing.getURI())) { - queryString = bindValues( - QUERY_LOCATE_CONFIG_CONTEXT_WITH_NO_DOMAIN, - uriValue("baseUri", baseUri), - uriValue("rangeUri", rangeUri)); + qHolder = queryHolder( + QUERY_LOCATE_CONFIG_CONTEXT_WITH_NO_DOMAIN) + .bindToUri("baseUri", baseUri).bindToUri( + "rangeUri", rangeUri); } else { - queryString = bindValues( - QUERY_LOCATE_CONFIG_CONTEXT_WITH_DOMAIN, - uriValue("baseUri", baseUri), - uriValue("rangeUri", rangeUri), - uriValue("domainUri", domainUri)); + qHolder = queryHolder( + QUERY_LOCATE_CONFIG_CONTEXT_WITH_DOMAIN) + .bindToUri("baseUri", baseUri) + .bindToUri("rangeUri", rangeUri) + .bindToUri("domainUri", domainUri); } if (log.isDebugEnabled()) { log.debug("domainUri=" + domainUri + ", baseUri=" + baseUri - + ", rangeUri=" + rangeUri + ", queryString=" - + queryString); + + ", rangeUri=" + rangeUri + ", qHolder=" + qHolder); } ParserLocateConfigContext parser = new ParserLocateConfigContext( domainUri, baseUri, rangeUri); - Set contexts = new SparqlQueryRunner( - displayModel).executeSelect(parser, queryString); + Set contexts = createSelectQueryContext( + displayModel, qHolder).execute().parse(parser); log.debug("found " + contexts.size() + " contexts: " + contexts); return contexts; diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/dao/jena/IndividualDaoJena.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/dao/jena/IndividualDaoJena.java index 9651f308f..bb5fa217e 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/dao/jena/IndividualDaoJena.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/dao/jena/IndividualDaoJena.java @@ -13,7 +13,7 @@ import java.util.List; import java.util.Random; import java.util.Set; -import org.apache.commons.lang.NotImplementedException; +import org.apache.commons.lang3.NotImplementedException; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -605,12 +605,12 @@ public class IndividualDaoJena extends JenaBaseDao implements IndividualDao { public Collection getAllIndividualUris() { //this is implemented in IndivdiualSDB - throw new NotImplementedException(); + throw new NotImplementedException(""); } public Iterator getUpdatedSinceIterator(long updatedSince){ //this is implemented in IndivdiualSDB - throw new NotImplementedException(); + throw new NotImplementedException(""); } public boolean isIndividualOfClass(String vclassURI, String indURI) { diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/dao/jena/IndividualDaoSDB.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/dao/jena/IndividualDaoSDB.java index 0abe07421..94e816891 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/dao/jena/IndividualDaoSDB.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/dao/jena/IndividualDaoSDB.java @@ -13,7 +13,7 @@ import java.util.LinkedList; import java.util.List; import edu.cornell.mannlib.vitro.webapp.rdfservice.ResultSetConsumer; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.joda.time.DateTime; diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/dao/jena/JenaBaseDao.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/dao/jena/JenaBaseDao.java index 74a224829..cb7214c8c 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/dao/jena/JenaBaseDao.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/dao/jena/JenaBaseDao.java @@ -868,6 +868,28 @@ public class JenaBaseDao extends JenaBaseDaoCon { return label; } + /** + * Searches for literal in preferred language. + * @param labels + * the literals to search; must not be null + * @return the literal in preferred language if its containing in given list; + * otherwise the first entry will returned; returns null if an empty list was given + */ + protected Literal tryLiteralForPreferredLanguages(List labels) { + + // search for literal of preferred language + for (Literal literal : labels) { + for (String lang : PREFERRED_LANGUAGES) { + if (lang.equals(literal.getLanguage())) { + return literal; + } + } + } + + // return first literal as last resort + return 0 == labels.size() ? null : labels.get(0); + } + private Literal tryPropertyForPreferredLanguages( OntResource r, Property p, boolean alsoTryNoLang ) { Literal label = null; List labels = r.listPropertyValues(p).toList(); @@ -1149,7 +1171,7 @@ public class JenaBaseDao extends JenaBaseDaoCon { String describeQueryStr = "DESCRIBE <" + res.getURI() + ">" ; -// ? "PREFIX afn: \n\n" + +// ? "PREFIX afn: \n\n" + // "DESCRIBE ?bnode \n" + // "WHERE { \n" + // " FILTER(afn:bnode(?bnode) = \"" + res.getId().toString() + "\")\n" + diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/dao/jena/JenaModelUtils.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/dao/jena/JenaModelUtils.java index ca1aac69a..cc933e02f 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/dao/jena/JenaModelUtils.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/dao/jena/JenaModelUtils.java @@ -262,7 +262,7 @@ public class JenaModelUtils { dataset.getLock().enterCriticalSection(Lock.READ); try { StringBuffer buff = new StringBuffer(); - buff.append("PREFIX afn: \n") + buff.append("PREFIX afn: \n") .append("CONSTRUCT { \n") .append(" ?res <" + property.getURI() + "> ?o } WHERE { \n"); if (graphURI != null) { @@ -294,7 +294,7 @@ public class JenaModelUtils { StringBuffer describeQueryStrBuff = new StringBuffer() .append("PREFIX rdf: \n") - .append("PREFIX afn: \n") + .append("PREFIX afn: \n") .append("DESCRIBE ?res WHERE { \n"); if (graphURI != null) { describeQueryStrBuff diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/dao/jena/ObjectPropertyDaoJena.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/dao/jena/ObjectPropertyDaoJena.java index 97b68557f..8e532f580 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/dao/jena/ObjectPropertyDaoJena.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/dao/jena/ObjectPropertyDaoJena.java @@ -11,7 +11,7 @@ import java.util.List; import java.util.Map; import edu.cornell.mannlib.vitro.webapp.rdfservice.ResultSetConsumer; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/dao/jena/ObjectPropertyStatementDaoJena.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/dao/jena/ObjectPropertyStatementDaoJena.java index 43c43699c..3e75e5687 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/dao/jena/ObjectPropertyStatementDaoJena.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/dao/jena/ObjectPropertyStatementDaoJena.java @@ -7,16 +7,19 @@ import java.util.Collections; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; +import java.util.Locale; import java.util.Map; +import java.util.Map.Entry; import java.util.Objects; import java.util.Set; import edu.cornell.mannlib.vitro.webapp.rdfservice.ResultSetConsumer; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.jena.ontology.OntModel; +import org.apache.jena.ontology.OntResource; import org.apache.jena.query.Dataset; import org.apache.jena.query.Query; import org.apache.jena.query.QueryExecution; @@ -26,6 +29,7 @@ import org.apache.jena.query.QuerySolution; import org.apache.jena.query.QuerySolutionMap; import org.apache.jena.query.ResultSet; import org.apache.jena.query.Syntax; +import org.apache.jena.rdf.model.Literal; import org.apache.jena.rdf.model.Model; import org.apache.jena.rdf.model.ModelFactory; import org.apache.jena.rdf.model.Property; @@ -503,7 +507,8 @@ public class ObjectPropertyStatementDaoJena extends JenaBaseDao implements Objec return Collections.emptyMap(); } - Map types = new LinkedHashMap(); + Map result = new LinkedHashMap(); + Map> types = new LinkedHashMap>(); DatasetWrapper w = dwf.getDatasetWrapper(); Dataset dataset = w.getDataset(); dataset.getLock().enterCriticalSection(Lock.READ); @@ -521,16 +526,24 @@ public class ObjectPropertyStatementDaoJena extends JenaBaseDao implements Objec } RDFNode labelNode = soln.get("label"); - String label = null; - if (labelNode.isLiteral()) { - label = labelNode.asLiteral().getLexicalForm(); - } - - if (StringUtils.isNotBlank(type) && StringUtils.isNotBlank(label)) { - types.put(type, label); + if (StringUtils.isNotBlank(type) && labelNode.isLiteral()) { + + List langLabels = types.get(type); + if (null == langLabels) { + types.put(type, langLabels = new ArrayList()); + } + langLabels.add(labelNode.asLiteral()); + } } - return types; + + // choose labels corresponding to preferred languages + Set>> typeEntries = types.entrySet(); + for (Entry> current : typeEntries) { + result.put(current.getKey(), tryLiteralForPreferredLanguages(current.getValue()).getLexicalForm()); + } + + return result; } catch (Exception e) { log.error("Error getting most specific types for subject " + subjectUri); diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/dao/jena/QueryUtils.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/dao/jena/QueryUtils.java index 9afe1c049..4ca95de30 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/dao/jena/QueryUtils.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/dao/jena/QueryUtils.java @@ -9,8 +9,8 @@ import java.util.Iterator; import java.util.List; import java.util.Map; -import org.apache.commons.lang.ObjectUtils; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.ObjectUtils; +import org.apache.commons.lang3.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/dao/jena/RDFServiceGraph.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/dao/jena/RDFServiceGraph.java index 651a4e368..e591f4fa2 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/dao/jena/RDFServiceGraph.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/dao/jena/RDFServiceGraph.java @@ -7,6 +7,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; import java.util.List; +import java.util.function.Supplier; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -524,6 +525,16 @@ public class RDFServiceGraph implements GraphWithPerform { return null; } + @Override + public void execute(Runnable runnable) { + + } + + @Override + public T calculate(Supplier supplier) { + return null; + } + @Override public boolean transactionsSupported() { return true; diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/dao/jena/SparqlGraphMultilingual.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/dao/jena/SparqlGraphMultilingual.java index ac1a8c71b..b05c3faec 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/dao/jena/SparqlGraphMultilingual.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/dao/jena/SparqlGraphMultilingual.java @@ -8,7 +8,7 @@ import java.util.Comparator; import java.util.LinkedList; import java.util.List; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/dao/jena/VitroModelSource.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/dao/jena/VitroModelSource.java index cd333df4e..cac045b14 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/dao/jena/VitroModelSource.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/dao/jena/VitroModelSource.java @@ -3,7 +3,7 @@ package edu.cornell.mannlib.vitro.webapp.dao.jena; import javax.servlet.ServletContext; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.apache.jena.rdf.model.Model; import org.apache.jena.rdf.model.ModelReader; diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/edit/n3editing/VTwo/EditConfigurationVTwo.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/edit/n3editing/VTwo/EditConfigurationVTwo.java index 536b531c0..e49805e6f 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/edit/n3editing/VTwo/EditConfigurationVTwo.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/edit/n3editing/VTwo/EditConfigurationVTwo.java @@ -16,8 +16,8 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpSession; import org.apache.commons.fileupload.servlet.ServletFileUpload; -import org.apache.commons.lang.builder.ToStringBuilder; -import org.apache.commons.lang.builder.ToStringStyle; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.joda.time.DateTime; diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/edit/n3editing/VTwo/EditN3GeneratorVTwo.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/edit/n3editing/VTwo/EditN3GeneratorVTwo.java index 370268905..7b1803f4f 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/edit/n3editing/VTwo/EditN3GeneratorVTwo.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/edit/n3editing/VTwo/EditN3GeneratorVTwo.java @@ -11,7 +11,7 @@ import java.util.Map; import java.util.Set; import java.util.regex.Matcher; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -68,7 +68,7 @@ public class EditN3GeneratorVTwo { } log.debug("The original value String is " + values.toString()); - String valueString = org.apache.commons.lang.StringUtils.join(values, + String valueString = org.apache.commons.lang3.StringUtils.join(values, ">, <"); valueString = "<" + valueString + ">"; log.debug("The multiUri value String is " + valueString); @@ -233,7 +233,7 @@ public class EditN3GeneratorVTwo { log.debug("value of literal for " + var + " was null"); } } - String valueString = org.apache.commons.lang.StringUtils.join(n3Values, ","); + String valueString = org.apache.commons.lang3.StringUtils.join(n3Values, ","); //Substitute it in to n3 String varRegex = "\\?" + var + "(?=\\p{Punct}|\\p{Space}|$)"; diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/edit/n3editing/VTwo/MultiValueEditSubmission.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/edit/n3editing/VTwo/MultiValueEditSubmission.java index 4c313bc46..9d978ec89 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/edit/n3editing/VTwo/MultiValueEditSubmission.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/edit/n3editing/VTwo/MultiValueEditSubmission.java @@ -11,9 +11,9 @@ import java.util.List; import java.util.Map; import org.apache.commons.fileupload.FileItem; -import org.apache.commons.lang.StringUtils; -import org.apache.commons.lang.builder.ToStringBuilder; -import org.apache.commons.lang.builder.ToStringStyle; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.joda.time.format.DateTimeFormat; diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/edit/n3editing/VTwo/fields/ChildVClassesWithParent.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/edit/n3editing/VTwo/fields/ChildVClassesWithParent.java index f19eb0b09..f129b2541 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/edit/n3editing/VTwo/fields/ChildVClassesWithParent.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/edit/n3editing/VTwo/fields/ChildVClassesWithParent.java @@ -7,7 +7,7 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Map; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.apache.jena.vocabulary.OWL; diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/edit/n3editing/VTwo/fields/FieldVTwo.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/edit/n3editing/VTwo/fields/FieldVTwo.java index d12551420..4f0be8bd3 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/edit/n3editing/VTwo/fields/FieldVTwo.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/edit/n3editing/VTwo/fields/FieldVTwo.java @@ -5,8 +5,8 @@ package edu.cornell.mannlib.vitro.webapp.edit.n3editing.VTwo.fields; import java.util.ArrayList; import java.util.List; -import org.apache.commons.lang.builder.ToStringBuilder; -import org.apache.commons.lang.builder.ToStringStyle; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; import edu.cornell.mannlib.vitro.webapp.edit.n3editing.VTwo.BaseEditElementVTwo; import edu.cornell.mannlib.vitro.webapp.edit.n3editing.VTwo.EditElementVTwo; diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/edit/n3editing/VTwo/fields/IndividualsViaObjectPropetyOptions.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/edit/n3editing/VTwo/fields/IndividualsViaObjectPropetyOptions.java index dbbe5d630..9dcba93ec 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/edit/n3editing/VTwo/fields/IndividualsViaObjectPropetyOptions.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/edit/n3editing/VTwo/fields/IndividualsViaObjectPropetyOptions.java @@ -14,7 +14,7 @@ import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/edit/n3editing/configuration/generators/BaseEditConfigurationGenerator.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/edit/n3editing/configuration/generators/BaseEditConfigurationGenerator.java index 9f0d691b1..b3917921c 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/edit/n3editing/configuration/generators/BaseEditConfigurationGenerator.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/edit/n3editing/configuration/generators/BaseEditConfigurationGenerator.java @@ -6,7 +6,7 @@ import java.util.List; import javax.servlet.http.HttpSession; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.apache.jena.ontology.OntModel; diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/edit/n3editing/configuration/generators/DefaultAddMissingIndividualFormGenerator.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/edit/n3editing/configuration/generators/DefaultAddMissingIndividualFormGenerator.java index 1218ee7a6..e53382bd1 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/edit/n3editing/configuration/generators/DefaultAddMissingIndividualFormGenerator.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/edit/n3editing/configuration/generators/DefaultAddMissingIndividualFormGenerator.java @@ -11,7 +11,7 @@ import java.util.Map; import javax.servlet.http.HttpSession; import edu.cornell.mannlib.vitro.webapp.web.templatemodels.searchresult.IndividualSearchResult; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -226,7 +226,7 @@ public class DefaultAddMissingIndividualFormGenerator implements EditConfigurati return prefixes; } - private String getN3ForName() { + protected String getN3ForName() { return "?" + objectVarName + " rdfs:label ?name ."; } diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/edit/n3editing/configuration/validators/AntiXssValidation.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/edit/n3editing/configuration/validators/AntiXssValidation.java index 854235984..29db68047 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/edit/n3editing/configuration/validators/AntiXssValidation.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/edit/n3editing/configuration/validators/AntiXssValidation.java @@ -7,7 +7,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.owasp.validator.html.AntiSamy; import org.owasp.validator.html.CleanResults; import org.owasp.validator.html.PolicyException; diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/edit/n3editing/configuration/validators/DefaultDataPropertyFormValidator.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/edit/n3editing/configuration/validators/DefaultDataPropertyFormValidator.java index 665426cdc..a6d7125ac 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/edit/n3editing/configuration/validators/DefaultDataPropertyFormValidator.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/edit/n3editing/configuration/validators/DefaultDataPropertyFormValidator.java @@ -6,7 +6,7 @@ import java.util.List; import java.util.Map; import java.util.regex.Pattern; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/edit/n3editing/controller/ProcessRdfFormController.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/edit/n3editing/controller/ProcessRdfFormController.java index 7ba226297..c73519c30 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/edit/n3editing/controller/ProcessRdfFormController.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/edit/n3editing/controller/ProcessRdfFormController.java @@ -8,7 +8,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/filters/ModelSwitcher.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/filters/ModelSwitcher.java index a8638e2e1..4ff84d94c 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/filters/ModelSwitcher.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/filters/ModelSwitcher.java @@ -15,7 +15,7 @@ import static edu.cornell.mannlib.vitro.webapp.modelaccess.ModelNames.DISPLAY_TB import javax.servlet.ServletContext; import javax.servlet.http.HttpServletRequest; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/filters/StartupStatusDisplayFilter.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/filters/StartupStatusDisplayFilter.java index f618c9e24..d1f127f27 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/filters/StartupStatusDisplayFilter.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/filters/StartupStatusDisplayFilter.java @@ -18,7 +18,7 @@ import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import edu.cornell.mannlib.vitro.webapp.beans.ApplicationBean; import edu.cornell.mannlib.vitro.webapp.dao.WebappDaoFactory; diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/filters/VitroURL.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/filters/VitroURL.java index ba5b2d03f..37dc6b828 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/filters/VitroURL.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/filters/VitroURL.java @@ -12,7 +12,7 @@ import java.util.List; import java.util.regex.Pattern; import java.util.regex.Matcher; -import org.apache.commons.lang.StringEscapeUtils; +import org.apache.commons.lang3.StringEscapeUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/freemarker/config/FreemarkerConfiguration.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/freemarker/config/FreemarkerConfiguration.java index 3fc8e3c83..0658d9aef 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/freemarker/config/FreemarkerConfiguration.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/freemarker/config/FreemarkerConfiguration.java @@ -13,7 +13,7 @@ import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; import javax.servlet.http.HttpServletRequest; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/freemarker/loader/FreemarkerTemplateLoader.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/freemarker/loader/FreemarkerTemplateLoader.java index 1dd73816a..1b784ed7f 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/freemarker/loader/FreemarkerTemplateLoader.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/freemarker/loader/FreemarkerTemplateLoader.java @@ -18,7 +18,7 @@ import java.util.TreeSet; import java.util.regex.Matcher; import java.util.regex.Pattern; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/i18n/selection/LocaleSelectionController.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/i18n/selection/LocaleSelectionController.java index f57139fb3..e90034f08 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/i18n/selection/LocaleSelectionController.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/i18n/selection/LocaleSelectionController.java @@ -11,8 +11,8 @@ import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -import org.apache.commons.lang.LocaleUtils; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.LocaleUtils; +import org.apache.commons.lang3.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/i18n/selection/LocaleSelectionSetup.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/i18n/selection/LocaleSelectionSetup.java index 081019a04..9687cdd75 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/i18n/selection/LocaleSelectionSetup.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/i18n/selection/LocaleSelectionSetup.java @@ -10,8 +10,8 @@ import javax.servlet.ServletContext; import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; -import org.apache.commons.lang.LocaleUtils; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.LocaleUtils; +import org.apache.commons.lang3.StringUtils; import edu.cornell.mannlib.vitro.webapp.config.ConfigurationProperties; import edu.cornell.mannlib.vitro.webapp.startup.StartupStatus; @@ -132,6 +132,12 @@ public class LocaleSelectionSetup implements ServletContextListener { private Locale buildLocale(String localeString) throws IllegalArgumentException { + + // Replicate exception from lang2 with empty strings + if ("".equals(localeString)) { + throw new IllegalArgumentException("Invalid locale format"); + } + Locale locale = LocaleUtils.toLocale(localeString); if (!"es_GO".equals(localeString) && // No complaint about bogus locale diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/imageprocessor/imageio/IIOImageProcessor.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/imageprocessor/imageio/IIOImageProcessor.java index 7add5de92..f7ddc725e 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/imageprocessor/imageio/IIOImageProcessor.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/imageprocessor/imageio/IIOImageProcessor.java @@ -2,7 +2,6 @@ package edu.cornell.mannlib.vitro.webapp.imageprocessor.imageio; -import com.sun.media.jai.codec.MemoryCacheSeekableStream; import edu.cornell.mannlib.vitro.webapp.modules.Application; import edu.cornell.mannlib.vitro.webapp.modules.ComponentStartupStatus; import edu.cornell.mannlib.vitro.webapp.modules.imageProcessor.ImageProcessor; @@ -10,11 +9,8 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import javax.imageio.ImageIO; -import javax.media.jai.JAI; -import javax.media.jai.RenderedOp; -import javax.media.jai.operator.BandSelectDescriptor; -import javax.media.jai.operator.StreamDescriptor; -import javax.media.jai.util.ImagingListener; +import javax.imageio.stream.ImageInputStream; +import javax.imageio.stream.MemoryCacheImageInputStream; import java.awt.geom.AffineTransform; import java.awt.image.AffineTransformOp; import java.awt.image.BufferedImage; @@ -55,8 +51,8 @@ public class IIOImageProcessor implements ImageProcessor { */ @Override public void startup(Application application, ComponentStartupStatus ss) { - JAI.getDefaultInstance().setImagingListener( - new NonNoisyImagingListener()); +// JAI.getDefaultInstance().setImagingListener( +// new NonNoisyImagingListener()); } @Override @@ -66,7 +62,7 @@ public class IIOImageProcessor implements ImageProcessor { @Override public Dimensions getDimensions(InputStream imageStream) throws ImageProcessorException, IOException { - MemoryCacheSeekableStream stream = new MemoryCacheSeekableStream(imageStream); + ImageInputStream stream = new MemoryCacheImageInputStream(imageStream); BufferedImage image = ImageIO.read(stream); return new Dimensions(image.getWidth(), image.getHeight()); } @@ -80,7 +76,7 @@ public class IIOImageProcessor implements ImageProcessor { CropRectangle crop, Dimensions limits) throws ImageProcessorException, IOException { try { - MemoryCacheSeekableStream stream = new MemoryCacheSeekableStream(mainImageStream); + ImageInputStream stream = new MemoryCacheImageInputStream(mainImageStream); BufferedImage mainImage = ImageIO.read(stream); BufferedImage bufferedImage = new BufferedImage(mainImage.getWidth(), mainImage.getHeight(), BufferedImage.TYPE_3BYTE_BGR); // BufferedImage.TYPE_INT_RGB @@ -176,33 +172,4 @@ public class IIOImageProcessor implements ImageProcessor { ImageIO.write(image, "JPG", bytes); return bytes.toByteArray(); } - - /** - * This ImagingListener means that Java Advanced Imaging won't dump an - * exception log to System.out. It writes to the log, instead. - * - * Further, since the lack of native accelerator classes isn't an error, it - * is written as a simple log message. - */ - static class NonNoisyImagingListener implements ImagingListener { - @Override - public boolean errorOccurred(String message, Throwable thrown, - Object where, boolean isRetryable) throws RuntimeException { - if (thrown instanceof RuntimeException) { - throw (RuntimeException) thrown; - } - if ((thrown instanceof NoClassDefFoundError) - && (thrown.getMessage() - .contains("com/sun/medialib/mlib/Image"))) { - log.info("Java Advanced Imaging: Could not find mediaLib " - + "accelerator wrapper classes. " - + "Continuing in pure Java mode."); - return false; - } - log.error(thrown, thrown); - return false; - } - - } - } diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/imageprocessor/jai/JaiImageProcessor.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/imageprocessor/jai/JaiImageProcessor.java index b5d4a7cae..dc8e8f674 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/imageprocessor/jai/JaiImageProcessor.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/imageprocessor/jai/JaiImageProcessor.java @@ -2,234 +2,44 @@ package edu.cornell.mannlib.vitro.webapp.imageprocessor.jai; -import java.awt.geom.AffineTransform; -import java.awt.image.AffineTransformOp; -import java.awt.image.BufferedImage; -import java.awt.image.ColorConvertOp; -import java.awt.image.ColorModel; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; - -import javax.imageio.ImageIO; -import javax.media.jai.JAI; -import javax.media.jai.RenderedOp; -import javax.media.jai.operator.BandSelectDescriptor; -import javax.media.jai.operator.StreamDescriptor; -import javax.media.jai.util.ImagingListener; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -import com.sun.media.jai.codec.MemoryCacheSeekableStream; - +import edu.cornell.mannlib.vitro.webapp.imageprocessor.imageio.IIOImageProcessor; import edu.cornell.mannlib.vitro.webapp.modules.Application; import edu.cornell.mannlib.vitro.webapp.modules.ComponentStartupStatus; import edu.cornell.mannlib.vitro.webapp.modules.imageProcessor.ImageProcessor; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.io.IOException; +import java.io.InputStream; /** - * Crop the main image as specified, and scale it to the correct size for a - * thumbnail. - * - * Use the JAI library to read the file because the javax.imageio package - * doesn't read extended JPEG properly. Use JAI to remove transparency from - * JPEGs and PNGs, simply by removing the alpha channel. Annoyingly, this will - * not work with GIFs with transparent pixels. - * - * The transforms in the JAI library are buggy, so standard AWT operations do - * the scaling and cropping. The most obvious problem in the JAI library is the - * refusal to crop after scaling an image. - * - * Scale first to avoid the boundary error that produces black lines along the - * edge of the image. - * - * Use the javax.imagio pacakge to write the thumbnail image as a JPEG file. + * Re-implemented as ImageIO / TwelveMonkeys plugin for better OpenJDK compatibility and to remove + * JAI codec dependencies that are not distributed via Maven + * + * This transitional stub extends the new class, and logs warnings for people to update their config */ -public class JaiImageProcessor implements ImageProcessor { +@Deprecated +public class JaiImageProcessor extends IIOImageProcessor { private static final Log log = LogFactory.getLog(JaiImageProcessor.class); - /** If an image has 3 color bands and 1 alpha band, we want these. */ - private static final int[] COLOR_BAND_INDEXES = new int[] { 0, 1, 2 }; - - /** - * Prevent Java Advanced Imaging from complaining about the lack of - * accelerator classes. - */ + @Deprecated @Override public void startup(Application application, ComponentStartupStatus ss) { - JAI.getDefaultInstance().setImagingListener( - new NonNoisyImagingListener()); + log.warn("JaiImageProcessor is deprecated and will be removed - please update config/applicationSetup.n3 to use edu.cornell.mannlib.vitro.webapp.imageprocessor.imageio.IIOImageProcessor"); + super.startup(application, ss); } + @Deprecated @Override - public void shutdown(Application application) { - // Nothing to tear down. + public Dimensions getDimensions(InputStream imageStream) throws ImageProcessorException, IOException { + log.warn("JaiImageProcessor is deprecated and will be removed - please update config/applicationSetup.n3 to use edu.cornell.mannlib.vitro.webapp.imageprocessor.imageio.IIOImageProcessor"); + return super.getDimensions(imageStream); } + @Deprecated @Override - public Dimensions getDimensions(InputStream imageStream) - throws ImageProcessorException, IOException { - MemoryCacheSeekableStream stream = new MemoryCacheSeekableStream( - imageStream); - RenderedOp image = JAI.create("stream", stream); - return new Dimensions(image.getWidth(), image.getHeight()); + public InputStream cropAndScale(InputStream mainImageStream, CropRectangle crop, Dimensions limits) throws ImageProcessorException, IOException { + log.warn("JaiImageProcessor is deprecated and will be removed - please update config/applicationSetup.n3 to use edu.cornell.mannlib.vitro.webapp.imageprocessor.imageio.IIOImageProcessor"); + return super.cropAndScale(mainImageStream, crop, limits); } - - /** - * Crop the main image according to this rectangle, and scale it to the - * correct size for a thumbnail. - */ - @Override - public InputStream cropAndScale(InputStream mainImageStream, - CropRectangle crop, Dimensions limits) - throws ImageProcessorException, IOException { - try { - RenderedOp mainImage = loadImage(mainImageStream); - - BufferedImage bufferedImage = new BufferedImage(mainImage.getWidth(), mainImage.getHeight(), BufferedImage.TYPE_3BYTE_BGR); // BufferedImage.TYPE_INT_RGB - new ColorConvertOp(null).filter(mainImage.getAsBufferedImage(), bufferedImage); - - log.debug("initial image: " + imageSize(bufferedImage)); - - log.debug("initial crop: " + crop); - CropRectangle boundedCrop = limitCropRectangleToImageBounds( - bufferedImage, crop); - log.debug("bounded crop: " + boundedCrop); - - float scaleFactor = figureScaleFactor(boundedCrop, limits); - log.debug("scale factor: " + scaleFactor); - - BufferedImage scaledImage = scaleImage(bufferedImage, scaleFactor); - log.debug("scaled image: " + imageSize(scaledImage)); - - CropRectangle rawScaledCrop = adjustCropRectangleToScaledImage( - boundedCrop, scaleFactor); - log.debug("scaled crop: " + rawScaledCrop); - CropRectangle scaledCrop = limitCropRectangleToImageBounds( - scaledImage, rawScaledCrop); - log.debug("bounded scaled crop: " + scaledCrop); - - BufferedImage croppedImage = cropImage(scaledImage, scaledCrop); - log.debug("cropped image: " + imageSize(croppedImage)); - - byte[] jpegBytes = encodeAsJpeg(croppedImage); - return new ByteArrayInputStream(jpegBytes); - } catch (Exception e) { - throw new IllegalStateException("Failed to scale the image", e); - } - } - - private RenderedOp loadImage(InputStream imageStream) { - return StreamDescriptor.create(new MemoryCacheSeekableStream( - imageStream), null, null); - } - - private RenderedOp makeImageOpaque(RenderedOp image) { - ColorModel colorModel = image.getColorModel(); - - if (!colorModel.hasAlpha()) { - // The image is already opaque. - return image; - } - - if (image.getNumBands() == 4) { - // The image has a separate alpha channel. Drop the alpha channel. - return BandSelectDescriptor.create(image, COLOR_BAND_INDEXES, null); - } - - // Don't know how to handle it. Probably a GIF with a transparent - // background. Give up. - return image; - } - - private String imageSize(BufferedImage image) { - return image.getWidth() + " by " + image.getHeight(); - } - - private CropRectangle limitCropRectangleToImageBounds(BufferedImage image, - CropRectangle crop) { - - int imageWidth = image.getWidth(); - int imageHeight = image.getHeight(); - - // Ensure that x and y are at least zero, but not big enough to push the - // crop rectangle out of the image. - int greatestX = imageWidth - MINIMUM_CROP_SIZE; - int greatestY = imageHeight - MINIMUM_CROP_SIZE; - int x = Math.max(0, Math.min(greatestX, Math.abs(crop.x))); - int y = Math.max(0, Math.min(greatestY, Math.abs(crop.y))); - - // Ensure that width and height are at least as big as the minimum, but - // no so big as to extend beyond the image. - int greatestW = imageWidth - x; - int greatestH = imageHeight - y; - int w = Math.max(MINIMUM_CROP_SIZE, Math.min(greatestW, crop.width)); - int h = Math.max(MINIMUM_CROP_SIZE, Math.min(greatestH, crop.height)); - - return new CropRectangle(x, y, h, w); - } - - private float figureScaleFactor(CropRectangle boundedCrop, Dimensions limits) { - float horizontalScale = ((float) limits.width) - / ((float) boundedCrop.width); - float verticalScale = ((float) limits.height) - / ((float) boundedCrop.height); - return Math.min(horizontalScale, verticalScale); - } - - private BufferedImage scaleImage(BufferedImage image, float scaleFactor) { - AffineTransform transform = AffineTransform.getScaleInstance( - scaleFactor, scaleFactor); - AffineTransformOp atoOp = new AffineTransformOp(transform, null); - return atoOp.filter(image, null); - } - - private CropRectangle adjustCropRectangleToScaledImage(CropRectangle crop, - float scaleFactor) { - int newX = (int) (crop.x * scaleFactor); - int newY = (int) (crop.y * scaleFactor); - int newHeight = (int) (crop.height * scaleFactor); - int newWidth = (int) (crop.width * scaleFactor); - return new CropRectangle(newX, newY, newHeight, newWidth); - } - - private BufferedImage cropImage(BufferedImage image, CropRectangle crop) { - return image.getSubimage(crop.x, crop.y, crop.width, crop.height); - } - - private byte[] encodeAsJpeg(BufferedImage image) throws IOException { - ByteArrayOutputStream bytes = new ByteArrayOutputStream(); - ImageIO.write(image, "JPG", bytes); - return bytes.toByteArray(); - } - - /** - * This ImagingListener means that Java Advanced Imaging won't dump an - * exception log to System.out. It writes to the log, instead. - * - * Further, since the lack of native accelerator classes isn't an error, it - * is written as a simple log message. - */ - static class NonNoisyImagingListener implements ImagingListener { - @Override - public boolean errorOccurred(String message, Throwable thrown, - Object where, boolean isRetryable) throws RuntimeException { - if (thrown instanceof RuntimeException) { - throw (RuntimeException) thrown; - } - if ((thrown instanceof NoClassDefFoundError) - && (thrown.getMessage() - .contains("com/sun/medialib/mlib/Image"))) { - log.info("Java Advanced Imaging: Could not find mediaLib " - + "accelerator wrapper classes. " - + "Continuing in pure Java mode."); - return false; - } - log.error(thrown, thrown); - return false; - } - - } - } diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/ontology/update/OntologyChangeParser.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/ontology/update/OntologyChangeParser.java index fc3327762..40f84722d 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/ontology/update/OntologyChangeParser.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/ontology/update/OntologyChangeParser.java @@ -5,14 +5,16 @@ package edu.cornell.mannlib.vitro.webapp.ontology.update; import java.io.File; import java.io.FileInputStream; import java.io.IOException; +import java.io.InputStreamReader; import java.util.ArrayList; import java.util.List; import java.util.StringTokenizer; +import org.apache.commons.csv.CSVFormat; +import org.apache.commons.csv.CSVParser; +import org.apache.commons.csv.CSVRecord; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import org.skife.csv.CSVReader; -import org.skife.csv.SimpleReader; import edu.cornell.mannlib.vitro.webapp.ontology.update.AtomicOntologyChange.AtomicChangeType; @@ -35,7 +37,6 @@ public class OntologyChangeParser { /** * @param diffPath Diff path - * @throws IOException */ @SuppressWarnings({ "unchecked", "null", "static-access" }) @@ -50,52 +51,53 @@ public class OntologyChangeParser { String destinationURI = null; StringTokenizer stArr = null; FileInputStream in = new FileInputStream(new File(diffPath)); - CSVReader readFile = new SimpleReader(); - readFile.setSeperator('\t'); - - List rows = readFile.parse(in); - - for(int rowNum = 0; rowNum < rows.size(); rowNum++){ - - String[] cols = rows.get(rowNum); - if (cols.length != 5) { - logger.logError("Invalid PromptDiff data at row " + (rowNum + 1) - + ". Expected 5 columns; found " + cols.length ); + + CSVParser readFile = new CSVParser(new InputStreamReader(in), + CSVFormat.DEFAULT.withRecordSeparator('\t')); + + int rowNum = 0; + for (CSVRecord record : readFile) { + rowNum++; + if (record.size() != 5) { + logger.logError("Invalid PromptDiff data at row " + (rowNum) + + ". Expected 5 columns; found " + record.size() ); } else { - + String col = null; changeObj = new AtomicOntologyChange(); - - if (cols[0] != null && cols[0].length() > 0) { - changeObj.setSourceURI(cols[0]); - } - - if (cols[1] != null && cols[1].length() > 0) { - changeObj.setDestinationURI(cols[1]); + + col = record.get(0); + if (col != null && col.length() > 0) { + changeObj.setSourceURI(col); } - if (cols[4] != null && cols[4].length() > 0) { - changeObj.setNotes(cols[4]); + col = record.get(1); + if (col != null && col.length() > 0) { + changeObj.setDestinationURI(col); + } + + col = record.get(4); + if (col != null && col.length() > 0) { + changeObj.setNotes(col); } - if ("Yes".equals(cols[2])) { + if ("Yes".equals(record.get(2))) { changeObj.setAtomicChangeType(AtomicChangeType.RENAME); - } else if ("Delete".equals(cols[3])) { + } else if ("Delete".equals(record.get(3))) { changeObj.setAtomicChangeType(AtomicChangeType.DELETE); - } else if ("Add".equals(cols[3])) { + } else if ("Add".equals(record.get(3))) { changeObj.setAtomicChangeType(AtomicChangeType.ADD); } else { logger.logError("Invalid rename or change type data: '" + - cols[2] + " " + cols[3] + "'"); + record.get(2) + " " + record.get(3) + "'"); } - - + log.debug(changeObj); changeObjects.add(changeObj); - } - } + + readFile.close(); if (changeObjects.size() == 0) { logger.log("No ABox updates are required."); diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/rdfservice/RDFService.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/rdfservice/RDFService.java index c2394035e..d0ac60400 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/rdfservice/RDFService.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/rdfservice/RDFService.java @@ -8,6 +8,7 @@ import java.util.List; import org.apache.jena.rdf.model.Model; import org.apache.jena.rdf.model.ModelChangedListener; +import org.apache.jena.rdf.model.RDFNode; /** * Interface for API to write, read, and update Vitro's RDF store, with support @@ -247,7 +248,11 @@ public interface RDFService { * * @return ChangeSet an empty ChangeSet object */ - public ChangeSet manufactureChangeSet(); + public ChangeSet manufactureChangeSet(); + + public long countTriples(RDFNode subject, RDFNode predicate, RDFNode object) throws RDFServiceException; + + public Model getTriples(RDFNode subject, RDFNode predicate, RDFNode object, long limit, long offset) throws RDFServiceException; /** * Frees any resources held by this RDFService object diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/rdfservice/adapters/AbstractModelDecorator.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/rdfservice/adapters/AbstractModelDecorator.java index e9c19a486..c9d6c384f 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/rdfservice/adapters/AbstractModelDecorator.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/rdfservice/adapters/AbstractModelDecorator.java @@ -10,6 +10,7 @@ import java.util.Calendar; import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.function.Supplier; import org.apache.jena.datatypes.RDFDatatype; import org.apache.jena.graph.Graph; @@ -1078,4 +1079,33 @@ public abstract class AbstractModelDecorator implements Model { return inner.isClosed(); } + @Override + public Statement getRequiredProperty(Resource resource, Property property, String s) { + return inner.getRequiredProperty(resource, property, s); + } + + @Override + public Statement getProperty(Resource resource, Property property, String s) { + return inner.getProperty(resource, property, s); + } + + @Override + public void executeInTxn(Runnable runnable) { + inner.executeInTxn(runnable); + } + + @Override + public T calculateInTxn(Supplier supplier) { + return inner.calculateInTxn(supplier); + } + + @Override + public PrefixMapping clearNsPrefixMap() { + return inner.clearNsPrefixMap(); + } + + @Override + public int numPrefixes() { + return inner.numPrefixes(); + } } diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/rdfservice/adapters/AbstractOntModelDecorator.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/rdfservice/adapters/AbstractOntModelDecorator.java index 653f5372b..6712b9501 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/rdfservice/adapters/AbstractOntModelDecorator.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/rdfservice/adapters/AbstractOntModelDecorator.java @@ -11,6 +11,7 @@ import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.function.Supplier; import org.apache.jena.datatypes.RDFDatatype; import org.apache.jena.graph.Graph; @@ -1775,4 +1776,34 @@ public abstract class AbstractOntModelDecorator implements OntModel { public Model writeAll(OutputStream out, String lang, String base) { return inner.writeAll(out, lang, base); } + + @Override + public Statement getRequiredProperty(Resource resource, Property property, String s) { + return inner.getRequiredProperty(resource, property, s); + } + + @Override + public Statement getProperty(Resource resource, Property property, String s) { + return inner.getProperty(resource, property, s); + } + + @Override + public void executeInTxn(Runnable runnable) { + inner.executeInTxn(runnable); + } + + @Override + public T calculateInTxn(Supplier supplier) { + return inner.calculateInTxn(supplier); + } + + @Override + public PrefixMapping clearNsPrefixMap() { + return inner.clearNsPrefixMap(); + } + + @Override + public int numPrefixes() { + return inner.numPrefixes(); + } } diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/rdfservice/filter/LanguageFilteringRDFService.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/rdfservice/filter/LanguageFilteringRDFService.java index aa3846068..183a4987a 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/rdfservice/filter/LanguageFilteringRDFService.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/rdfservice/filter/LanguageFilteringRDFService.java @@ -468,6 +468,16 @@ public class LanguageFilteringRDFService implements RDFService { return s.manufactureChangeSet(); } + @Override + public long countTriples(RDFNode subject, RDFNode predicate, RDFNode object) throws RDFServiceException { + return s.countTriples(subject, predicate, object); + } + + @Override + public Model getTriples(RDFNode subject, RDFNode predicate, RDFNode object, long limit, long offset) throws RDFServiceException { + return s.getTriples(subject, predicate, object, limit, offset); + } + @Override public void close() { s.close(); diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/rdfservice/impl/RDFServiceFactorySingle.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/rdfservice/impl/RDFServiceFactorySingle.java index 72ae3e208..ffa75e439 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/rdfservice/impl/RDFServiceFactorySingle.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/rdfservice/impl/RDFServiceFactorySingle.java @@ -16,6 +16,7 @@ import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFServiceException; import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFServiceFactory; import edu.cornell.mannlib.vitro.webapp.rdfservice.ResultSetConsumer; import edu.cornell.mannlib.vitro.webapp.utils.logging.ToString; +import org.apache.jena.rdf.model.RDFNode; /** * An RDFServiceFactory that always returns the same RDFService object @@ -192,6 +193,16 @@ public class RDFServiceFactorySingle implements RDFServiceFactory { return s.manufactureChangeSet(); } + @Override + public long countTriples(RDFNode subject, RDFNode predicate, RDFNode object) throws RDFServiceException { + return s.countTriples(subject, predicate, object); + } + + @Override + public Model getTriples(RDFNode subject, RDFNode predicate, RDFNode object, long limit, long offset) throws RDFServiceException { + return s.getTriples(subject, predicate, object, limit, offset); + } + @Override public void close() { // Don't close s. It's being used by everybody. diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/rdfservice/impl/RDFServiceImpl.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/rdfservice/impl/RDFServiceImpl.java index c02cd555d..521bb7285 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/rdfservice/impl/RDFServiceImpl.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/rdfservice/impl/RDFServiceImpl.java @@ -12,18 +12,24 @@ import java.util.concurrent.CopyOnWriteArrayList; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.apache.jena.atlas.io.StringWriterI; import org.apache.jena.graph.Node; import org.apache.jena.graph.NodeFactory; import org.apache.jena.graph.Triple; import org.apache.jena.query.Query; import org.apache.jena.query.QueryFactory; import org.apache.jena.query.QueryParseException; +import org.apache.jena.query.QuerySolution; import org.apache.jena.query.Syntax; +import org.apache.jena.rdf.model.Literal; import org.apache.jena.rdf.model.Model; import org.apache.jena.rdf.model.ModelChangedListener; import org.apache.jena.rdf.model.ModelFactory; +import org.apache.jena.rdf.model.RDFNode; import org.apache.jena.rdf.model.Statement; import org.apache.jena.rdf.model.StmtIterator; +import org.apache.jena.riot.out.NodeFormatter; +import org.apache.jena.riot.out.NodeFormatterTTL; import org.apache.jena.vocabulary.RDF; import edu.cornell.mannlib.vitro.webapp.rdfservice.ChangeListener; @@ -34,6 +40,7 @@ import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFService; import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFServiceException; import edu.cornell.mannlib.vitro.webapp.rdfservice.ResultSetConsumer; import edu.cornell.mannlib.vitro.webapp.utils.logging.ToString; +import org.vivoweb.linkeddatafragments.datasource.rdfservice.RDFServiceBasedRequestProcessorForTPFs; public abstract class RDFServiceImpl implements RDFService { @@ -340,5 +347,121 @@ public abstract class RDFServiceImpl implements RDFService { public String toString() { return ToString.simpleName(this) + "[" + ToString.hashHex(this) + "]"; } - + + @Override + public long countTriples(RDFNode subject, RDFNode predicate, RDFNode object) throws RDFServiceException { + StringBuilder whereClause = new StringBuilder(); + StringBuilder orderBy = new StringBuilder(); + + if ( subject != null ) { + appendNode(whereClause.append(' '), subject); + } else { + whereClause.append(" ?s"); + orderBy.append(" ?s"); + } + + if ( predicate != null ) { + appendNode(whereClause.append(' '), predicate); + } else { + whereClause.append(" ?p"); + orderBy.append(" ?p"); + } + + if ( object != null ) { + appendNode(whereClause.append(' '), object); + } else { + whereClause.append(" ?o"); + orderBy.append(" ?o"); + } + + long estimate = -1; + + StringBuilder count = new StringBuilder(); + count.append("SELECT (COUNT(*) AS ?count) WHERE { "); + count.append(whereClause.toString()); + count.append(" . "); + count.append(" }"); + CountConsumer countConsumer = new CountConsumer(); + this.sparqlSelectQuery(count.toString(), countConsumer); + return countConsumer.count; + } + + @Override + public Model getTriples(RDFNode subject, RDFNode predicate, RDFNode object, long limit, long offset) throws RDFServiceException { + StringBuilder whereClause = new StringBuilder(); + StringBuilder orderBy = new StringBuilder(); + + if ( subject != null ) { + appendNode(whereClause.append(' '), subject); + } else { + whereClause.append(" ?s"); + orderBy.append(" ?s"); + } + + if ( predicate != null ) { + appendNode(whereClause.append(' '), predicate); + } else { + whereClause.append(" ?p"); + orderBy.append(" ?p"); + } + + if ( object != null ) { + appendNode(whereClause.append(' '), object); + } else { + whereClause.append(" ?o"); + orderBy.append(" ?o"); + } + + StringBuilder constructQuery = new StringBuilder(); + + constructQuery.append("CONSTRUCT { "); + constructQuery.append(whereClause.toString()); + constructQuery.append(" } WHERE { "); + constructQuery.append(whereClause.toString()).append(" . "); + constructQuery.append(" }"); + + if (orderBy.length() > 0) { + constructQuery.append(" ORDER BY").append(orderBy.toString()); + } + + if (limit > 0) { + constructQuery.append(" LIMIT ").append(limit); + } + + if (offset > 0) { + constructQuery.append(" OFFSET ").append(offset); + } + + Model triples = ModelFactory.createDefaultModel(); + this.sparqlConstructQuery(constructQuery.toString(), triples); + + return triples; + } + + private void appendNode(StringBuilder builder, RDFNode node) { + if (node.isLiteral()) { + builder.append(literalToString(node.asLiteral())); + } else if (node.isURIResource()) { + builder.append('<' + node.asResource().getURI() + '>'); + } + } + + private String literalToString(Literal l) { + StringWriterI sw = new StringWriterI(); + NodeFormatter fmt = new NodeFormatterTTL(null, null); + fmt.formatLiteral(sw, l.asNode()); + return sw.toString(); + } + + class CountConsumer extends ResultSetConsumer { + public long count = -1; + + @Override + protected void processQuerySolution(QuerySolution qs) { + if (count == -1) { + Literal literal = qs.getLiteral("count"); + count = literal.getLong(); + } + } + } } diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/rdfservice/impl/jena/RDFServiceJena.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/rdfservice/impl/jena/RDFServiceJena.java index 9323652ef..6b448b3aa 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/rdfservice/impl/jena/RDFServiceJena.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/rdfservice/impl/jena/RDFServiceJena.java @@ -16,6 +16,9 @@ import edu.cornell.mannlib.vitro.webapp.rdfservice.ResultSetConsumer; import org.apache.commons.io.IOUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.apache.jena.query.QuerySolutionMap; +import org.apache.jena.query.Syntax; +import org.apache.jena.rdf.model.Literal; import org.apache.jena.riot.RDFDataMgr; import org.apache.log4j.lf5.util.StreamUtils; @@ -612,6 +615,71 @@ public abstract class RDFServiceJena extends RDFServiceImpl implements RDFServic return graph.isIsomorphicWith(fromTripleStoreModel); } + @Override + public long countTriples(RDFNode subject, RDFNode predicate, RDFNode object) throws RDFServiceException { + Query countQuery = QueryFactory.create("SELECT (COUNT(?s) AS ?count) WHERE { ?s ?p ?o } ORDER BY ?s ?p ?o", Syntax.syntaxSPARQL_11); + QuerySolutionMap map = new QuerySolutionMap(); + if ( subject != null ) { + map.add("s", subject); + } + if ( predicate != null ) { + map.add("p", predicate); + } + if ( object != null ) { + map.add("o", object); + } + + DatasetWrapper dw = getDatasetWrapper(); + try { + Dataset d = dw.getDataset(); + try (QueryExecution qexec = QueryExecutionFactory.create(countQuery, d, map)) { + ResultSet results = qexec.execSelect(); + if (results.hasNext()) { + QuerySolution soln = results.nextSolution() ; + Literal literal = soln.getLiteral("count"); + return literal.getLong(); + } + } + } finally { + dw.close(); + } + + return 0; + } + + @Override + public Model getTriples(RDFNode subject, RDFNode predicate, RDFNode object, long limit, long offset) throws RDFServiceException { + Query query = QueryFactory.create("CONSTRUCT WHERE { ?s ?p ?o }", Syntax.syntaxSPARQL_11); + QuerySolutionMap map = new QuerySolutionMap(); + if ( subject != null ) { + map.add("s", subject); + } + if ( predicate != null ) { + map.add("p", predicate); + } + if ( object != null ) { + map.add("o", object); + } + + query.setOffset(offset); + query.setLimit(limit); + + Model triples = ModelFactory.createDefaultModel(); + + DatasetWrapper dw = getDatasetWrapper(); + try { + Dataset d = dw.getDataset(); + try (QueryExecution qexec = QueryExecutionFactory.create(query, d, map)) { + qexec.execConstruct(triples); + } + + return triples; + } finally { + dw.close(); + } + } + + @Override public void close() { // nothing @@ -620,5 +688,4 @@ public abstract class RDFServiceJena extends RDFServiceImpl implements RDFServic protected QueryExecution createQueryExecution(String queryString, Query q, Dataset d) { return QueryExecutionFactory.create(q, d); } - } diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/rdfservice/impl/jena/sdb/RDFServiceSDB.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/rdfservice/impl/jena/sdb/RDFServiceSDB.java index 58d4faf7a..3eb123d8d 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/rdfservice/impl/jena/sdb/RDFServiceSDB.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/rdfservice/impl/jena/sdb/RDFServiceSDB.java @@ -4,20 +4,34 @@ package edu.cornell.mannlib.vitro.webapp.rdfservice.impl.jena.sdb; import java.io.IOException; import java.sql.Connection; +import java.sql.ResultSet; import java.sql.SQLException; +import java.sql.Statement; import javax.sql.DataSource; +import org.apache.commons.lang3.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.apache.jena.datatypes.RDFDatatype; +import org.apache.jena.datatypes.TypeMapper; +import org.apache.jena.datatypes.xsd.XSDDatatype; +import org.apache.jena.graph.Node; +import org.apache.jena.graph.NodeFactory; +import org.apache.jena.graph.Triple; import org.apache.jena.query.Dataset; import org.apache.jena.query.Query; import org.apache.jena.query.QueryExecution; import org.apache.jena.query.QueryExecutionFactory; +import org.apache.jena.rdf.model.Model; +import org.apache.jena.rdf.model.ModelFactory; +import org.apache.jena.rdf.model.RDFNode; import org.apache.jena.sdb.SDBFactory; import org.apache.jena.sdb.Store; import org.apache.jena.sdb.StoreDesc; +import org.apache.jena.sdb.layout2.NodeLayout2; +import org.apache.jena.sdb.layout2.ValueType; import org.apache.jena.sdb.sql.SDBConnection; import edu.cornell.mannlib.vitro.webapp.dao.jena.DatasetWrapper; @@ -28,6 +42,8 @@ import edu.cornell.mannlib.vitro.webapp.rdfservice.ChangeSet; import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFService; import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFServiceException; import edu.cornell.mannlib.vitro.webapp.rdfservice.impl.jena.RDFServiceJena; +import org.apache.jena.sdb.store.DatabaseType; +import org.apache.jena.sdb.store.LayoutType; public class RDFServiceSDB extends RDFServiceJena implements RDFService { @@ -147,8 +163,118 @@ public class RDFServiceSDB extends RDFServiceJena implements RDFService { // This used to execute against the default model if the query included an OPTIONAL // However, in recent Jena this turns out to be much slower than executing against the dataset directly } - - @Override + + @Override + public long countTriples(RDFNode subject, RDFNode predicate, RDFNode object) throws RDFServiceException { + if (LayoutType.LayoutTripleNodesHash.equals(storeDesc.getLayout())) { + if (DatabaseType.MySQL.equals(storeDesc.getDbType()) || + DatabaseType.PostgreSQL.equals(storeDesc.getDbType())) { + SDBConnection sdbConn = getSDBConnection(); + try { + String whereClause = makeWhereClause(subject, predicate, object); + Statement stmt = sdbConn.getSqlConnection().createStatement(); + ResultSet rs = stmt.executeQuery("SELECT count(DISTINCT s,p,o) AS tcount FROM Quads" + (StringUtils.isEmpty(whereClause) ? "" : " WHERE " + whereClause)); + try { + while (rs.next()) { + return rs.getLong("tcount"); + } + } finally { + rs.close(); + } + } catch (SQLException sqle) { + throw new RDFServiceException("Unable to retrieve triples", sqle); + } finally { + close(sdbConn); + } + } + } else { + return super.countTriples(subject, predicate, object); + } + + return super.countTriples(subject, predicate, object); + } + + @Override + public Model getTriples(RDFNode subject, RDFNode predicate, RDFNode object, long limit, long offset) throws RDFServiceException { + if (LayoutType.LayoutTripleNodesHash.equals(storeDesc.getLayout())) { + if (DatabaseType.MySQL.equals(storeDesc.getDbType()) || + DatabaseType.PostgreSQL.equals(storeDesc.getDbType())) { + Model triples = ModelFactory.createDefaultModel(); + + SDBConnection sdbConn = getSDBConnection(); + try { + String whereClause = makeWhereClause(subject, predicate, object); + Statement stmt = sdbConn.getSqlConnection().createStatement(); + ResultSet rs = stmt.executeQuery("SELECT \n" + + "N1.lex AS s_lex,\n" + + "N1.lang AS s_lang,\n" + + "N1.datatype AS s_datatype,\n" + + "N1.type AS s_type,\n" + + "N2.lex AS p_lex,\n" + + "N2.lang AS p_lang,\n" + + "N2.datatype AS p_datatype,\n" + + "N2.type AS p_type,\n" + + "N3.lex AS o_lex,\n" + + "N3.lang AS o_lang,\n" + + "N3.datatype AS o_datatype,\n" + + "N3.type AS o_type\n" + + "FROM\n" + + "(SELECT DISTINCT s,p,o FROM Quads" + + (StringUtils.isEmpty(whereClause) ? "" : " WHERE " + whereClause) + + " ORDER BY s,p,o " + + (limit > 0 ? "LIMIT " + limit : "") + + (offset > 0 ? " OFFSET " + offset : "") + ") Q\n" + + "LEFT OUTER JOIN\n" + + "\tNodes AS N1\n" + + "ON ( Q.s = N1.hash )\n" + + "LEFT OUTER JOIN\n" + + "\tNodes AS N2\n" + + "ON ( Q.p = N2.hash )\n" + + "LEFT OUTER JOIN\n" + + "\tNodes AS N3\n" + + "ON ( Q.o = N3.hash )"); + + try { + while (rs.next()) { + Node subjectNode = makeNode( + rs.getString("s_lex"), + rs.getString("s_datatype"), + rs.getString("s_lang"), + ValueType.lookup(rs.getInt("s_type"))); + + Node predicateNode = makeNode( + rs.getString("p_lex"), + rs.getString("p_datatype"), + rs.getString("p_lang"), + ValueType.lookup(rs.getInt("p_type"))); + + Node objectNode = makeNode( + rs.getString("o_lex"), + rs.getString("o_datatype"), + rs.getString("o_lang"), + ValueType.lookup(rs.getInt("o_type"))); + + triples.add( + triples.asStatement(Triple.create(subjectNode, predicateNode, objectNode)) + ); + } + } finally { + rs.close(); + } + } catch (SQLException sqle) { + throw new RDFServiceException("Unable to retrieve triples", sqle); + } finally { + close(sdbConn); + } + + return triples; + } + } + + return super.getTriples(subject, predicate, object, limit, offset); + } + + @Override public void close() { if (conn != null) { try { @@ -159,4 +285,55 @@ public class RDFServiceSDB extends RDFServiceJena implements RDFService { } } + // Copied from Jena SQLBridge2 + private static Node makeNode(String lex, String datatype, String lang, ValueType vType) { + switch(vType) { + case BNODE: + return NodeFactory.createBlankNode(lex); + case URI: + return NodeFactory.createURI(lex); + case STRING: + return NodeFactory.createLiteral(lex, lang); + case XSDSTRING: + return NodeFactory.createLiteral(lex, XSDDatatype.XSDstring); + case INTEGER: + return NodeFactory.createLiteral(lex, XSDDatatype.XSDinteger); + case DOUBLE: + return NodeFactory.createLiteral(lex, XSDDatatype.XSDdouble); + case DATETIME: + return NodeFactory.createLiteral(lex, XSDDatatype.XSDdateTime); + case OTHER: + RDFDatatype dt = TypeMapper.getInstance().getSafeTypeByName(datatype); + return NodeFactory.createLiteral(lex, dt); + default: + log.warn("Unrecognized: (" + lex + ", " + lang + ", " + vType + ")"); + return NodeFactory.createLiteral("UNRECOGNIZED"); + } + } + + private String makeWhereClause(RDFNode subject, RDFNode predicate, RDFNode object) { + StringBuilder whereClause = new StringBuilder(); + if (subject != null) { + if (whereClause.length() > 0) { + whereClause.append(" AND "); + } + whereClause.append("s=").append(NodeLayout2.hash(subject.asNode())); + } + + if (predicate != null) { + if (whereClause.length() > 0) { + whereClause.append(" AND "); + } + whereClause.append("p=").append(NodeLayout2.hash(predicate.asNode())); + } + + if (object != null) { + if (whereClause.length() > 0) { + whereClause.append(" AND "); + } + whereClause.append("o=").append(NodeLayout2.hash(object.asNode())); + } + + return whereClause.length() > 0 ? whereClause.toString() : null; + } } diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/rdfservice/impl/logging/LoggingRDFService.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/rdfservice/impl/logging/LoggingRDFService.java index 19006cfd4..f267c7763 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/rdfservice/impl/logging/LoggingRDFService.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/rdfservice/impl/logging/LoggingRDFService.java @@ -14,6 +14,7 @@ import edu.cornell.mannlib.vitro.webapp.rdfservice.ChangeSet; import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFService; import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFServiceException; import edu.cornell.mannlib.vitro.webapp.rdfservice.ResultSetConsumer; +import org.apache.jena.rdf.model.RDFNode; /** * This RDFService wrapper adds instrumentation to the time-consuming methods of @@ -182,6 +183,16 @@ public class LoggingRDFService implements RDFService { return innerService.manufactureChangeSet(); } + @Override + public long countTriples(RDFNode subject, RDFNode predicate, RDFNode object) throws RDFServiceException { + return innerService.countTriples(subject, predicate, object); + } + + @Override + public Model getTriples(RDFNode subject, RDFNode predicate, RDFNode object, long limit, long offset) throws RDFServiceException { + return innerService.getTriples(subject, predicate, object, limit, offset); + } + @Override public void close() { innerService.close(); diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/rdfservice/impl/logging/RDFServiceLogger.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/rdfservice/impl/logging/RDFServiceLogger.java index 4daebd9fe..6188a4378 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/rdfservice/impl/logging/RDFServiceLogger.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/rdfservice/impl/logging/RDFServiceLogger.java @@ -7,7 +7,7 @@ import java.util.Arrays; import java.util.List; import java.util.regex.Pattern; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/rdfservice/impl/virtuoso/RDFServiceVirtuoso.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/rdfservice/impl/virtuoso/RDFServiceVirtuoso.java index 1c7ff10a0..08ab76046 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/rdfservice/impl/virtuoso/RDFServiceVirtuoso.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/rdfservice/impl/virtuoso/RDFServiceVirtuoso.java @@ -127,12 +127,7 @@ public class RDFServiceVirtuoso extends RDFServiceSparql { private HttpPost createHttpRequest(String updateString) { HttpPost meth = new HttpPost(updateEndpointURI); meth.addHeader("Content-Type", "application/sparql-query"); - try { - meth.setEntity(new StringEntity(updateString, "UTF-8")); - } catch (UnsupportedEncodingException e) { - // UTF-8 is unsupported? - throw new RuntimeException(e); - } + meth.setEntity(new StringEntity(updateString, "UTF-8")); return meth; } diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/search/controller/AutocompleteController.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/search/controller/AutocompleteController.java index 294970953..1783c58cf 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/search/controller/AutocompleteController.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/search/controller/AutocompleteController.java @@ -15,7 +15,7 @@ import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.json.JSONArray; @@ -333,18 +333,10 @@ public class AutocompleteController extends VitroAjaxController { return label; } - public String getJsonLabel() { - return JSONObject.quote(label); - } - public String getUri() { return uri; } - public String getJsonUri() { - return JSONObject.quote(uri); - } - public String getMsType() { return msType; } @@ -360,11 +352,6 @@ public class AutocompleteController extends VitroAjaxController { return theType; } - public String getJsonMsType() { - return JSONObject.quote(msType); - } - - //Simply passing in the array in the map converts it to a string and not to an array //which is what we want so need to convert to an object instad JSONObject toJSONObject() { diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/search/controller/PagedSearchController.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/search/controller/PagedSearchController.java index 57fcc63a2..64911f65c 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/search/controller/PagedSearchController.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/search/controller/PagedSearchController.java @@ -18,7 +18,7 @@ import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -262,8 +262,8 @@ public class PagedSearchController extends FreemarkerHttpServlet { .getIndividualTemplateModels(individuals, vreq)); body.put("querytext", queryText); - body.put("title", queryText + " - " + appBean.getApplicationName() - + " Search Results"); + body.put("title", new StringBuilder().append(appBean.getApplicationName()).append(" - "). + append(I18n.text(vreq, "search_results_for")).append(" '").append(queryText).append("'").toString()); body.put("hitCount", hitCount); body.put("startIndex", startIndex); diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/search/controller/UpdateUrisInIndex.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/search/controller/UpdateUrisInIndex.java index c8cf04478..1d86082e7 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/search/controller/UpdateUrisInIndex.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/search/controller/UpdateUrisInIndex.java @@ -16,7 +16,7 @@ import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import org.apache.commons.fileupload.FileItem; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/searchengine/InstrumentedSearchEngineWrapper.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/searchengine/InstrumentedSearchEngineWrapper.java index 72228d940..d4ea2382e 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/searchengine/InstrumentedSearchEngineWrapper.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/searchengine/InstrumentedSearchEngineWrapper.java @@ -26,7 +26,6 @@ import edu.cornell.mannlib.vitro.webapp.modules.searchEngine.SearchResponse; import edu.cornell.mannlib.vitro.webapp.modules.searchEngine.SearchResultDocument; import edu.cornell.mannlib.vitro.webapp.modules.searchEngine.SearchResultDocumentList; import edu.cornell.mannlib.vitro.webapp.utils.configuration.Property; -import edu.cornell.mannlib.vitro.webapp.utils.configuration.Validation; /** * Manages the life-cycle of the SearchEngine. Adds logging, controlled by @@ -40,26 +39,11 @@ public class InstrumentedSearchEngineWrapper implements SearchEngine { private volatile LifecycleState lifecycleState = NEW; - @Property(uri = "http://vitro.mannlib.cornell.edu/ns/vitro/ApplicationSetup#wraps") + @Property(uri = "http://vitro.mannlib.cornell.edu/ns/vitro/ApplicationSetup#wraps", minOccurs = 1, maxOccurs = 1) public void setInnerEngine(SearchEngine inner) { - if (innerEngine == null) { - innerEngine = inner; - } else { - throw new IllegalStateException( - "Configuration includes multiple SearchEngine instancess: " - + innerEngine + ", and " + inner); - } + innerEngine = inner; } - @Validation - public void validate() throws Exception { - if (innerEngine == null) { - throw new IllegalStateException( - "Configuration did not include a wrapped SearchEngine."); - } - } - - /** * Complain unless ACTIVE. */ @@ -222,13 +206,13 @@ public class InstrumentedSearchEngineWrapper implements SearchEngine { return count; } } - + // ---------------------------------------------------------------------- // Helper classes // ---------------------------------------------------------------------- - - private static class SearchResponseForDocumentCount implements SearchResponse { + private static class SearchResponseForDocumentCount implements + SearchResponse { private final int count; public SearchResponseForDocumentCount(int count) { @@ -254,28 +238,29 @@ public class InstrumentedSearchEngineWrapper implements SearchEngine { public List getFacetFields() { return Collections.emptyList(); } - - private class EmptyDocumentListWithCount implements SearchResultDocumentList { - @Override - public Iterator iterator() { - return Collections.emptyIterator(); - } - - @Override - public int size() { - return 0; - } - - @Override - public long getNumFound() { - return count; - } - - @Override - public SearchResultDocument get(int i) { - throw new ArrayIndexOutOfBoundsException(i); - } + + private class EmptyDocumentListWithCount implements + SearchResultDocumentList { + @Override + public Iterator iterator() { + return Collections.emptyIterator(); + } + + @Override + public int size() { + return 0; + } + + @Override + public long getNumFound() { + return count; + } + + @Override + public SearchResultDocument get(int i) { + throw new ArrayIndexOutOfBoundsException(i); + } } } - + } diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/searchengine/SearchEngineLogger.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/searchengine/SearchEngineLogger.java index f3821ea77..d4b1cbf8f 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/searchengine/SearchEngineLogger.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/searchengine/SearchEngineLogger.java @@ -12,7 +12,7 @@ import java.util.Collection; import java.util.List; import java.util.regex.Pattern; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/searchindex/SearchIndexerImpl.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/searchindex/SearchIndexerImpl.java index 3e29bdab0..75974a5a9 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/searchindex/SearchIndexerImpl.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/searchindex/SearchIndexerImpl.java @@ -99,7 +99,7 @@ public class SearchIndexerImpl implements SearchIndexer { private Set uriFinders; private WebappDaoFactory wadf; - private boolean rebuildOnUnpause = false; + private boolean rebuildOnUnpause = false; private volatile int paused = 0; @@ -110,25 +110,14 @@ public class SearchIndexerImpl implements SearchIndexer { // ConfigurationBeanLoader methods. // ---------------------------------------------------------------------- - @Property(uri = "http://vitro.mannlib.cornell.edu/ns/vitro/ApplicationSetup#threadPoolSize") + @Property(uri = "http://vitro.mannlib.cornell.edu/ns/vitro/ApplicationSetup#threadPoolSize", minOccurs = 1, maxOccurs = 1) public void setThreadPoolSize(String size) { - if (threadPoolSize == null) { - threadPoolSize = Integer.parseInt(size); - } else { - throw new IllegalStateException( - "Configuration includes multiple values for threadPoolSize: " - + threadPoolSize + ", and " + size); - } + threadPoolSize = Integer.parseInt(size); } @Validation public void validate() throws Exception { - if (threadPoolSize == null) { - throw new IllegalStateException( - "Configuration did not include a value for threadPoolSize."); - } else { - this.pool = new WorkerThreadPool(threadPoolSize); - } + this.pool = new WorkerThreadPool(threadPoolSize); } // ---------------------------------------------------------------------- @@ -241,7 +230,7 @@ public class SearchIndexerImpl implements SearchIndexer { } } - private synchronized void schedulePendingUris() { + private synchronized void schedulePendingUris() { if (paused == 0 && pendingUris.size() > 0) { scheduleUpdatesForUris(pendingUris); pendingUris = new ArrayList<>(); @@ -278,13 +267,14 @@ public class SearchIndexerImpl implements SearchIndexer { if (changes == null || changes.isEmpty()) { return; } - if (paused > 0) { + if (paused > 0) { if (addToPendingStatements(changes)) { return; } - } + } - scheduler.scheduleTask(new UpdateStatementsTask(new IndexerConfigImpl(this), changes)); + scheduler.scheduleTask(new UpdateStatementsTask(new IndexerConfigImpl( + this), changes)); log.debug("Scheduled updates for " + changes.size() + " statements."); } @@ -306,13 +296,14 @@ public class SearchIndexerImpl implements SearchIndexer { if (uris == null || uris.isEmpty()) { return; } - if (paused > 0) { + if (paused > 0) { if (pendingUris.addAll(uris)) { return; } - } + } - scheduler.scheduleTask(new UpdateUrisTask(new IndexerConfigImpl(this), uris)); + scheduler.scheduleTask(new UpdateUrisTask(new IndexerConfigImpl(this), + uris)); log.debug("Scheduled updates for " + uris.size() + " uris."); } @@ -332,13 +323,14 @@ public class SearchIndexerImpl implements SearchIndexer { return; } fireEvent(REBUILD_REQUESTED); - if (paused > 0) { + if (paused > 0) { // Make sure that we are rebuilding when we unpause // and don't bother noting any other changes until unpaused - rebuildOnUnpause = true; - return; - } - scheduler.scheduleTask(new RebuildIndexTask(new IndexerConfigImpl(this))); + rebuildOnUnpause = true; + return; + } + scheduler + .scheduleTask(new RebuildIndexTask(new IndexerConfigImpl(this))); log.debug("Scheduled a full rebuild."); } @@ -447,13 +439,13 @@ public class SearchIndexerImpl implements SearchIndexer { } public synchronized void scheduleTask(Task task) { - if (!started) { - deferredQueue.add(task); - log.debug("added task to deferred queue: " + task); - } else { + if (!started) { + deferredQueue.add(task); + log.debug("added task to deferred queue: " + task); + } else { taskQueue.scheduleTask(task); log.debug("added task to task queue: " + task); - } + } } public synchronized void start() { @@ -463,8 +455,9 @@ public class SearchIndexerImpl implements SearchIndexer { private void processDeferredTasks() { for (Task task : deferredQueue) { - taskQueue.scheduleTask(task); - log.debug("moved task from deferred queue to task queue: " + task); + taskQueue.scheduleTask(task); + log.debug("moved task from deferred queue to task queue: " + + task); } deferredQueue.clear(); } diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/searchindex/documentBuilding/FieldBooster.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/searchindex/documentBuilding/FieldBooster.java index 5a86e1f65..7a3f0dad9 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/searchindex/documentBuilding/FieldBooster.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/searchindex/documentBuilding/FieldBooster.java @@ -17,27 +17,18 @@ public class FieldBooster implements DocumentModifier { private final List fieldNames = new ArrayList<>(); private volatile Float boost; - @Property(uri = "http://vitro.mannlib.cornell.edu/ns/vitro/ApplicationSetup#hasTargetField") + @Property(uri = "http://vitro.mannlib.cornell.edu/ns/vitro/ApplicationSetup#hasTargetField", minOccurs = 1) public void addTargetField(String fieldName) { fieldNames.add(fieldName); } - @Property(uri = "http://vitro.mannlib.cornell.edu/ns/vitro/ApplicationSetup#hasBoost") + @Property(uri = "http://vitro.mannlib.cornell.edu/ns/vitro/ApplicationSetup#hasBoost", minOccurs = 1) public void setBoost(float boost) { this.boost = boost; } @Validation public void validate() { - if (boost == null) { - throw new IllegalStateException( - "Configuration did not include a boost value."); - } - if (fieldNames.isEmpty()) { - throw new IllegalStateException( - "Configuration did not include a target field."); - } - Set uniqueFieldNames = new HashSet<>(fieldNames); List duplicateFieldNames = new ArrayList<>(fieldNames); for (String fn : uniqueFieldNames) { diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/searchindex/documentBuilding/SelectQueryDocumentModifier.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/searchindex/documentBuilding/SelectQueryDocumentModifier.java index 1494ddbbb..971cf4fe5 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/searchindex/documentBuilding/SelectQueryDocumentModifier.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/searchindex/documentBuilding/SelectQueryDocumentModifier.java @@ -5,7 +5,7 @@ package edu.cornell.mannlib.vitro.webapp.searchindex.documentBuilding; import static edu.cornell.mannlib.vitro.webapp.modelaccess.ModelAccess.WhichService.CONTENT; import static edu.cornell.mannlib.vitro.webapp.search.VitroSearchTermNames.ALLTEXT; import static edu.cornell.mannlib.vitro.webapp.search.VitroSearchTermNames.ALLTEXTUNSTEMMED; -import static edu.cornell.mannlib.vitro.webapp.utils.sparql.SelectQueryRunner.createQueryContext; +import static edu.cornell.mannlib.vitro.webapp.utils.sparqlrunner.SparqlQueryRunner.createSelectQueryContext; import java.util.ArrayList; import java.util.Collections; @@ -24,7 +24,7 @@ import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFService; import edu.cornell.mannlib.vitro.webapp.utils.configuration.ContextModelsUser; import edu.cornell.mannlib.vitro.webapp.utils.configuration.Property; import edu.cornell.mannlib.vitro.webapp.utils.configuration.Validation; -import edu.cornell.mannlib.vitro.webapp.utils.sparql.SelectQueryHolder; +import edu.cornell.mannlib.vitro.webapp.utils.sparqlrunner.QueryHolder; /** * Modify the document, adding the results of one or more select queries. @@ -151,10 +151,10 @@ public class SelectQueryDocumentModifier implements DocumentModifier, private List getTextForQuery(String query, Individual ind) { try { - SelectQueryHolder queryHolder = new SelectQueryHolder(query) - .bindToUri("uri", ind.getURI()); - List list = createQueryContext(rdfService, queryHolder) - .execute().getStringFields().flatten(); + QueryHolder queryHolder = new QueryHolder(query).bindToUri("uri", + ind.getURI()); + List list = createSelectQueryContext(rdfService, + queryHolder).execute().toStringFields().flatten(); log.debug(label + " - query: '" + query + "' returns " + list); return list; } catch (Throwable t) { diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/searchindex/indexing/SelectQueryUriFinder.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/searchindex/indexing/SelectQueryUriFinder.java index c82e5fb9c..98229828c 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/searchindex/indexing/SelectQueryUriFinder.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/searchindex/indexing/SelectQueryUriFinder.java @@ -3,8 +3,8 @@ package edu.cornell.mannlib.vitro.webapp.searchindex.indexing; import static edu.cornell.mannlib.vitro.webapp.modelaccess.ModelAccess.WhichService.CONTENT; -import static edu.cornell.mannlib.vitro.webapp.utils.sparql.SelectQueryRunner.createQueryContext; -import static edu.cornell.mannlib.vitro.webapp.utils.sparql.SelectQueryRunner.selectQuery; +import static edu.cornell.mannlib.vitro.webapp.utils.sparqlrunner.SparqlQueryRunner.createSelectQueryContext; +import static edu.cornell.mannlib.vitro.webapp.utils.sparqlrunner.SparqlQueryRunner.queryHolder; import java.util.ArrayList; import java.util.Collections; @@ -23,7 +23,7 @@ import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFService; import edu.cornell.mannlib.vitro.webapp.utils.configuration.ContextModelsUser; import edu.cornell.mannlib.vitro.webapp.utils.configuration.Property; import edu.cornell.mannlib.vitro.webapp.utils.configuration.Validation; -import edu.cornell.mannlib.vitro.webapp.utils.sparql.SelectQueryHolder; +import edu.cornell.mannlib.vitro.webapp.utils.sparqlrunner.QueryHolder; /** * Find URIs based on one or more select queries. @@ -74,7 +74,7 @@ public class SelectQueryUriFinder implements IndexingUriFinder, label = l; } - @Property(uri = "http://vitro.mannlib.cornell.edu/ns/vitro/ApplicationSetup#hasSelectQuery") + @Property(uri = "http://vitro.mannlib.cornell.edu/ns/vitro/ApplicationSetup#hasSelectQuery", minOccurs = 1) public void addQuery(String query) { queries.add(query); } @@ -89,10 +89,6 @@ public class SelectQueryUriFinder implements IndexingUriFinder, if (label == null) { label = this.getClass().getSimpleName() + ":" + this.hashCode(); } - if (queries.isEmpty()) { - throw new IllegalStateException( - "Configuration contains no queries for " + label); - } } @Override @@ -122,7 +118,7 @@ public class SelectQueryUriFinder implements IndexingUriFinder, } private List getUrisForQuery(Statement stmt, String queryString) { - SelectQueryHolder query = selectQuery(queryString); + QueryHolder query = queryHolder(queryString); query = query.bindToUri("predicate", stmt.getPredicate().getURI()); query = tryToBindUri(query, "subject", stmt.getSubject()); @@ -131,12 +127,12 @@ public class SelectQueryUriFinder implements IndexingUriFinder, return Collections.emptyList(); } - return createQueryContext(rdfService, query).execute() - .getStringFields().flatten(); + return createSelectQueryContext(rdfService, query).execute() + .toStringFields().flatten(); } - private SelectQueryHolder tryToBindUri(SelectQueryHolder query, - String name, RDFNode node) { + private QueryHolder tryToBindUri(QueryHolder query, String name, + RDFNode node) { if (query == null) { return null; } diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/searchindex/tasks/UpdateDocumentWorkUnit.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/searchindex/tasks/UpdateDocumentWorkUnit.java index cbab202e2..239812746 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/searchindex/tasks/UpdateDocumentWorkUnit.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/searchindex/tasks/UpdateDocumentWorkUnit.java @@ -18,7 +18,7 @@ import java.util.HashSet; import java.util.List; import java.util.Set; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.joda.time.DateTime; diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/services/shortview/ShortViewService.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/services/shortview/ShortViewService.java index 13b04e32f..af0049767 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/services/shortview/ShortViewService.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/services/shortview/ShortViewService.java @@ -4,7 +4,7 @@ package edu.cornell.mannlib.vitro.webapp.services.shortview; import java.util.Map; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import edu.cornell.mannlib.vitro.webapp.beans.Individual; import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest; diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/triplesource/impl/sdb/SDBDataSource.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/triplesource/impl/sdb/SDBDataSource.java index 4131f91e2..4bbc7a839 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/triplesource/impl/sdb/SDBDataSource.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/triplesource/impl/sdb/SDBDataSource.java @@ -6,7 +6,7 @@ import java.beans.PropertyVetoException; import javax.servlet.ServletContext; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/triplesource/impl/sparql/ContentTripleSourceSPARQL.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/triplesource/impl/sparql/ContentTripleSourceSPARQL.java index da90f2eda..1e4ff0283 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/triplesource/impl/sparql/ContentTripleSourceSPARQL.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/triplesource/impl/sparql/ContentTripleSourceSPARQL.java @@ -19,7 +19,6 @@ import edu.cornell.mannlib.vitro.webapp.rdfservice.impl.RDFServiceFactorySingle; import edu.cornell.mannlib.vitro.webapp.rdfservice.impl.logging.LoggingRDFServiceFactory; import edu.cornell.mannlib.vitro.webapp.rdfservice.impl.sparql.RDFServiceSparql; import edu.cornell.mannlib.vitro.webapp.utils.configuration.Property; -import edu.cornell.mannlib.vitro.webapp.utils.configuration.Validation; import edu.cornell.mannlib.vitro.webapp.utils.logging.ToString; /** @@ -41,34 +40,14 @@ public class ContentTripleSourceSPARQL extends ContentTripleSource { private Dataset dataset; private ModelMaker modelMaker; - @Property(uri = "http://vitro.mannlib.cornell.edu/ns/vitro/ApplicationSetup#hasEndpointURI") + @Property(uri = "http://vitro.mannlib.cornell.edu/ns/vitro/ApplicationSetup#hasEndpointURI", minOccurs = 1, maxOccurs = 1) public void setEndpointURI(String eUri) { - if (endpointURI == null) { - endpointURI = eUri; - } else { - throw new IllegalStateException( - "Configuration includes multiple instances of EndpointURI: " - + endpointURI + ", and " + eUri); - } + endpointURI = eUri; } - @Property(uri = "http://vitro.mannlib.cornell.edu/ns/vitro/ApplicationSetup#hasUpdateEndpointURI") + @Property(uri = "http://vitro.mannlib.cornell.edu/ns/vitro/ApplicationSetup#hasUpdateEndpointURI", maxOccurs = 1) public void setUpdateEndpointURI(String ueUri) { - if (updateEndpointURI == null) { - updateEndpointURI = ueUri; - } else { - throw new IllegalStateException( - "Configuration includes multiple instances of UpdateEndpointURI: " - + updateEndpointURI + ", and " + ueUri); - } - } - - @Validation - public void validate() throws Exception { - if (endpointURI == null) { - throw new IllegalStateException( - "Configuration did not include an EndpointURI."); - } + updateEndpointURI = ueUri; } @Override diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/triplesource/impl/tdb/ContentTripleSourceTDB.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/triplesource/impl/tdb/ContentTripleSourceTDB.java index 43656a7ec..5ab71592d 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/triplesource/impl/tdb/ContentTripleSourceTDB.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/triplesource/impl/tdb/ContentTripleSourceTDB.java @@ -24,7 +24,6 @@ import edu.cornell.mannlib.vitro.webapp.rdfservice.impl.jena.tdb.RDFServiceTDB; import edu.cornell.mannlib.vitro.webapp.rdfservice.impl.logging.LoggingRDFServiceFactory; import edu.cornell.mannlib.vitro.webapp.servlet.setup.JenaDataSourceSetupBase; import edu.cornell.mannlib.vitro.webapp.utils.configuration.Property; -import edu.cornell.mannlib.vitro.webapp.utils.configuration.Validation; import edu.cornell.mannlib.vitro.webapp.utils.logging.ToString; /** @@ -49,23 +48,9 @@ public class ContentTripleSourceTDB extends ContentTripleSource { private Dataset dataset; private ModelMaker modelMaker; - @Property(uri = "http://vitro.mannlib.cornell.edu/ns/vitro/ApplicationSetup#hasTdbDirectory") + @Property(uri = "http://vitro.mannlib.cornell.edu/ns/vitro/ApplicationSetup#hasTdbDirectory", minOccurs = 1, maxOccurs = 1) public void setTdbPath(String path) { - if (tdbPath == null) { - tdbPath = path; - } else { - throw new IllegalStateException( - "Configuration includes multiple instances of TdbDirectory: " - + tdbPath + ", and " + path); - } - } - - @Validation - public void validate() throws Exception { - if (tdbPath == null) { - throw new IllegalStateException( - "Configuration did not include a TdbDirectory."); - } + tdbPath = path; } @Override @@ -106,7 +91,8 @@ public class ContentTripleSourceTDB extends ContentTripleSource { } private void checkForFirstTimeStartup() { - if (this.dataset.getNamedModel(ModelNames.TBOX_ASSERTIONS).getGraph().isEmpty()) { + if (this.dataset.getNamedModel(ModelNames.TBOX_ASSERTIONS).getGraph() + .isEmpty()) { JenaDataSourceSetupBase.thisIsFirstStartup(); } } diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/triplesource/impl/virtuoso/ContentTripleSourceVirtuoso.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/triplesource/impl/virtuoso/ContentTripleSourceVirtuoso.java index 441279fb3..5cb4c0ce0 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/triplesource/impl/virtuoso/ContentTripleSourceVirtuoso.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/triplesource/impl/virtuoso/ContentTripleSourceVirtuoso.java @@ -7,7 +7,6 @@ import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFService; import edu.cornell.mannlib.vitro.webapp.rdfservice.impl.virtuoso.RDFServiceVirtuoso; import edu.cornell.mannlib.vitro.webapp.triplesource.impl.sparql.ContentTripleSourceSPARQL; import edu.cornell.mannlib.vitro.webapp.utils.configuration.Property; -import edu.cornell.mannlib.vitro.webapp.utils.configuration.Validation; import edu.cornell.mannlib.vitro.webapp.utils.logging.ToString; /** @@ -19,54 +18,19 @@ public class ContentTripleSourceVirtuoso extends ContentTripleSourceSPARQL { private String username; private String password; - @Property(uri = "http://vitro.mannlib.cornell.edu/ns/vitro/ApplicationSetup#hasBaseURI") + @Property(uri = "http://vitro.mannlib.cornell.edu/ns/vitro/ApplicationSetup#hasBaseURI", minOccurs = 1, maxOccurs = 1) public void setBaseUri(String uri) { - if (baseUri == null) { - baseUri = uri; - } else { - throw new IllegalStateException( - "Configuration includes multiple instances of BaseURI: " - + baseUri + ", and " + uri); - } + baseUri = uri; } - @Property(uri = "http://vitro.mannlib.cornell.edu/ns/vitro/ApplicationSetup#hasUsername") + @Property(uri = "http://vitro.mannlib.cornell.edu/ns/vitro/ApplicationSetup#hasUsername", minOccurs = 1, maxOccurs = 1) public void setUsername(String user) { - if (username == null) { - username = user; - } else { - throw new IllegalStateException( - "Configuration includes multiple instances of Username: " - + username + ", and " + user); - } + username = user; } - @Property(uri = "http://vitro.mannlib.cornell.edu/ns/vitro/ApplicationSetup#hasPassword") + @Property(uri = "http://vitro.mannlib.cornell.edu/ns/vitro/ApplicationSetup#hasPassword", minOccurs = 1, maxOccurs = 1) public void setPassword(String pass) { - if (password == null) { - password = pass; - } else { - throw new IllegalStateException( - "Configuration includes multiple instances of Password: " - + password + ", and " + pass); - } - } - - @Override - @Validation - public void validate() throws Exception { - if (baseUri == null) { - throw new IllegalStateException( - "Configuration did not include a BaseURI."); - } - if (username == null) { - throw new IllegalStateException( - "Configuration did not include a Username."); - } - if (password == null) { - throw new IllegalStateException( - "Configuration did not include a Password."); - } + password = pass; } @Override diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/Csv2Rdf.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/Csv2Rdf.java index 0f9d90cc7..eb5c63b98 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/Csv2Rdf.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/Csv2Rdf.java @@ -4,14 +4,15 @@ package edu.cornell.mannlib.vitro.webapp.utils; import java.io.IOException; import java.io.InputStream; +import java.io.InputStreamReader; import java.util.List; import java.util.Random; import javax.servlet.http.HttpServletRequest; -import org.skife.csv.CSVReader; -import org.skife.csv.SimpleReader; - +import org.apache.commons.csv.CSVFormat; +import org.apache.commons.csv.CSVParser; +import org.apache.commons.csv.CSVRecord; import org.apache.jena.datatypes.xsd.XSDDatatype; import org.apache.jena.ontology.DatatypeProperty; import org.apache.jena.ontology.Individual; @@ -34,11 +35,11 @@ public class Csv2Rdf { private String individualNameBase; private String propertyNameBase; private char separatorChar; - private char[] quoteChars; + private char quoteChar; - public Csv2Rdf(char[] quoteChars, String namespace, String tboxNamespace, String typeName) { + public Csv2Rdf(char quoteChar, String namespace, String tboxNamespace, String typeName) { this.separatorChar = ','; - this.quoteChars = quoteChars; + this.quoteChar = quoteChar; this.namespace = namespace; this.tboxNamespace = tboxNamespace; this.typeName = typeName; @@ -46,9 +47,9 @@ public class Csv2Rdf { this.propertyNameBase = individualNameBase+"_"; } - public Csv2Rdf(char separatorChar, char[] quoteChars, String namespace, String tboxNamespace, String typeName) { + public Csv2Rdf(char separatorChar, char quoteChar, String namespace, String tboxNamespace, String typeName) { this.separatorChar = separatorChar; - this.quoteChars = quoteChars; + this.quoteChar = quoteChar; this.namespace = namespace; this.tboxNamespace = tboxNamespace; this.typeName = typeName; @@ -66,47 +67,48 @@ public class Csv2Rdf { ontModel.addSubModel(tboxOntModel); OntClass theClass = tboxOntModel.createClass(tboxNamespace+typeName); - CSVReader cReader = new SimpleReader(); - cReader.setSeperator(separatorChar); - cReader.setQuoteCharacters(quoteChars); - - URIGenerator uriGen = (wadf != null && destination != null) + URIGenerator uriGen = (wadf != null && destination != null) ? new RandomURIGenerator(wadf, destination) - : new SequentialURIGenerator(); - - List fileRows = cReader.parse(fis); - - String[] columnHeaders = fileRows.get(0); + : new SequentialURIGenerator(); - DatatypeProperty[] dpArray = new DatatypeProperty[columnHeaders.length]; + CSVParser cReader = new CSVParser(new InputStreamReader(fis), + CSVFormat.DEFAULT.withRecordSeparator(separatorChar) + .withQuote(quoteChar)); - for (int i=0; i0) { - ind.addProperty(dpArray[col], value); // no longer using: , XSDDatatype.XSDstring); - // TODO: specification of datatypes for columns - } - } - } - + DatatypeProperty[] dpArray = null; + + for (CSVRecord cRecord : cReader) { + if (dpArray == null) { + dpArray = new DatatypeProperty[cRecord.size()]; + + for (int i = 0; i < dpArray.length; i++) { + dpArray[i] = tboxOntModel.createDatatypeProperty(tboxNamespace+propertyNameBase+cRecord.get(i).replaceAll("\\W","")); + } + } else { + Individual ind = null; + String uri = uriGen.getNextURI(); + if (uri!=null) { + ind = ontModel.createIndividual(uri, theClass); + } else { + ind = ontModel.createIndividual(theClass); + } + for (int col = 0; col0) { + ind.addProperty(dpArray[col], value); // no longer using: , XSDDatatype.XSDstring); + // TODO: specification of datatypes for columns + } + } + } + } + + cReader.close(); ontModel.removeSubModel(tboxOntModel); Model[] resultModels = new Model[2]; resultModels[0] = ontModel; resultModels[1] = tboxOntModel; return resultModels; - } private interface URIGenerator { diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/FedoraConfiguration.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/FedoraConfiguration.java deleted file mode 100644 index d014e0688..000000000 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/FedoraConfiguration.java +++ /dev/null @@ -1,198 +0,0 @@ -/* $This file is distributed under the terms of the license in /doc/license.txt$ */ - -package edu.cornell.mannlib.vitro.webapp.utils; - -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.InputStream; -import java.util.Properties; - -import javax.servlet.ServletContext; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.joda.time.format.DateTimeFormatter; -import org.joda.time.format.ISODateTimeFormat; - -import org.apache.jena.rdf.model.Property; -import org.apache.jena.rdf.model.ResourceFactory; - -import edu.cornell.mannlib.vitro.webapp.dao.VitroVocabulary; -import fedora.client.FedoraClient; - -public class FedoraConfiguration { - - private static String FEDORA_PROPERTIES = "/WEB-INF/fedora.properties"; - - public String fedoraUrl = null; - private String adminUser = null; - private String adminPassword = null; - private String pidNamespace = null; - private String configurationStatus = "

Fedora configuration not yet loaded

"; - private boolean configured = false; - private boolean connected = false; - - public FedoraConfiguration(ServletContext context){ - setup( context ); - } - - public boolean isConfigured(){ return configured; } - public boolean isConnected(){ return connected; } - - public String setup(ServletContext context ){ - internalSetup( context); - if( ! configured ) - return configurationStatus; - canConnectToFedoraServer(); - return configurationStatus; - } - - private void internalSetup(ServletContext context) { - this.configurationStatus = ""; - StringBuffer status = new StringBuffer(""); - - if( connected && configured ) - return; - - Properties props = new Properties(); - String path = context.getRealPath(FEDORA_PROPERTIES); - try{ - InputStream in = new FileInputStream(new File( path )); - props.load( in ); - fedoraUrl = props.getProperty("fedoraUrl"); - adminUser = props.getProperty("adminUser"); - adminPassword = props.getProperty("adminPassword"); - pidNamespace = props.getProperty("pidNamespace"); - if( fedoraUrl == null || adminUser == null || adminPassword == null ){ - if( fedoraUrl == null ){ - log.error("'fedoraUrl' not found in properties file"); - status.append("

'fedoraUrl' not found in properties file.

\n"); - } - if( adminUser == null ) { - log.error("'adminUser' was not found in properties file, the " + - "user name of the fedora admin is needed to access the " + - "fedora API-M services."); - status.append("

'adminUser' was not found in properties file, the " + - "user name of the fedora admin is needed to access the " + - "fedora API-M services.

\n"); - } - if( adminPassword == null ){ - log.error("'adminPassword' was not found in properties file, the " + - "admin password is needed to access the fedora API-M services."); - status.append("

'adminPassword' was not found in properties file, the " + - "admin password is needed to access the fedora API-M services.

\n"); - } - if( pidNamespace == null ){ - log.error("'pidNamespace' was not found in properties file, the " + - "PID namespace indicates which namespace to use when creating " + - "new fedor digital objects."); - status.append("

'pidNamespace' was not found in properties file, the " + - "PID namespace indicates which namespace to use when creating " + - "new fedor digital objects.

\n"); - } - fedoraUrl = null; adminUser = null; adminPassword = null; - configured = false; - } else { - configured = true; - } - }catch(FileNotFoundException e){ - log.error("No fedora.properties file found,"+ - "it should be located at " + path); - status.append("

Fedora configuration failed.

\n"); - status.append("

No fedora.properties file found,"+ - "it should be located at " + path + "

\n"); - configured = false; - return; - }catch(Exception ex){ - status.append("

Fedora configuration failed.

\n"); - status.append("

Exception while loading" + path + "

\n"); - status.append("

" + ex.getMessage() + "

\n"); - log.error("could not load fedora properties", ex); - fedoraUrl = null; adminUser = null; adminPassword = null; - configured = false; - return; - } - - status.append(RELOAD_MSG); - this.configurationStatus += status.toString(); - } - - public boolean canConnectToFedoraServer( ){ - try{ - FedoraClient fc = new FedoraClient(fedoraUrl,adminUser, adminPassword); - String fedoraVersion = fc.getServerVersion(); - if( fedoraVersion != null && fedoraVersion.length() > 0 ){ - configurationStatus += "

Fedora server is live and is running " + - "fedora version " + fedoraVersion + "

\n"; - connected = true; - return true; - } else { - configurationStatus += "

Unable to reach fedora server

\n"; - connected = false; - return false; - } - }catch (Exception e) { - configurationStatus += "

There was an error while checking the " + - "fedora server version

\n

"+ e.getMessage() + "

\n"; - connected = false; - return false; - } - } - - - - public static final Property FILE_NAME = ResourceFactory.createProperty(VitroVocabulary.FILE_NAME); - public static final Property CONTENT_TYPE = ResourceFactory.createProperty(VitroVocabulary.CONTENT_TYPE); - public static final Property FILE_LOCATION = ResourceFactory.createProperty(VitroVocabulary.FILE_LOCATION); - //public static final Property FEDORA_ID = ResourceFactory.createProperty(VitroVocabulary.FEDORA_PID); - - DateTimeFormatter isoFormatter = ISODateTimeFormat.dateTime(); - - private static Log log = LogFactory.getLog(FedoraConfiguration.class); - - private static final String RELOAD_MSG = - "

The fedora configuartion file will be reloaded if " + - "you edit the properties file and check the status.

\n"; - - public String getFedoraUrl() { - return fedoraUrl; - } - - public void setFedoraUrl(String fedoraUrl) { - this.fedoraUrl = fedoraUrl; - } - - public String getAdminUser() { - return adminUser; - } - - public void setAdminUser(String adminUser) { - this.adminUser = adminUser; - } - - public String getAdminPassword() { - return adminPassword; - } - - public void setAdminPassword(String adminPassword) { - this.adminPassword = adminPassword; - } - - public String getPidNamespace() { - return pidNamespace; - } - - public void setPidNamespace(String pidNamespace) { - this.pidNamespace = pidNamespace; - } - - public String getConfigurationStatus() { - return configurationStatus; - } - - public void setConfigurationStatus(String configurationStatus) { - this.configurationStatus = configurationStatus; - } - -} diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/configuration/ConfigurationBeanLoader.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/configuration/ConfigurationBeanLoader.java index 4a9ce2361..cf7793838 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/configuration/ConfigurationBeanLoader.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/configuration/ConfigurationBeanLoader.java @@ -116,6 +116,7 @@ public class ConfigurationBeanLoader { WrappedInstance wrapper = InstanceWrapper.wrap(parsedRdf .getConcreteClass()); wrapper.satisfyInterfaces(ctx, req); + wrapper.checkCardinality(parsedRdf.getPropertyStatements()); wrapper.setProperties(this, parsedRdf.getPropertyStatements()); wrapper.validate(); return wrapper.getInstance(); diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/configuration/InstanceWrapper.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/configuration/InstanceWrapper.java index c882ccfd5..45db851ad 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/configuration/InstanceWrapper.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/configuration/InstanceWrapper.java @@ -6,7 +6,9 @@ import java.lang.reflect.Method; import java.util.HashMap; import java.util.HashSet; import java.util.Map; -import java.util.Set; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; import edu.cornell.mannlib.vitro.webapp.utils.configuration.PropertyType.PropertyMethod; import edu.cornell.mannlib.vitro.webapp.utils.configuration.PropertyType.PropertyTypeException; @@ -16,11 +18,17 @@ import edu.cornell.mannlib.vitro.webapp.utils.configuration.PropertyType.Propert * instance of the class. */ public class InstanceWrapper { + private static final Log log = LogFactory.getLog(InstanceWrapper.class); + public static WrappedInstance wrap(Class concreteClass) throws InstanceWrapperException { - return new WrappedInstance(createInstance(concreteClass), - parsePropertyAnnotations(concreteClass), - parseValidationAnnotations(concreteClass)); + T instance = createInstance(concreteClass); + HashSet validationMethods = new HashSet<>( + parseValidationAnnotations(concreteClass).values()); + Map propertyMethods = new PropertyAnnotationsMap( + concreteClass).byUri(); + return new WrappedInstance(instance, propertyMethods, + validationMethods); } private static T createInstance(Class concreteClass) @@ -33,58 +41,131 @@ public class InstanceWrapper { } } - private static Map parsePropertyAnnotations( - Class concreteClass) throws InstanceWrapperException { - Map map = new HashMap<>(); - for (Method method : concreteClass.getDeclaredMethods()) { - Property annotation = method.getAnnotation(Property.class); - if (annotation == null) { - continue; - } - if (!method.getReturnType().equals(Void.TYPE)) { - throw new InstanceWrapperException("Property method '" + method - + "' should return void."); - } - Class[] parameterTypes = method.getParameterTypes(); - if (parameterTypes.length != 1) { - throw new InstanceWrapperException("Property method '" + method - + "' must accept exactly one parameter."); - } - - String uri = annotation.uri(); - if (map.containsKey(uri)) { - throw new InstanceWrapperException( - "Two property methods have the same URI value: " - + map.get(uri).getMethod() + ", and " + method); - } - try { - map.put(uri, PropertyType.createPropertyMethod(method)); - } catch (PropertyTypeException e) { - throw new InstanceWrapperException( - "Failed to create the PropertyMethod", e); + private static Map parseValidationAnnotations(Class clazz) + throws InstanceWrapperException { + if (Object.class.equals(clazz)) { + return new HashMap<>(); + } else { + Map methods = parseValidationAnnotations(clazz + .getSuperclass()); + for (Method method : clazz.getDeclaredMethods()) { + String name = method.getName(); + if (methods.containsKey(name)) { + Method m = methods.get(name); + throw new InstanceWrapperException("Method " + name + + " in " + method.getDeclaringClass().getName() + + " overrides a validation method in " + + m.getDeclaringClass().getName()); + } + if (method.getAnnotation(Validation.class) == null) { + continue; + } + if (method.getParameterTypes().length > 0) { + throw new InstanceWrapperException("Validation method '" + + method + "' should not have parameters."); + } + if (!method.getReturnType().equals(Void.TYPE)) { + throw new InstanceWrapperException("Validation method '" + + method + "' should return void."); + } + methods.put(name, method); } + return methods; } - return map; } - private static Set parseValidationAnnotations(Class concreteClass) - throws InstanceWrapperException { - Set methods = new HashSet<>(); - for (Method method : concreteClass.getDeclaredMethods()) { - if (method.getAnnotation(Validation.class) == null) { - continue; + private static class PropertyAnnotationsMap { + private Map mapByUri = new HashMap<>(); + private Map mapByName = new HashMap<>(); + + public PropertyAnnotationsMap(Class clazz) + throws InstanceWrapperException { + if (!Object.class.equals(clazz)) { + populateTheMaps(clazz); } - if (method.getParameterTypes().length > 0) { - throw new InstanceWrapperException("Validation method '" - + method + "' should not have parameters."); - } - if (!method.getReturnType().equals(Void.TYPE)) { - throw new InstanceWrapperException("Validation method '" - + method + "' should return void."); - } - methods.add(method); } - return methods; + + private void populateTheMaps(Class clazz) + throws InstanceWrapperException { + PropertyAnnotationsMap superMap = new PropertyAnnotationsMap( + clazz.getSuperclass()); + mapByUri = superMap.byUri(); + mapByName = superMap.byName(); + for (Method method : clazz.getDeclaredMethods()) { + String name = method.getName(); + + Method matchByName = methodByName(name); + if (matchByName != null) { + throw new InstanceWrapperException("Method " + name + + " in " + clazz.getName() + + " conflicts with a property method in " + + matchByName.getDeclaringClass().getName()); + } + + Property annotation = method.getAnnotation(Property.class); + if (annotation == null) { + continue; + } + + if (!method.getReturnType().equals(Void.TYPE)) { + throw new InstanceWrapperException("Property method '" + + method + "' should return void."); + } + + if (method.getParameterTypes().length != 1) { + throw new InstanceWrapperException("Property method '" + + method + "' must accept exactly one parameter."); + } + + String uri = annotation.uri(); + Method matchByUri = methodByUri(uri); + if (matchByUri != null) { + throw new InstanceWrapperException( + "Two property methods have the same URI value: " + + matchByUri + ", and " + method); + } + + if (annotation.minOccurs() < 0) { + throw new InstanceWrapperException( + "minOccurs must not be negative."); + } + + if (annotation.maxOccurs() < annotation.minOccurs()) { + throw new InstanceWrapperException( + "maxOccurs must not be less than minOccurs."); + } + + try { + PropertyMethod pm = PropertyType.createPropertyMethod( + method, annotation); + mapByUri.put(uri, pm); + mapByName.put(name, pm); + } catch (PropertyTypeException e) { + throw new InstanceWrapperException( + "Failed to create the PropertyMethod", e); + } + } + + } + + private Method methodByName(String name) { + PropertyMethod pm = mapByName.get(name); + return (pm == null) ? null : pm.getMethod(); + } + + private Method methodByUri(String name) { + PropertyMethod pm = mapByUri.get(name); + return (pm == null) ? null : pm.getMethod(); + } + + public Map byUri() { + return mapByUri; + } + + public Map byName() { + return mapByName; + } + } public static class InstanceWrapperException extends Exception { diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/configuration/Property.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/configuration/Property.java index 0eecb23e8..bcc60bba3 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/configuration/Property.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/configuration/Property.java @@ -15,4 +15,6 @@ import java.lang.annotation.Target; @Target(ElementType.METHOD) public @interface Property { String uri(); + int minOccurs() default 0; + int maxOccurs() default Integer.MAX_VALUE; } diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/configuration/PropertyType.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/configuration/PropertyType.java index ef840517d..56a38b364 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/configuration/PropertyType.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/configuration/PropertyType.java @@ -11,7 +11,6 @@ import java.lang.reflect.Method; import org.apache.jena.datatypes.RDFDatatype; import org.apache.jena.datatypes.xsd.impl.RDFLangString; import org.apache.jena.rdf.model.Literal; -import org.apache.jena.rdf.model.Property; import org.apache.jena.rdf.model.RDFNode; import org.apache.jena.rdf.model.Statement; @@ -26,38 +25,41 @@ public enum PropertyType { RESOURCE { @Override public PropertyStatement buildPropertyStatement(Statement s) { - return new ResourcePropertyStatement(s.getPredicate(), s + return new ResourcePropertyStatement(s.getPredicate().getURI(), s .getObject().asResource().getURI()); } @Override - protected PropertyMethod buildPropertyMethod(Method method) { - return new ResourcePropertyMethod(method); + protected PropertyMethod buildPropertyMethod(Method method, + Property annotation) { + return new ResourcePropertyMethod(method, annotation); } }, STRING { @Override public PropertyStatement buildPropertyStatement(Statement s) { - return new StringPropertyStatement(s.getPredicate(), s.getObject() - .asLiteral().getString()); + return new StringPropertyStatement(s.getPredicate().getURI(), s + .getObject().asLiteral().getString()); } @Override - protected PropertyMethod buildPropertyMethod(Method method) { - return new StringPropertyMethod(method); + protected PropertyMethod buildPropertyMethod(Method method, + Property annotation) { + return new StringPropertyMethod(method, annotation); } }, FLOAT { @Override public PropertyStatement buildPropertyStatement(Statement s) { - return new FloatPropertyStatement(s.getPredicate(), s.getObject() - .asLiteral().getFloat()); + return new FloatPropertyStatement(s.getPredicate().getURI(), s + .getObject().asLiteral().getFloat()); } @Override - protected PropertyMethod buildPropertyMethod(Method method) { - return new FloatPropertyMethod(method); + protected PropertyMethod buildPropertyMethod(Method method, + Property annotation) { + return new FloatPropertyMethod(method, annotation); } }; @@ -101,24 +103,25 @@ public enum PropertyType { return type.buildPropertyStatement(s); } - public static PropertyMethod createPropertyMethod(Method method) - throws PropertyTypeException { + public static PropertyMethod createPropertyMethod(Method method, + Property annotation) throws PropertyTypeException { Class parameterType = method.getParameterTypes()[0]; PropertyType type = PropertyType.typeForParameterType(parameterType); - return type.buildPropertyMethod(method); + return type.buildPropertyMethod(method, annotation); } protected abstract PropertyStatement buildPropertyStatement(Statement s); - protected abstract PropertyMethod buildPropertyMethod(Method method); + protected abstract PropertyMethod buildPropertyMethod(Method method, + Property annotation); public static abstract class PropertyStatement { private final PropertyType type; private final String predicateUri; - public PropertyStatement(PropertyType type, Property predicate) { + public PropertyStatement(PropertyType type, String predicateUri) { this.type = type; - this.predicateUri = predicate.getURI(); + this.predicateUri = predicateUri; } public PropertyType getType() { @@ -135,8 +138,8 @@ public enum PropertyType { public static class ResourcePropertyStatement extends PropertyStatement { private final String objectUri; - public ResourcePropertyStatement(Property predicate, String objectUri) { - super(RESOURCE, predicate); + public ResourcePropertyStatement(String predicateUri, String objectUri) { + super(RESOURCE, predicateUri); this.objectUri = objectUri; } @@ -149,8 +152,8 @@ public enum PropertyType { public static class StringPropertyStatement extends PropertyStatement { private final String string; - public StringPropertyStatement(Property predicate, String string) { - super(STRING, predicate); + public StringPropertyStatement(String predicateUri, String string) { + super(STRING, predicateUri); this.string = string; } @@ -163,8 +166,8 @@ public enum PropertyType { public static class FloatPropertyStatement extends PropertyStatement { private final float f; - public FloatPropertyStatement(Property predicate, float f) { - super(FLOAT, predicate); + public FloatPropertyStatement(String predicateUri, float f) { + super(FLOAT, predicateUri); this.f = f; } @@ -177,10 +180,23 @@ public enum PropertyType { public static abstract class PropertyMethod { protected final PropertyType type; protected final Method method; + protected final String propertyUri; + protected final int minOccurs; + protected final int maxOccurs; - public PropertyMethod(PropertyType type, Method method) { + // Add cardinality values here! Final, with getters. + public PropertyMethod(PropertyType type, Method method, + Property annotation) { this.type = type; this.method = method; + this.propertyUri = annotation.uri(); + this.minOccurs = annotation.minOccurs(); + this.maxOccurs = annotation.maxOccurs(); + checkCardinalityBounds(); + } + + private void checkCardinalityBounds() { + // This is where we check for negative values or out of order. } public Method getMethod() { @@ -191,6 +207,18 @@ public enum PropertyType { return method.getParameterTypes()[0]; } + public String getPropertyUri() { + return propertyUri; + } + + public int getMinOccurs() { + return minOccurs; + } + + public int getMaxOccurs() { + return maxOccurs; + } + public void confirmCompatible(PropertyStatement ps) throws PropertyTypeException { if (type != ps.getType()) { @@ -213,20 +241,20 @@ public enum PropertyType { } public static class ResourcePropertyMethod extends PropertyMethod { - public ResourcePropertyMethod(Method method) { - super(RESOURCE, method); + public ResourcePropertyMethod(Method method, Property annotation) { + super(RESOURCE, method, annotation); } } public static class StringPropertyMethod extends PropertyMethod { - public StringPropertyMethod(Method method) { - super(STRING, method); + public StringPropertyMethod(Method method, Property annotation) { + super(STRING, method, annotation); } } public static class FloatPropertyMethod extends PropertyMethod { - public FloatPropertyMethod(Method method) { - super(FLOAT, method); + public FloatPropertyMethod(Method method, Property annotation) { + super(FLOAT, method, annotation); } } diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/configuration/README.md b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/configuration/README.md new file mode 100644 index 000000000..68da421fa --- /dev/null +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/configuration/README.md @@ -0,0 +1,174 @@ +# package edu.cornell.mannlib.vitro.webapp.utils.configuration; +## Overview +### Purpose +This package consists of `ConfigurationBeanLoader` and associated classes. +`ConfigurationBeanLoader` will instantiate and populate objects according to a +description encoded in the triples of an RDF Graph, +and annotations within the Java class of the instantiated object. + +The description must include + ++ the URI of exactly one concrete Java class, from which the instance will be created. + +The description may also include + ++ URIs of Java interfaces which the concrete class implements. +The description may be use to satisfy a request +for any of those interfaces, as well as a request for the concrete class. + ++ Data properties. These will be passed to "property methods" in the instance +as part of the creation/initialization. The data value must be an untyped +literal (String) or a numeric literal (Float). + ++ Object properties. The URI is assumed to be that of another loader description. +The loader will attempt to instantiate the described object, and pass it to +the appropriate property method on the original instance. The result may be a +network of instances, nested to an arbitrary level. + +The loader also recognizes two special interfaces: `RequestModelsUser` and `ContextModelsUser`. +If a created instance implements these interfaces, +the loader will provide the appropriate `ModelAccess` object, +allowing the instance to access the Vitro triple-stores. + +### Examples of use + +#### ApplicationSetup + +When Vitro starts up, `ApplicationSetup` uses a `ConfigurationBeanLoader` to instantiate the Vitro's component modules. +The loader creates an RDF Graph from the file `applicationSetup.n3` and instantiates a `SearchEngine` instance, +a `FileStorage` instance, etc. + +Here is some RDF that might be used by `ApplicationSetup`: + + @prefix : . + :application + a , + ; + :hasSearchEngine :instrumentedSearchEngineWrapper ; + :hasFileStorage :ptiFileStorage . + + :ptiFileStorage + a , + . + + :instrumentedSearchEngineWrapper + a , + ; + :wraps :solrSearchEngine . + + :solrSearchEngine + a , + . + +In this case, the `ConfigurationBeanLoader` would be asked to load all instances of +`edu.cornell.mannlib.vitro.webapp.modules.Application`. +The application individual is declared to be both an `Application` and an `ApplicationImpl`. +This is valid because `Application` is an interface, and `ApplicationImpl` implements that interface. +An instance of `ApplicationImpl` will be created. + +The application instance has two child objects: a `SearchEngine` and a `FileStorage`. +These objects will also be created, and calls will be made to the application's "property methods" (see below). + +The `SearchEngine` in turn has a child object, so that also will be created, and provided to the `SearchEngine`. + +#### SearchIndexer + +When Vitro's `SearchIndexer` is initialized, it uses a `ConfigurationBeanLoader` to create +lists of `SearchIndexExcluder`s, `DocumentModifier`s, and `IndexingUriFinder`s. +Descriptions of these are taken from Vitro's display model. + +## Specifications + +### ConfigurationBeanLoader +The principal methods are: + ++ `public T loadInstance(String uri, Class resultClass) throws ConfigurationBeanLoaderException` + + Search the graph for triples that describe the `uri`. + If the description indicates that the individual is of type `resultClass`, create an instance and populate it. + Return a reference to the created instance. Throw an exception if the `uri` does not exist in the graph, + or if the description does not correctly describe an individual. + + The `resultClass` may be an interface. In that case, each individual must also have a type statement that refers + to a concrete class that satisfies the interface. An instance of the concrete class will be created. + ++ `public Set loadAll(Class resultClass) throws ConfigurationBeanLoaderException` + + Search the graph for all individuals of type `resultClass`. For each such individual, call `loadInstance`. + Return a set containing the created instances. If no individuals are found, return an empty `Set`. + +### Restrictions on instantiated classes. +Each class to be instantiated must have a niladic constructor. + +### Property methods +When the loader encounters a data property or an object property in a description, +it will look in the instantiated class for a method tagged with the +`edu.cornell.mannlib.vitro.webapp.utils.configuration.Property` annotation. + +For example: + + @Property( + uri = "http://vitro.mannlib.cornell.edu/ns/vitro/ApplicationSetup#hasConfigurationTripleSource" + minOccurs = 1, + maxOccurs = 3) + public void setTBoxReasonerModule(TBoxReasonerModule module) { + this.module = module; + } + + +In more detail: + ++ A class must contain exactly one method that serves each property URI in the description. ++ The description need not include properies for all of the property methods in the class. ++ Each property method must be public, must have exactly one parameter, and must return null. ++ The name of the property method is immaterial, except that there must not be another method +with the same name in the class. ++ Property methods in superclasses will be recognized and accepted, but they may not be +overridden in a subclass. ++ If `minOccurs` is omitted, the default is `0`. If `minOccurs` is provided, it must be non-negative. ++ If `maxOccurs` is omitted, the default is `MAXINT`. If `maxOccurs` is provided, it must not be less than `minOccurs`. + +When instantiating: + ++ The parameter on a property method must match the value supplied in the RDF description. + + If the type of the parameter is `Float`, the object of the triple must be a numeric literal, or +an untyped literal that can be parsed as a number. + + If the type of the parameter is `String`, the object of the triple must be a String literal or an untyped literal. + + If the type of the parameter is another class, then the object of the triple must be the URI of +another RDF description, from which the loader can create an instance of the required class. ++ The number of values for a given property URI must not be less than the `minOccurs` value on the corresponding property method. ++ The number of values for a given property URI must not be greater than the `maxOccurs` value on the corresponding property method. + +### Validation methods +When the loader has satisfied all of the properties in an instance, it will +look in the instantiated class for any methods tagged with the +`edu.cornell.mannlib.vitro.webapp.utils.configuration.Validation` annotation. + +For example: + + @Validation + public void validate() throws Exception { + if (baseUri == null) { + throw new IllegalStateException( + "Configuration did not include a BaseURI."); + } + } + +Each such method will be called by the loader, and provides a opportunity to +confirm that the bean has been properly initialized. + +Again, in detail: + ++ Each validation method must be public, must accept no parameters, and must return null. ++ The name of the validation method is immaterial, except that there must not be another ++ method with the same name in the lass. ++ Validation methods in superclasses will be called, but may not be overridden in a subclass. + +### Life cycle +For each instance that the loader creates, the loader will: + ++ Call the appropriate property method for each property in the description. +For object properties, this includes recursive calls to create subordinate objects. +The order of property method calls is undefined. ++ Call the validation methods on the class. The order of validation method calls is undefined. + +If any property method or validation method throws an exception, the process stops, +and the exception is propagated upward. \ No newline at end of file diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/configuration/WrappedInstance.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/configuration/WrappedInstance.java index ced8285bb..4b739c6ec 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/configuration/WrappedInstance.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/configuration/WrappedInstance.java @@ -5,6 +5,7 @@ package edu.cornell.mannlib.vitro.webapp.utils.configuration; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.Collection; +import java.util.HashMap; import java.util.Map; import java.util.Set; @@ -63,6 +64,45 @@ public class WrappedInstance { } } + /** + * The loader provides the distilled property statements from the RDF. Check + * that they satisfy the cardinality requested on their methods. + */ + public void checkCardinality(Set propertyStatements) + throws CardinalityException { + Map statementCounts = countPropertyStatementsByPredicateUri(propertyStatements); + for (PropertyMethod pm : propertyMethods.values()) { + Integer c = statementCounts.get(pm.getPropertyUri()); + int count = (c == null) ? 0 : c; + if (count < pm.getMinOccurs()) { + throw new CardinalityException("Expecting at least " + + pm.getMinOccurs() + " values for '" + + pm.getPropertyUri() + "', but found " + count + "."); + } + if (count > pm.getMaxOccurs()) { + throw new CardinalityException("Expecting no more than " + + pm.getMaxOccurs() + " values for '" + + pm.getPropertyUri() + "', but found " + count + "."); + } + } + statementCounts.hashCode(); + } + + private Map countPropertyStatementsByPredicateUri( + Set propertyStatements) { + Map statementCounts = new HashMap<>(); + for (String pmPredicateUri : propertyMethods.keySet()) { + int count = 0; + for (PropertyStatement ps : propertyStatements) { + if (ps.getPredicateUri().equals(pmPredicateUri)) { + count++; + } + } + statementCounts.put(pmPredicateUri, count); + } + return statementCounts; + } + /** * The loader provides the distilled property statements from the RDF, to * populate the instance. @@ -76,7 +116,6 @@ public class WrappedInstance { if (pm == null) { throw new NoSuchPropertyMethodException(ps); } - pm.confirmCompatible(ps); if (ps instanceof ResourcePropertyStatement) { @@ -132,4 +171,10 @@ public class WrappedInstance { } } + public static class CardinalityException extends Exception { + public CardinalityException(String message) { + super(message); + } + } + } \ No newline at end of file diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/dataGetter/DataGetterBase.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/dataGetter/DataGetterBase.java index 5545534ac..2f34b810e 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/dataGetter/DataGetterBase.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/dataGetter/DataGetterBase.java @@ -5,7 +5,7 @@ import static edu.cornell.mannlib.vitro.webapp.modelaccess.ModelNames.DISPLAY; import javax.servlet.ServletContext; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.apache.jena.rdf.model.Model; diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/dataGetter/DataGetterUtils.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/dataGetter/DataGetterUtils.java index b83c366fc..53e6ccea9 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/dataGetter/DataGetterUtils.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/dataGetter/DataGetterUtils.java @@ -10,7 +10,7 @@ import java.util.Map; import javax.servlet.http.HttpServletRequest; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.json.JSONObject; diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/dataGetter/IndividualsForClassesDataGetter.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/dataGetter/IndividualsForClassesDataGetter.java index 782e928db..27ef8f9de 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/dataGetter/IndividualsForClassesDataGetter.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/dataGetter/IndividualsForClassesDataGetter.java @@ -11,7 +11,7 @@ import java.util.Map; import javax.servlet.ServletContext; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.json.JSONObject; diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/dataGetter/SearchIndividualsDataGetter.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/dataGetter/SearchIndividualsDataGetter.java index 739b9f309..b0bfed510 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/dataGetter/SearchIndividualsDataGetter.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/dataGetter/SearchIndividualsDataGetter.java @@ -177,7 +177,7 @@ public class SearchIndividualsDataGetter extends DataGetterBase implements DataG vclass.getURI(), page, alpha, - vreq.getWebappDaoFactory().getIndividualDao()); + vreq); body.putAll(vcResults.asFreemarkerMap()); List inds = vcResults.getEntities(); diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/developer/loggers/StackTraceUtility.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/developer/loggers/StackTraceUtility.java index ac63f22df..13b7e6358 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/developer/loggers/StackTraceUtility.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/developer/loggers/StackTraceUtility.java @@ -9,7 +9,7 @@ import java.util.List; import java.util.ListIterator; import java.util.regex.Pattern; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/logging/ToString.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/logging/ToString.java index 223206ad8..29fcb8ac6 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/logging/ToString.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/logging/ToString.java @@ -10,7 +10,7 @@ import java.util.List; import java.util.Map.Entry; import java.util.Set; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.apache.jena.graph.Graph; import org.apache.jena.graph.compose.Polyadic; diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/searchengine/AutoCompleteWords.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/searchengine/AutoCompleteWords.java index 0027c973d..2748f4758 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/searchengine/AutoCompleteWords.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/searchengine/AutoCompleteWords.java @@ -6,7 +6,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/searchengine/SearchQueryUtils.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/searchengine/SearchQueryUtils.java index 3c61a06fa..2abf76471 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/searchengine/SearchQueryUtils.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/searchengine/SearchQueryUtils.java @@ -7,7 +7,7 @@ import java.util.Collection; import java.util.List; import java.util.Map; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/sparql/RdfServiceQueryContext.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/sparql/RdfServiceQueryContext.java deleted file mode 100644 index e27f3ca9f..000000000 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/sparql/RdfServiceQueryContext.java +++ /dev/null @@ -1,125 +0,0 @@ -/* $This file is distributed under the terms of the license in /doc/license.txt$ */ - -package edu.cornell.mannlib.vitro.webapp.utils.sparql; - -import java.util.Arrays; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.Map; -import java.util.Set; - -import org.apache.commons.lang.StringUtils; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -import org.apache.jena.query.QuerySolution; -import org.apache.jena.query.ResultSet; -import org.apache.jena.rdf.model.RDFNode; - -import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFService; -import edu.cornell.mannlib.vitro.webapp.rdfservice.impl.RDFServiceUtils; -import edu.cornell.mannlib.vitro.webapp.utils.sparql.SelectQueryRunner.ExecutingSelectQueryContext; -import edu.cornell.mannlib.vitro.webapp.utils.sparql.SelectQueryRunner.SelectQueryContext; -import edu.cornell.mannlib.vitro.webapp.utils.sparql.SelectQueryRunner.StringResultsMapping; -import edu.cornell.mannlib.vitro.webapp.utils.sparql.SelectQueryRunner.StringResultsMappingImpl; - -/** - * An implementation of QueryContext based on an RDFService. - * - * Package access. Instances should be created only by SelectQueryRunner, or by - * a method on this class. - */ -class RdfServiceQueryContext implements SelectQueryContext { - private static final Log log = LogFactory - .getLog(RdfServiceQueryContext.class); - - private final RDFService rdfService; - private final SelectQueryHolder query; - - RdfServiceQueryContext(RDFService rdfService, SelectQueryHolder query) { - this.rdfService = rdfService; - this.query = query; - } - - @Override - public RdfServiceQueryContext bindVariableToUri(String name, String uri) { - return new RdfServiceQueryContext(rdfService, - query.bindToUri(name, uri)); - } - - @Override - public ExecutingSelectQueryContext execute() { - return new RdfServiceExecutingQueryContext(rdfService, query); - } - - private static class RdfServiceExecutingQueryContext implements - ExecutingSelectQueryContext { - private final RDFService rdfService; - private final SelectQueryHolder query; - - public RdfServiceExecutingQueryContext(RDFService rdfService, - SelectQueryHolder query) { - this.rdfService = rdfService; - this.query = query; - } - - @Override - public StringResultsMapping getStringFields(String... names) { - Set fieldNames = new HashSet<>(Arrays.asList(names)); - StringResultsMappingImpl mapping = new StringResultsMappingImpl(); - try { - ResultSet results = RDFServiceUtils.sparqlSelectQuery( - query.getQueryString(), rdfService); - return mapResultsForQuery(results, fieldNames); - } catch (Exception e) { - log.error( - "problem while running query '" - + query.getQueryString() + "'", e); - } - return mapping; - } - - private StringResultsMapping mapResultsForQuery(ResultSet results, - Set fieldNames) { - StringResultsMappingImpl mapping = new StringResultsMappingImpl(); - while (results.hasNext()) { - Map rowMapping = mapResultsForRow( - results.nextSolution(), fieldNames); - if (!rowMapping.isEmpty()) { - mapping.add(rowMapping); - } - } - return mapping; - } - - private Map mapResultsForRow(QuerySolution row, - Set fieldNames) { - Map map = new HashMap<>(); - for (Iterator names = row.varNames(); names.hasNext();) { - String name = names.next(); - RDFNode node = row.get(name); - String text = getTextForNode(node); - if (StringUtils.isNotBlank(text)) { - map.put(name, text); - } - } - if (!fieldNames.isEmpty()) { - map.keySet().retainAll(fieldNames); - } - return map; - } - - private String getTextForNode(RDFNode node) { - if (node == null) { - return ""; - } else if (node.isLiteral()) { - return node.asLiteral().getString().trim(); - } else { - return node.toString().trim(); - } - } - - } - -} diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/sparql/SelectQueryHolder.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/sparql/SelectQueryHolder.java deleted file mode 100644 index d59ce1bea..000000000 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/sparql/SelectQueryHolder.java +++ /dev/null @@ -1,36 +0,0 @@ -/* $This file is distributed under the terms of the license in /doc/license.txt$ */ - -package edu.cornell.mannlib.vitro.webapp.utils.sparql; - -import java.util.regex.Pattern; - -/** - * Holds the text of a SPARQL Select query, and allows you to perform some lexical - * operations on it. - * - * This is immutable, so don't forget to get the result of the operations. - */ -public class SelectQueryHolder { - private final String queryString; - - public SelectQueryHolder(String queryString) { - this.queryString = queryString; - } - - public String getQueryString() { - return queryString; - } - - public boolean hasVariable(String name) { - String regex = "\\?" + name + "\\b"; - return Pattern.compile(regex).matcher(queryString).find(); - } - - public SelectQueryHolder bindToUri(String name, String uri) { - String regex = "\\?" + name + "\\b"; - String replacement = "<" + uri + ">"; - String bound = queryString.replaceAll(regex, replacement); - return new SelectQueryHolder(bound); - } - -} diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/sparql/SelectQueryRunner.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/sparql/SelectQueryRunner.java deleted file mode 100644 index ad640cc5d..000000000 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/sparql/SelectQueryRunner.java +++ /dev/null @@ -1,104 +0,0 @@ -/* $This file is distributed under the terms of the license in /doc/license.txt$ */ - -package edu.cornell.mannlib.vitro.webapp.utils.sparql; - -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFService; - -/** - * A conversational tool for handling SPARQL queries. - * - * {@code - * Examples: - * List values = createQueryContext(rdfService, queryString) - * .bindVariableToUri("uri", uri) - * .execute() - * .getStringFields("partner") - * .flatten(); - * - * SelectQueryHolder q = selectQuery(queryString) - * .bindToUri("uri", uri)); - * List map = createQueryContext(rdfService, q) - * .execute() - * .getStringFields(); - * } - * - * The execute() method does not actually execute the query: it merely sets it - * up syntactically. - * - * If you don't supply any field names to getStringFields(), you get all of - * them. - * - * Any string value that returns a blank or empty string is omitted from the - * results. Any row that returns no values is omitted from the results. - */ -public final class SelectQueryRunner { - private static final Log log = LogFactory.getLog(SelectQueryRunner.class); - - private SelectQueryRunner() { - // No need to create an instance. - } - - public static SelectQueryHolder selectQuery(String queryString) { - return new SelectQueryHolder(queryString); - } - - public static SelectQueryContext createQueryContext(RDFService rdfService, - String queryString) { - return createQueryContext(rdfService, selectQuery(queryString)); - } - - public static SelectQueryContext createQueryContext(RDFService rdfService, - SelectQueryHolder query) { - return new RdfServiceQueryContext(rdfService, query); - } - - public static interface SelectQueryContext { - public SelectQueryContext bindVariableToUri(String name, String uri); - - public ExecutingSelectQueryContext execute(); - } - - public static interface ExecutingSelectQueryContext { - public StringResultsMapping getStringFields(String... fieldNames); - } - - public static interface StringResultsMapping extends - List> { - public List flatten(); - - public Set flattenToSet(); - } - - // ---------------------------------------------------------------------- - // Helper classes - // ---------------------------------------------------------------------- - - static class StringResultsMappingImpl extends - ArrayList> implements StringResultsMapping { - - @Override - public List flatten() { - List flat = new ArrayList<>(); - for (Map map : this) { - flat.addAll(map.values()); - } - return flat; - } - - @Override - public Set flattenToSet() { - return new HashSet<>(flatten()); - } - - } - -} diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/SparqlQueryUtils.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/sparql/SparqlQueryUtils.java similarity index 97% rename from api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/SparqlQueryUtils.java rename to api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/sparql/SparqlQueryUtils.java index 651932223..cf886b5c0 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/SparqlQueryUtils.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/sparql/SparqlQueryUtils.java @@ -1,6 +1,6 @@ /* $This file is distributed under the terms of the license in /doc/license.txt$ */ -package edu.cornell.mannlib.vitro.webapp.utils; +package edu.cornell.mannlib.vitro.webapp.utils.sparql; import java.util.Arrays; import java.util.Iterator; diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/sparqlrunner/ModelConstructQueryContext.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/sparqlrunner/ModelConstructQueryContext.java new file mode 100644 index 000000000..2881fee57 --- /dev/null +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/sparqlrunner/ModelConstructQueryContext.java @@ -0,0 +1,87 @@ +/* $This file is distributed under the terms of the license in /doc/license.txt$ */ + +package edu.cornell.mannlib.vitro.webapp.utils.sparqlrunner; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.jena.query.Query; +import org.apache.jena.query.QueryExecution; +import org.apache.jena.query.QueryExecutionFactory; +import org.apache.jena.query.QueryFactory; +import org.apache.jena.query.Syntax; +import org.apache.jena.rdf.model.Model; +import org.apache.jena.rdf.model.ModelFactory; + +import edu.cornell.mannlib.vitro.webapp.utils.sparqlrunner.SparqlQueryRunner.ConstructQueryContext; +import edu.cornell.mannlib.vitro.webapp.utils.sparqlrunner.SparqlQueryRunner.ExecutingConstructQueryContext; + +/** + * TODO + */ +public class ModelConstructQueryContext implements ConstructQueryContext { + private static final Log log = LogFactory + .getLog(ModelConstructQueryContext.class); + + private static final Syntax SYNTAX = Syntax.syntaxARQ; + + private final Model model; + private final QueryHolder query; + + public ModelConstructQueryContext(Model model, QueryHolder query) { + this.model = model; + this.query = query; + } + + @Override + public ConstructQueryContext bindVariableToUri(String name, String uri) { + return new ModelConstructQueryContext(model, query.bindToUri(name, uri)); + } + + @Override + public ConstructQueryContext bindVariableToPlainLiteral(String name, + String value) { + return new ModelConstructQueryContext(model, query.bindToPlainLiteral( + name, value)); + } + + @Override + public String toString() { + return "ModelConstructQueryContext[query=" + query + "]"; + } + + @Override + public ExecutingConstructQueryContext execute() { + return new ModelExecutingConstructQueryContext(model, query); + } + + private static class ModelExecutingConstructQueryContext implements + ExecutingConstructQueryContext { + private final Model model; + private final QueryHolder query; + + public ModelExecutingConstructQueryContext(Model model, QueryHolder query) { + this.model = model; + this.query = query; + } + + @Override + public Model toModel() { + QueryExecution qe = null; + try { + Query q = QueryFactory.create(query.getQueryString(), SYNTAX); + qe = QueryExecutionFactory.create(q, model); + return qe.execConstruct(); + } catch (Exception e) { + log.error( + "problem while running query '" + + query.getQueryString() + "'", e); + return ModelFactory.createDefaultModel(); + } finally { + if (qe != null) { + qe.close(); + } + } + } + + } +} diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/sparqlrunner/ModelSelectQueryContext.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/sparqlrunner/ModelSelectQueryContext.java new file mode 100644 index 000000000..5de518aff --- /dev/null +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/sparqlrunner/ModelSelectQueryContext.java @@ -0,0 +1,129 @@ +/* $This file is distributed under the terms of the license in /doc/license.txt$ */ + +package edu.cornell.mannlib.vitro.webapp.utils.sparqlrunner; + +import java.io.OutputStream; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.jena.query.Query; +import org.apache.jena.query.QueryExecution; +import org.apache.jena.query.QueryExecutionFactory; +import org.apache.jena.query.QueryFactory; +import org.apache.jena.query.ResultSet; +import org.apache.jena.query.ResultSetFormatter; +import org.apache.jena.query.Syntax; +import org.apache.jena.rdf.model.Model; + +import edu.cornell.mannlib.vitro.webapp.utils.sparqlrunner.SparqlQueryRunner.ExecutingSelectQueryContext; +import edu.cornell.mannlib.vitro.webapp.utils.sparqlrunner.SparqlQueryRunner.SelectQueryContext; + +/** + * An implementation of QueryContext based on a Model. + * + * Package access. Instances should be created only by SparqlQueryRunner, or by + * a method on this class. + */ +class ModelSelectQueryContext implements SelectQueryContext { + private static final Log log = LogFactory + .getLog(ModelSelectQueryContext.class); + + private static final Syntax SYNTAX = Syntax.syntaxARQ; + + private final Model model; + private final QueryHolder query; + + public ModelSelectQueryContext(Model model, QueryHolder query) { + this.model = model; + this.query = query; + } + + @Override + public ModelSelectQueryContext bindVariableToUri(String name, String uri) { + return new ModelSelectQueryContext(model, query.bindToUri(name, uri)); + } + + @Override + public ModelSelectQueryContext bindVariableToPlainLiteral(String name, + String value) { + return new ModelSelectQueryContext(model, query.bindToPlainLiteral( + name, value)); + } + + @Override + public String toString() { + return "ModelSelectQueryContext[query=" + query + "]"; + } + + @Override + public ExecutingSelectQueryContext execute() { + return new ModelExecutingQueryContext(model, query); + } + + private static class ModelExecutingQueryContext implements + ExecutingSelectQueryContext { + private final Model model; + private final QueryHolder query; + + public ModelExecutingQueryContext(Model model, QueryHolder query) { + this.model = model; + this.query = query; + } + + @Override + public StringResultsMapping toStringFields(String... names) { + String qString = query.getQueryString(); + Set fieldNames = new HashSet<>(Arrays.asList(names)); + try { + Query q = QueryFactory.create(qString, SYNTAX); + QueryExecution qexec = QueryExecutionFactory.create(q, model); + try { + ResultSet results = qexec.execSelect(); + return new StringResultsMapping(results, fieldNames); + } finally { + qexec.close(); + } + } catch (Exception e) { + log.error("problem while running query '" + qString + "'", e); + return StringResultsMapping.EMPTY; + } + } + + @Override + public T parse(ResultSetParser parser) { + String qString = query.getQueryString(); + try { + Query q = QueryFactory.create(qString, SYNTAX); + QueryExecution qexec = QueryExecutionFactory.create(q, model); + try { + return parser.parseResults(qString, qexec.execSelect()); + } finally { + qexec.close(); + } + } catch (Exception e) { + log.error("problem while running query '" + qString + "'", e); + return parser.defaultValue(); + } + } + + @Override + public void writeToOutput(OutputStream output) { + String qString = query.getQueryString(); + try { + Query q = QueryFactory.create(qString, SYNTAX); + QueryExecution qexec = QueryExecutionFactory.create(q, model); + try { + ResultSetFormatter.outputAsJSON(output, qexec.execSelect()); + } finally { + qexec.close(); + } + } catch (Exception e) { + log.error("problem while running query '" + qString + "'", e); + } + } + + } +} diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/sparqlrunner/QueryHolder.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/sparqlrunner/QueryHolder.java new file mode 100644 index 000000000..031542b07 --- /dev/null +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/sparqlrunner/QueryHolder.java @@ -0,0 +1,79 @@ +/* $This file is distributed under the terms of the license in /doc/license.txt$ */ + +package edu.cornell.mannlib.vitro.webapp.utils.sparqlrunner; + +import java.util.Objects; +import java.util.regex.Pattern; + +/** + * Holds the text of a SPARQL query, and allows you to perform some lexical + * operations on it. + * + * This is immutable, so don't forget to get the result of the operations. + */ +public class QueryHolder { + private final String queryString; + + public QueryHolder(String queryString) { + this.queryString = Objects.requireNonNull(queryString); + } + + public String getQueryString() { + return queryString; + } + + public boolean hasVariable(String name) { + String regex = "\\?" + name + "\\b"; + return Pattern.compile(regex).matcher(queryString).find(); + } + + public QueryHolder bindToUri(String name, String uri) { + String regex = "\\?" + name + "\\b"; + String replacement = "<" + uri + ">"; + String bound = replaceWithinBraces(regex, replacement); + return new QueryHolder(bound); + } + + public QueryHolder bindToPlainLiteral(String name, String value) { + String regex = "\\?" + name + "\\b"; + String replacement = '"' + value + '"'; + String bound = replaceWithinBraces(regex, replacement); + return new QueryHolder(bound); + } + + private String replaceWithinBraces(String regex, String replacement) { + int openBrace = queryString.indexOf('{'); + int closeBrace = queryString.lastIndexOf('}'); + if (openBrace == -1 || closeBrace == -1) { + return queryString; + } else { + String prefix = queryString.substring(0, openBrace); + String suffix = queryString.substring(closeBrace); + String patterns = queryString.substring(openBrace, closeBrace); + return prefix + patterns.replaceAll(regex, replacement) + suffix; + } + } + + @Override + public int hashCode() { + return queryString.hashCode(); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } else if (obj == null) { + return false; + } else if (getClass() != obj.getClass()) { + return false; + } + QueryHolder that = (QueryHolder) obj; + return Objects.equals(this.queryString, that.queryString); + } + + @Override + public String toString() { + return "QueryHolder[" + queryString + "]"; + } +} diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/sparqlrunner/README.md b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/sparqlrunner/README.md new file mode 100644 index 000000000..9d19b3ec9 --- /dev/null +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/sparqlrunner/README.md @@ -0,0 +1,130 @@ +# A conversational API for running SPARQL queries + +`edu.cornell.mannlib.vitro.webapp.utils.sparqlrunner` provides a syntactically simple +way to manipulate and execute SPARQL queries. The query may be run against a Jena `Model` +or a Vitro `RDFService`. + +The API supports the most common tasks: + ++ bind URI values to variables in the query. ++ bind plain literal values to variables in the query. ++ execute a CONSTRUCT query and return a Jena model. ++ execute a SELECT query and return the results in a variety of formats: + + a List of String values + + a Set of String values + + a List of Maps of String values + + a JSON-formatted output stream + + the output of a custom parser class. + +The API could reasonably be extended to perform other operations, or to produce +additional forms of output. It currently provides only the functionality that +was immediately useful at the time of writing. + +## Usage Examples + +In general, the usage pattern is to + ++ create the query context ++ optionally bind values to variables in the query ++ execute ++ transform the results + +### SELECT to a List of Strings + +Run a SELECT query, binding a value to the variable `?uri` and returning +a List of the values that are returned for the variable `?partner`. + + List values = createSelectQueryContext(rdfService, queryString) + .bindVariableToUri("uri", uri) + .execute() + .toStringFields("partner") + .flatten(); + +### SELECT to a List of Maps + + Example: Run a SELECT query, returning a list of maps of strings. Each map + represents a record in the ResultSet, with variable names mapped to the + value in that record, translated to a String. + + List> maps = createSelectQueryContext(model, queryString) + .execute() + .toStringFields() + .getListOfMaps(); + +>_Note: Null or empty values are omitted from the maps; empty maps are omitted from the list._ + + +### SELECT to a Parser + + MyQueryResult mqr = createSelectQueryContext(model, queryString) + .bindVariableToUri("uri", uri) + .bindVariableToPlainLiteral("id", "PW-4250") + .execute() + .parse(new MyQueryParser()); + +### CONSTRUCT to a Model + + Model m = createConstructQueryContext(rdfService, queryString) + .execute() + .toModel(); + +## Using the QueryHolder + +The `QueryHolder` class can be useful in itself, for manipulating a query string. + +### Bind variables in a query + + String boundQuery = queryHolder(rawQuery) + .bindToUri("uri", uri) + .getQueryString(); + +### Inquire about unbound variables + + boolean foundIt = queryHolder(rawQuery) + .hasVariable("name"); + +### Prepare a query in advance of executing it. + QueryHolder qh = queryHolder(queryString) + .bindToUri("uri", uri)); + .bindVariableToPlainLiteral("id", "PW-4250") + List map = createSelectQueryContext(model, qh) + .execute() + .toStringFields() + .getListOfMaps(); + +## Parsing a ResultSet + +By writing a parser, you can translate the `ResultSet` from a SPARQL query +into any object, according to the translation algorithm you specify. You must +provide the parser with both a parsing method and a default value. The parser +will return the default value if the parsing method fails for any reason. In +this way, you are assured that the parser will not throw an exception. + +Here is an example of a simple parser. It inspects the first record in the +result set and builds an `ItemInfo` object. + + private static class ExpandProfileParser extends ResultSetParser { + @Override + protected ItemInfo defaultValue() { + return new ItemInfo(); + } + + @Override + protected ItemInfo parseResults(String queryStr, ResultSet results) { + ItemInfo item = new ItemInfo(); + if (results.hasNext()) { + QuerySolution solution = results.next(); + item.label = ifLiteralPresent(solution, "label", ""); + item.classLabel = ifLiteralPresent(solution, "classLabel", ""); + item.imageUrl = ifLiteralPresent(solution, "imageUrl", ""); + } + return item; + } + } + +This parser would be used in this way: + + ItemInfo info = createSelectQueryContext(model, queryString) + .execute() + .parse(new ExpandProfileParser()); + diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/sparqlrunner/RdfServiceConstructQueryContext.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/sparqlrunner/RdfServiceConstructQueryContext.java new file mode 100644 index 000000000..d3e0a7f19 --- /dev/null +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/sparqlrunner/RdfServiceConstructQueryContext.java @@ -0,0 +1,88 @@ +/* $This file is distributed under the terms of the license in /doc/license.txt$ */ + +package edu.cornell.mannlib.vitro.webapp.utils.sparqlrunner; + +import static edu.cornell.mannlib.vitro.webapp.rdfservice.RDFService.ModelSerializationFormat.NTRIPLE; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.jena.query.QueryExecution; +import org.apache.jena.rdf.model.Model; +import org.apache.jena.rdf.model.ModelFactory; + +import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFService; +import edu.cornell.mannlib.vitro.webapp.rdfservice.impl.RDFServiceUtils; +import edu.cornell.mannlib.vitro.webapp.utils.sparqlrunner.SparqlQueryRunner.ConstructQueryContext; +import edu.cornell.mannlib.vitro.webapp.utils.sparqlrunner.SparqlQueryRunner.ExecutingConstructQueryContext; + +/** + * TODO + */ +public class RdfServiceConstructQueryContext implements ConstructQueryContext { + private static final Log log = LogFactory + .getLog(RdfServiceConstructQueryContext.class); + + private final RDFService rdfService; + private final QueryHolder query; + + public RdfServiceConstructQueryContext(RDFService rdfService, + QueryHolder query) { + this.rdfService = rdfService; + this.query = query; + } + + @Override + public ConstructQueryContext bindVariableToUri(String name, String uri) { + return new RdfServiceConstructQueryContext(rdfService, query.bindToUri( + name, uri)); + } + + @Override + public ConstructQueryContext bindVariableToPlainLiteral(String name, + String value) { + return new RdfServiceConstructQueryContext(rdfService, + query.bindToPlainLiteral(name, value)); + } + + @Override + public ExecutingConstructQueryContext execute() { + return new RdfServiceExecutingConstructQueryContext(rdfService, query); + } + + @Override + public String toString() { + return "RdfServiceConstructQueryContext[query=" + query + "]"; + } + + private static class RdfServiceExecutingConstructQueryContext implements + ExecutingConstructQueryContext { + private final RDFService rdfService; + private final QueryHolder query; + + public RdfServiceExecutingConstructQueryContext(RDFService rdfService, + QueryHolder query) { + this.rdfService = rdfService; + this.query = query; + } + + @Override + public Model toModel() { + QueryExecution qe = null; + try { + return RDFServiceUtils.parseModel(rdfService + .sparqlConstructQuery(query.getQueryString(), NTRIPLE), + NTRIPLE); + } catch (Exception e) { + log.error( + "problem while running query '" + + query.getQueryString() + "'", e); + return ModelFactory.createDefaultModel(); + } finally { + if (qe != null) { + qe.close(); + } + } + } + + } +} diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/sparqlrunner/RdfServiceSelectQueryContext.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/sparqlrunner/RdfServiceSelectQueryContext.java new file mode 100644 index 000000000..b0a21052c --- /dev/null +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/sparqlrunner/RdfServiceSelectQueryContext.java @@ -0,0 +1,116 @@ +/* $This file is distributed under the terms of the license in /doc/license.txt$ */ + +package edu.cornell.mannlib.vitro.webapp.utils.sparqlrunner; + +import static edu.cornell.mannlib.vitro.webapp.rdfservice.RDFService.ResultFormat.JSON; + +import java.io.InputStream; +import java.io.OutputStream; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + +import org.apache.commons.io.IOUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.jena.query.ResultSet; + +import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFService; +import edu.cornell.mannlib.vitro.webapp.rdfservice.impl.RDFServiceUtils; +import edu.cornell.mannlib.vitro.webapp.utils.sparqlrunner.SparqlQueryRunner.ExecutingSelectQueryContext; +import edu.cornell.mannlib.vitro.webapp.utils.sparqlrunner.SparqlQueryRunner.SelectQueryContext; + +/** + * An implementation of QueryContext based on an RDFService. + * + * Package access. Instances should be created only by SparqlQueryRunner, or by + * a method on this class. + */ +class RdfServiceSelectQueryContext implements SelectQueryContext { + private static final Log log = LogFactory + .getLog(RdfServiceSelectQueryContext.class); + + private final RDFService rdfService; + private final QueryHolder query; + + RdfServiceSelectQueryContext(RDFService rdfService, QueryHolder query) { + this.rdfService = rdfService; + this.query = query; + } + + @Override + public RdfServiceSelectQueryContext bindVariableToUri(String name, + String uri) { + return new RdfServiceSelectQueryContext(rdfService, query.bindToUri( + name, uri)); + } + + @Override + public RdfServiceSelectQueryContext bindVariableToPlainLiteral(String name, + String value) { + return new RdfServiceSelectQueryContext(rdfService, + query.bindToPlainLiteral(name, value)); + } + + @Override + public String toString() { + return "RdfServiceSelectQueryContext[query=" + query + "]"; + } + + @Override + public ExecutingSelectQueryContext execute() { + return new RdfServiceExecutingQueryContext(rdfService, query); + } + + private static class RdfServiceExecutingQueryContext implements + ExecutingSelectQueryContext { + private final RDFService rdfService; + private final QueryHolder query; + + public RdfServiceExecutingQueryContext(RDFService rdfService, + QueryHolder query) { + this.rdfService = rdfService; + this.query = query; + } + + @Override + public StringResultsMapping toStringFields(String... names) { + Set fieldNames = new HashSet<>(Arrays.asList(names)); + try { + ResultSet results = RDFServiceUtils.sparqlSelectQuery( + query.getQueryString(), rdfService); + return new StringResultsMapping(results, fieldNames); + } catch (Exception e) { + log.error( + "problem while running query '" + + query.getQueryString() + "'", e); + return StringResultsMapping.EMPTY; + } + } + + @Override + public T parse(ResultSetParser parser) { + String qString = query.getQueryString(); + try { + return parser.parseResults(qString, + RDFServiceUtils.sparqlSelectQuery(qString, rdfService)); + } catch (Exception e) { + log.error("problem while running query '" + qString + "'", e); + return parser.defaultValue(); + } + } + + @Override + public void writeToOutput(OutputStream output) { + try { + InputStream resultStream = rdfService.sparqlSelectQuery( + query.getQueryString(), JSON); + IOUtils.copy(resultStream, output); + } catch (Exception e) { + log.error( + "problem while running query '" + + query.getQueryString() + "'", e); + } + } + } +} diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/sparqlrunner/ResultSetParser.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/sparqlrunner/ResultSetParser.java new file mode 100644 index 000000000..78e4bebb9 --- /dev/null +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/sparqlrunner/ResultSetParser.java @@ -0,0 +1,57 @@ +/* $This file is distributed under the terms of the license in /doc/license.txt$ */ + +package edu.cornell.mannlib.vitro.webapp.utils.sparqlrunner; + +import org.apache.jena.query.QuerySolution; +import org.apache.jena.query.ResultSet; +import org.apache.jena.rdf.model.Literal; +import org.apache.jena.rdf.model.RDFNode; + +/** + * Define the interface for parsing a result set, and provide some helpful + * methods as well. + */ +public abstract class ResultSetParser { + protected abstract T parseResults(String queryStr, ResultSet results); + + protected abstract T defaultValue(); + + protected String ifResourcePresent(QuerySolution solution, + String variableName, String defaultValue) { + RDFNode node = solution.get(variableName); + if (node == null || !node.isURIResource()) { + return defaultValue; + } + return node.asResource().getURI(); + } + + protected String ifLiteralPresent(QuerySolution solution, + String variableName, String defaultValue) { + Literal literal = solution.getLiteral(variableName); + if (literal == null) { + return defaultValue; + } else { + return literal.getString(); + } + } + + protected long ifLongPresent(QuerySolution solution, String variableName, + long defaultValue) { + Literal literal = solution.getLiteral(variableName); + if (literal == null) { + return defaultValue; + } else { + return literal.getLong(); + } + } + + protected int ifIntPresent(QuerySolution solution, String variableName, + int defaultValue) { + Literal literal = solution.getLiteral(variableName); + if (literal == null) { + return defaultValue; + } else { + return literal.getInt(); + } + } +} diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/sparqlrunner/SparqlQueryRunner.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/sparqlrunner/SparqlQueryRunner.java new file mode 100644 index 000000000..419ea7105 --- /dev/null +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/sparqlrunner/SparqlQueryRunner.java @@ -0,0 +1,127 @@ +/* $This file is distributed under the terms of the license in /doc/license.txt$ */ + +package edu.cornell.mannlib.vitro.webapp.utils.sparqlrunner; + +import java.io.OutputStream; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.jena.rdf.model.Model; + +import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFService; + +/** + * A conversational tool for handling SPARQL queries. + * + *
+ * Examples:
+ *   List values = createSelectQueryContext(rdfService, queryString)
+ *                             .bindVariableToUri("uri", uri)
+ * 				               .execute()
+ * 				               .toStringFields("partner")
+ * 				               .flatten();
+ * 
+ *   QueryHolder qh = queryHolder(queryString)
+ *                             .bindToUri("uri", uri));
+ *   List map = createSelectQueryContext(model, qh)
+ *                             .execute()
+ *                             .toStringFields();
+ * 
+ * + * The query context can come from either an RDFService or a Model. + * + * The execute() method does not actually execute the query: it merely sets it + * up syntactically. + * + * If you don't supply any field names to toStringFields(), you get all of + * them. + * + * Any string value that returns a blank or empty string is omitted from the + * results. Any row that returns no values is omitted from the results. + */ +public final class SparqlQueryRunner { + private static final Log log = LogFactory.getLog(SparqlQueryRunner.class); + + private SparqlQueryRunner() { + // No need to create an instance. + } + + public static QueryHolder queryHolder(String queryString) { + return new QueryHolder(queryString); + } + + // ------------- SELECT ----------- // + + public static SelectQueryContext createSelectQueryContext(RDFService rdfService, + String queryString) { + return createSelectQueryContext(rdfService, queryHolder(queryString)); + } + + public static SelectQueryContext createSelectQueryContext(RDFService rdfService, + QueryHolder query) { + return new RdfServiceSelectQueryContext(rdfService, query); + } + + public static SelectQueryContext createSelectQueryContext(Model model, + String queryString) { + return createSelectQueryContext(model, queryHolder(queryString)); + } + + public static SelectQueryContext createSelectQueryContext(Model model, + QueryHolder query) { + return new ModelSelectQueryContext(model, query); + } + + public static interface SelectQueryContext { + public SelectQueryContext bindVariableToUri(String name, String uri); + + public SelectQueryContext bindVariableToPlainLiteral(String name, + String value); + + public ExecutingSelectQueryContext execute(); + } + + public static interface ExecutingSelectQueryContext { + public StringResultsMapping toStringFields(String... fieldNames); + + public T parse(ResultSetParser parser); + + public void writeToOutput(OutputStream output); + } + + // ------------- CONSTRUCT ----------- // + + public static ConstructQueryContext createConstructQueryContext(RDFService rdfService, + String queryString) { + return createConstructQueryContext(rdfService, queryHolder(queryString)); + } + + public static ConstructQueryContext createConstructQueryContext(RDFService rdfService, + QueryHolder query) { + return new RdfServiceConstructQueryContext(rdfService, query); + } + + public static ConstructQueryContext createConstructQueryContext(Model model, + String queryString) { + return createConstructQueryContext(model, queryHolder(queryString)); + } + + public static ConstructQueryContext createConstructQueryContext(Model model, + QueryHolder query) { + return new ModelConstructQueryContext(model, query); + } + + public static interface ConstructQueryContext{ + public ConstructQueryContext bindVariableToUri(String name, String uri); + + public ConstructQueryContext bindVariableToPlainLiteral(String name, + String value); + + public ExecutingConstructQueryContext execute(); + } + + public static interface ExecutingConstructQueryContext { + public Model toModel(); + } + +} diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/sparqlrunner/StringResultsMapping.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/sparqlrunner/StringResultsMapping.java new file mode 100644 index 000000000..a9b9c1ade --- /dev/null +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/sparqlrunner/StringResultsMapping.java @@ -0,0 +1,95 @@ +/* $This file is distributed under the terms of the license in /doc/license.txt$ */ + +package edu.cornell.mannlib.vitro.webapp.utils.sparqlrunner; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.apache.commons.lang.StringUtils; +import org.apache.jena.query.QuerySolution; +import org.apache.jena.query.ResultSet; +import org.apache.jena.rdf.model.RDFNode; + +/** + * Parse the set of fieldNames against the results of the query, and make the + * extracted values available. + */ +public class StringResultsMapping { + public static final StringResultsMapping EMPTY = new StringResultsMapping(); + private final List> listOfMaps; + + public StringResultsMapping() { + this.listOfMaps = Collections.emptyList(); + } + + public StringResultsMapping(ResultSet results, Set fieldNames) { + this.listOfMaps = mapResultsForQuery(results, fieldNames); + } + + private List> mapResultsForQuery(ResultSet results, + Set fieldNames) { + List> mapping = new ArrayList<>(); + while (results.hasNext()) { + Map rowMapping = mapResultsForRow( + results.nextSolution(), fieldNames); + if (!rowMapping.isEmpty()) { + mapping.add(rowMapping); + } + } + return mapping; + } + + private Map mapResultsForRow(QuerySolution row, + Set fieldNames) { + Map map = new HashMap<>(); + for (Iterator names = row.varNames(); names.hasNext();) { + String name = names.next(); + RDFNode node = row.get(name); + String text = getTextForNode(node); + if (StringUtils.isNotBlank(text)) { + map.put(name, text); + } + } + if (!fieldNames.isEmpty()) { + map.keySet().retainAll(fieldNames); + } + return map; + } + + private String getTextForNode(RDFNode node) { + if (node == null) { + return ""; + } else if (node.isLiteral()) { + return node.asLiteral().getString().trim(); + } else { + return node.toString().trim(); + } + } + + public List> getListOfMaps() { + List> list = new ArrayList<>(); + for (Map map: listOfMaps) { + list.add(new HashMap<>(map)); + } + return list; + } + + public List flatten() { + List flat = new ArrayList<>(); + for (Map map : listOfMaps) { + flat.addAll(map.values()); + } + return flat; + } + + public Set flattenToSet() { + return new HashSet<>(flatten()); + } + +} diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/web/MiscWebUtils.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/web/MiscWebUtils.java index 743193c1e..81f1a6bf8 100755 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/web/MiscWebUtils.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/web/MiscWebUtils.java @@ -12,7 +12,7 @@ import javax.servlet.ServletRequest; import javax.servlet.http.HttpServletRequest; import javax.servlet.jsp.JspException; -import org.apache.commons.lang.StringEscapeUtils; +import org.apache.commons.lang3.StringEscapeUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -153,7 +153,7 @@ public class MiscWebUtils { Object obj = req.getAttribute(name); value = (obj instanceof Model || obj instanceof ModelCom) ? "[Jena model object]" : (obj == null) ? "[null]" : - StringEscapeUtils.escapeHtml(obj.toString()); + StringEscapeUtils.ESCAPE_HTML4.translate(obj.toString()); }catch(Exception ex){ value = "unable to get value" ; } catch (Error er){ @@ -177,7 +177,7 @@ public class MiscWebUtils { try{ Object obj = req.getParameter(name); value = (obj == null) ? "[null]" : - StringEscapeUtils.escapeHtml(obj.toString()); + StringEscapeUtils.ESCAPE_HTML4.translate(obj.toString()); }catch(Exception ex){ value = "unable to get value" ; } catch (Error er){ @@ -201,7 +201,7 @@ public class MiscWebUtils { Object obj = req.getSession().getAttribute(name); value = (obj instanceof Model || obj instanceof ModelCom) ? "[Jena model object]" : (obj == null) ? "[null]" : - StringEscapeUtils.escapeHtml(obj.toString()); + StringEscapeUtils.ESCAPE_HTML4.translate(obj.toString()); }catch(Exception ex){ value = "unable to get value" ; } catch (Error er){ diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/web/beanswrappers/ReadOnlyBeansWrapper.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/web/beanswrappers/ReadOnlyBeansWrapper.java index d31ff1d15..d0050d6c8 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/web/beanswrappers/ReadOnlyBeansWrapper.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/web/beanswrappers/ReadOnlyBeansWrapper.java @@ -4,6 +4,7 @@ package edu.cornell.mannlib.vitro.webapp.web.beanswrappers; import java.lang.reflect.Method; +import freemarker.ext.beans.MethodAppearanceFineTuner; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -26,35 +27,35 @@ public class ReadOnlyBeansWrapper extends BeansWrapper { public ReadOnlyBeansWrapper() { // Start by exposing all safe methods. setExposureLevel(EXPOSE_SAFE); - } - - @SuppressWarnings("rawtypes") - @Override - protected void finetuneMethodAppearance(Class cls, Method method, MethodAppearanceDecision decision) { - - // How to define a setter? This is a weak approximation: a method whose name - // starts with "set" or returns void. - if ( method.getName().startsWith("set") ) { - decision.setExposeMethodAs(null); - - } else if ( method.getReturnType().getName().equals("void") ) { - decision.setExposeMethodAs(null); - - } else { - - Class declaringClass = method.getDeclaringClass(); - if (declaringClass.equals(java.lang.Object.class)) { - decision.setExposeMethodAs(null); - - } else { - Package pkg = declaringClass.getPackage(); - if (pkg.getName().equals("java.util")) { - decision.setExposeMethodAs(null); + setMethodAppearanceFineTuner(new MethodAppearanceFineTuner() { + @Override + public void process(MethodAppearanceDecisionInput methodAppearanceDecisionInput, MethodAppearanceDecision methodAppearanceDecision) { + Method method = methodAppearanceDecisionInput.getMethod(); + // How to define a setter? This is a weak approximation: a method whose name + // starts with "set" or returns void. + if ( method.getName().startsWith("set") ) { + methodAppearanceDecision.setExposeMethodAs(null); + + } else if ( method.getReturnType().getName().equals("void") ) { + methodAppearanceDecision.setExposeMethodAs(null); + + } else { + + Class declaringClass = method.getDeclaringClass(); + if (declaringClass.equals(java.lang.Object.class)) { + methodAppearanceDecision.setExposeMethodAs(null); + + } else { + Package pkg = declaringClass.getPackage(); + if (pkg.getName().equals("java.util")) { + methodAppearanceDecision.setExposeMethodAs(null); + } + } } } - } + }); } - + // For exposing a method as a property (when it's not named getX or isX). Note that this is not // just a syntactic change in the template from X() to X, but also makes the value get precomputed. // private void exposeAsProperty(Method method, MethodAppearanceDecision decision) { diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/web/directives/WidgetDirective.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/web/directives/WidgetDirective.java index dff8eae6c..298a0be07 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/web/directives/WidgetDirective.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/web/directives/WidgetDirective.java @@ -11,7 +11,7 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Map; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/web/jsptags/OptionsForClassTag.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/web/jsptags/OptionsForClassTag.java index 3939a29c9..daa6ff814 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/web/jsptags/OptionsForClassTag.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/web/jsptags/OptionsForClassTag.java @@ -8,7 +8,7 @@ import edu.cornell.mannlib.vitro.webapp.beans.ObjectPropertyStatement; import edu.cornell.mannlib.vitro.webapp.beans.VClass; import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest; import edu.cornell.mannlib.vitro.webapp.dao.WebappDaoFactory; -import org.apache.commons.lang.StringEscapeUtils; +import org.apache.commons.lang3.StringEscapeUtils; import javax.servlet.http.HttpServletRequest; import javax.servlet.jsp.JspWriter; @@ -58,11 +58,11 @@ public class OptionsForClassTag extends TagSupport { for( Individual ind : individuals ){ String uri = ind.getURI() ; if( uri != null ){ - out.print(""); } diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/web/jsptags/OptionsForPropertyTag.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/web/jsptags/OptionsForPropertyTag.java index b3d90e8fb..261f42bdd 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/web/jsptags/OptionsForPropertyTag.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/web/jsptags/OptionsForPropertyTag.java @@ -14,7 +14,7 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.jsp.JspWriter; import javax.servlet.jsp.tagext.TagSupport; -import org.apache.commons.lang.StringEscapeUtils; +import org.apache.commons.lang3.StringEscapeUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -95,11 +95,11 @@ public class OptionsForPropertyTag extends TagSupport { for( Individual ind : individuals ){ String uri = ind.getURI() ; if( uri != null ){ - out.print(""); ++optionsCount; } diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/web/templatemodels/LinkTemplateModel.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/web/templatemodels/LinkTemplateModel.java index bab908947..edf9f9e2d 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/web/templatemodels/LinkTemplateModel.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/web/templatemodels/LinkTemplateModel.java @@ -2,7 +2,7 @@ package edu.cornell.mannlib.vitro.webapp.web.templatemodels; -import org.apache.commons.lang.StringEscapeUtils; +import org.apache.commons.lang3.StringEscapeUtils; import edu.cornell.mannlib.vitro.webapp.controller.freemarker.UrlBuilder; import edu.cornell.mannlib.vitro.webapp.controller.freemarker.UrlBuilder.ParamMap; @@ -42,7 +42,7 @@ public class LinkTemplateModel extends BaseTemplateModel { } protected void setText(String text) { - this.text = StringEscapeUtils.escapeHtml(text); + this.text = StringEscapeUtils.ESCAPE_HTML4.translate(text); } /* Template properties */ diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/web/templatemodels/Tags.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/web/templatemodels/Tags.java index ce1522f78..359b37c21 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/web/templatemodels/Tags.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/web/templatemodels/Tags.java @@ -5,7 +5,8 @@ package edu.cornell.mannlib.vitro.webapp.web.templatemodels; import java.lang.reflect.Method; import java.util.LinkedHashSet; -import org.apache.commons.lang.StringUtils; +import freemarker.ext.beans.MethodAppearanceFineTuner; +import org.apache.commons.lang3.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -46,20 +47,19 @@ public class Tags extends BaseTemplateModel { public TagsWrapper() { // Start by exposing all safe methods. setExposureLevel(EXPOSE_SAFE); - } - - @SuppressWarnings("rawtypes") - @Override - protected void finetuneMethodAppearance(Class cls, Method method, MethodAppearanceDecision decision) { - - try { - String methodName = method.getName(); - if ( ! ( methodName.equals("add") || methodName.equals("list")) ) { - decision.setExposeMethodAs(null); + setMethodAppearanceFineTuner(new MethodAppearanceFineTuner() { + @Override + public void process(MethodAppearanceDecisionInput methodAppearanceDecisionInput, MethodAppearanceDecision methodAppearanceDecision) { + try { + String methodName = methodAppearanceDecisionInput.getMethod().getName(); + if ( ! ( methodName.equals("add") || methodName.equals("list")) ) { + methodAppearanceDecision.setExposeMethodAs(null); + } + } catch (Exception e) { + log.error(e, e); + } } - } catch (Exception e) { - log.error(e, e); - } + }); } } diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/web/templatemodels/VClassGroupTemplateModel.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/web/templatemodels/VClassGroupTemplateModel.java index a8db892ed..c0335b821 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/web/templatemodels/VClassGroupTemplateModel.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/web/templatemodels/VClassGroupTemplateModel.java @@ -5,7 +5,7 @@ package edu.cornell.mannlib.vitro.webapp.web.templatemodels; import java.util.ArrayList; import java.util.List; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/web/templatemodels/customlistview/DataPropertyListConfig.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/web/templatemodels/customlistview/DataPropertyListConfig.java index b88b9ab38..6d34ccef8 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/web/templatemodels/customlistview/DataPropertyListConfig.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/web/templatemodels/customlistview/DataPropertyListConfig.java @@ -8,7 +8,7 @@ import java.io.IOException; import java.lang.reflect.Constructor; import java.util.Set; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/web/templatemodels/customlistview/PropertyListConfig.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/web/templatemodels/customlistview/PropertyListConfig.java index fa212fd64..f2dda64dd 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/web/templatemodels/customlistview/PropertyListConfig.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/web/templatemodels/customlistview/PropertyListConfig.java @@ -8,7 +8,7 @@ import java.io.IOException; import java.lang.reflect.Constructor; import java.util.Set; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/web/templatemodels/edit/EditConfigurationTemplateModel.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/web/templatemodels/edit/EditConfigurationTemplateModel.java index 4990cdf03..02c2eb0a1 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/web/templatemodels/edit/EditConfigurationTemplateModel.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/web/templatemodels/edit/EditConfigurationTemplateModel.java @@ -15,7 +15,7 @@ import java.util.List; import java.util.Map; import java.util.Set; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/web/templatemodels/individual/BaseIndividualTemplateModel.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/web/templatemodels/individual/BaseIndividualTemplateModel.java index a233014fc..76c10a5a9 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/web/templatemodels/individual/BaseIndividualTemplateModel.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/web/templatemodels/individual/BaseIndividualTemplateModel.java @@ -12,7 +12,7 @@ import java.util.Map; import javax.servlet.ServletContext; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/web/templatemodels/individual/CollatedObjectPropertyTemplateModel.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/web/templatemodels/individual/CollatedObjectPropertyTemplateModel.java index 81dd48ad0..2f449cf0a 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/web/templatemodels/individual/CollatedObjectPropertyTemplateModel.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/web/templatemodels/individual/CollatedObjectPropertyTemplateModel.java @@ -15,7 +15,7 @@ import java.util.regex.Pattern; import javax.servlet.ServletContext; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/web/templatemodels/individual/DataPropertyTemplateModel.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/web/templatemodels/individual/DataPropertyTemplateModel.java index c49d17f47..b24c49e77 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/web/templatemodels/individual/DataPropertyTemplateModel.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/web/templatemodels/individual/DataPropertyTemplateModel.java @@ -8,7 +8,7 @@ import java.util.ArrayList; import java.util.List; import java.util.Set; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/web/templatemodels/individual/ObjectPropertyTemplateModel.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/web/templatemodels/individual/ObjectPropertyTemplateModel.java index e26c43545..a0434968d 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/web/templatemodels/individual/ObjectPropertyTemplateModel.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/web/templatemodels/individual/ObjectPropertyTemplateModel.java @@ -12,7 +12,7 @@ import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/web/templatemodels/individual/SubclassTemplateModel.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/web/templatemodels/individual/SubclassTemplateModel.java index 05768ab10..b3bcee240 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/web/templatemodels/individual/SubclassTemplateModel.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/web/templatemodels/individual/SubclassTemplateModel.java @@ -4,7 +4,7 @@ package edu.cornell.mannlib.vitro.webapp.web.templatemodels.individual; import java.util.List; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import edu.cornell.mannlib.vitro.webapp.beans.ObjectPropertyStatement; import edu.cornell.mannlib.vitro.webapp.beans.VClass; diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/web/widgets/LoginWidget.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/web/widgets/LoginWidget.java index 347089b2e..5a62f7c32 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/web/widgets/LoginWidget.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/web/widgets/LoginWidget.java @@ -7,7 +7,7 @@ import java.util.Map; import javax.servlet.ServletContext; import javax.servlet.http.HttpServletRequest; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; diff --git a/api/src/main/java/edu/ucsf/vitro/opensocial/OpenSocialSmokeTests.java b/api/src/main/java/edu/ucsf/vitro/opensocial/OpenSocialSmokeTests.java index 7e3d3cc9d..56010863e 100644 --- a/api/src/main/java/edu/ucsf/vitro/opensocial/OpenSocialSmokeTests.java +++ b/api/src/main/java/edu/ucsf/vitro/opensocial/OpenSocialSmokeTests.java @@ -20,7 +20,7 @@ import javax.servlet.ServletContextListener; import edu.cornell.mannlib.vitro.webapp.utils.http.HttpClientFactory; import org.apache.commons.dbcp.BasicDataSource; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.http.HttpResponse; diff --git a/api/src/main/java/freemarker/ext/dump/BaseDumpDirective.java b/api/src/main/java/freemarker/ext/dump/BaseDumpDirective.java index a2ff4b473..fc3e90247 100644 --- a/api/src/main/java/freemarker/ext/dump/BaseDumpDirective.java +++ b/api/src/main/java/freemarker/ext/dump/BaseDumpDirective.java @@ -17,7 +17,7 @@ import java.util.TreeMap; import java.util.regex.Matcher; import java.util.regex.Pattern; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; diff --git a/api/src/main/java/org/linkeddatafragments/config/ConfigReader.java b/api/src/main/java/org/linkeddatafragments/config/ConfigReader.java new file mode 100644 index 000000000..3db1d638f --- /dev/null +++ b/api/src/main/java/org/linkeddatafragments/config/ConfigReader.java @@ -0,0 +1,119 @@ +package org.linkeddatafragments.config; + +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import org.linkeddatafragments.datasource.IDataSourceType; + +import java.io.Reader; +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; + +/** + * Reads the configuration of a Linked Data Fragments server. + * + * @author Ruben Verborgh + * @author Olaf Hartig + */ +public class ConfigReader { + private final Map dataSourceTypes = new HashMap<>(); + private final Map dataSources = new HashMap<>(); + private final Map prefixes = new HashMap<>(); + private final String baseURL; + + /** + * Creates a new configuration reader. + * + * @param configReader the configuration + */ + public ConfigReader(Reader configReader) { + JsonObject root = new JsonParser().parse(configReader).getAsJsonObject(); + this.baseURL = root.has("baseURL") ? root.getAsJsonPrimitive("baseURL").getAsString() : null; + + for (Entry entry : root.getAsJsonObject("datasourcetypes").entrySet()) { + final String className = entry.getValue().getAsString(); + dataSourceTypes.put(entry.getKey(), initDataSouceType(className) ); + } + for (Entry entry : root.getAsJsonObject("datasources").entrySet()) { + JsonObject dataSource = entry.getValue().getAsJsonObject(); + this.dataSources.put(entry.getKey(), dataSource); + } + for (Entry entry : root.getAsJsonObject("prefixes").entrySet()) { + this.prefixes.put(entry.getKey(), entry.getValue().getAsString()); + } + } + + /** + * Gets the data source types. + * + * @return a mapping of names of data source types to these types + */ + public Map getDataSourceTypes() { + return dataSourceTypes; + } + + /** + * Gets the data sources. + * + * @return the data sources + */ + public Map getDataSources() { + return dataSources; + } + + /** + * Gets the prefixes. + * + * @return the prefixes + */ + public Map getPrefixes() { + return prefixes; + } + + /** + * Gets the base URL + * + * @return the base URL + */ + public String getBaseURL() { + return baseURL; + } + + /** + * Loads a certain {@link IDataSourceType} class at runtime + * + * @param className IDataSourceType class + * @return the created IDataSourceType object + */ + protected IDataSourceType initDataSouceType(final String className ) + { + final Class c; + try { + c = Class.forName( className ); + } + catch ( ClassNotFoundException e ) { + throw new IllegalArgumentException( "Class not found: " + className, + e ); + } + + final Object o; + try { + o = c.newInstance(); + } + catch ( Exception e ) { + throw new IllegalArgumentException( + "Creating an instance of class '" + className + "' " + + "caused a " + e.getClass().getSimpleName() + ": " + + e.getMessage(), e ); + } + + if ( ! (o instanceof IDataSourceType) ) + throw new IllegalArgumentException( + "Class '" + className + "' is not an implementation " + + "of IDataSourceType." ); + + return (IDataSourceType) o; + } + +} diff --git a/api/src/main/java/org/linkeddatafragments/datasource/AbstractRequestProcessor.java b/api/src/main/java/org/linkeddatafragments/datasource/AbstractRequestProcessor.java new file mode 100644 index 000000000..6cfa14ef8 --- /dev/null +++ b/api/src/main/java/org/linkeddatafragments/datasource/AbstractRequestProcessor.java @@ -0,0 +1,76 @@ +package org.linkeddatafragments.datasource; + +import org.linkeddatafragments.fragments.ILinkedDataFragment; +import org.linkeddatafragments.fragments.ILinkedDataFragmentRequest; + +/** + * Base class for implementations of {@link IFragmentRequestProcessor}. + * + * @author Olaf Hartig + */ +abstract public class AbstractRequestProcessor + implements IFragmentRequestProcessor +{ + @Override + public void close() {} + + /** + * Create an {@link ILinkedDataFragment} from {@link ILinkedDataFragmentRequest} + * + * @param request + * @return + * @throws IllegalArgumentException + */ + @Override + final public ILinkedDataFragment createRequestedFragment( + final ILinkedDataFragmentRequest request ) + throws IllegalArgumentException + { + return getWorker( request ).createRequestedFragment(); + } + + /** + * Get the {@link Worker} from {@link ILinkedDataFragmentRequest} + * + * @param request + * @return + * @throws IllegalArgumentException + */ + abstract protected Worker getWorker( + final ILinkedDataFragmentRequest request ) + throws IllegalArgumentException; + + /** + * Processes {@link ILinkedDataFragmentRequest}s + * + */ + abstract static protected class Worker + { + + /** + * The {@link ILinkedDataFragmentRequest} to process + */ + public final ILinkedDataFragmentRequest request; + + /** + * Create a Worker + * + * @param request + */ + public Worker( final ILinkedDataFragmentRequest request ) + { + this.request = request; + } + + /** + * Create the requested {@link ILinkedDataFragment} + * + * @return The ILinkedDataFragment + * @throws IllegalArgumentException + */ + abstract public ILinkedDataFragment createRequestedFragment() + throws IllegalArgumentException; + + } // end of class Worker + +} diff --git a/api/src/main/java/org/linkeddatafragments/datasource/AbstractRequestProcessorForTriplePatterns.java b/api/src/main/java/org/linkeddatafragments/datasource/AbstractRequestProcessorForTriplePatterns.java new file mode 100644 index 000000000..de8070b7d --- /dev/null +++ b/api/src/main/java/org/linkeddatafragments/datasource/AbstractRequestProcessorForTriplePatterns.java @@ -0,0 +1,158 @@ +package org.linkeddatafragments.datasource; + +import org.apache.jena.rdf.model.Model; +import org.linkeddatafragments.fragments.ILinkedDataFragment; +import org.linkeddatafragments.fragments.ILinkedDataFragmentRequest; +import org.linkeddatafragments.fragments.tpf.ITriplePatternElement; +import org.linkeddatafragments.fragments.tpf.ITriplePatternFragment; +import org.linkeddatafragments.fragments.tpf.ITriplePatternFragmentRequest; +import org.linkeddatafragments.fragments.tpf.TriplePatternFragmentImpl; + +/** + * Base class for implementations of {@link IFragmentRequestProcessor} that + * process {@link ITriplePatternFragmentRequest}s. + * + * @param + * type for representing constants in triple patterns (i.e., URIs and + * literals) + * @param + * type for representing named variables in triple patterns + * @param + * type for representing anonymous variables in triple patterns (i.e., + * variables denoted by a blank node) + * + * @author Olaf Hartig + */ +public abstract class + AbstractRequestProcessorForTriplePatterns + extends AbstractRequestProcessor +{ + + /** + * + * @param request + * @return + * @throws IllegalArgumentException + */ + @Override + protected final Worker getWorker( + final ILinkedDataFragmentRequest request ) + throws IllegalArgumentException + { + if ( request instanceof ITriplePatternFragmentRequest) { + @SuppressWarnings("unchecked") + final ITriplePatternFragmentRequest tpfRequest = + (ITriplePatternFragmentRequest) request; + return getTPFSpecificWorker( tpfRequest ); + } + else + throw new IllegalArgumentException( request.getClass().getName() ); + } + + /** + * + * @param request + * @return + * @throws IllegalArgumentException + */ + abstract protected Worker getTPFSpecificWorker( + final ITriplePatternFragmentRequest request ) + throws IllegalArgumentException; + + /** + * + * @param + * @param + * @param + */ + abstract static protected class Worker + extends AbstractRequestProcessor.Worker + { + + /** + * + * @param request + */ + public Worker( + final ITriplePatternFragmentRequest request ) + { + super( request ); + } + + /** + * + * @return + * @throws IllegalArgumentException + */ + @Override + public ILinkedDataFragment createRequestedFragment() + throws IllegalArgumentException + { + final long limit = ILinkedDataFragmentRequest.TRIPLESPERPAGE; + final long offset; + if ( request.isPageRequest() ) + offset = limit * ( request.getPageNumber() - 1L ); + else + offset = 0L; + + @SuppressWarnings("unchecked") + final ITriplePatternFragmentRequest tpfRequest = + (ITriplePatternFragmentRequest) request; + + return createFragment( tpfRequest.getSubject(), + tpfRequest.getPredicate(), + tpfRequest.getObject(), + offset, limit ); + } + + /** + * + * @param subj + * @param pred + * @param obj + * @param offset + * @param limit + * @return + * @throws IllegalArgumentException + */ + abstract protected ILinkedDataFragment createFragment( + final ITriplePatternElement subj, + final ITriplePatternElement pred, + final ITriplePatternElement obj, + final long offset, + final long limit ) + throws IllegalArgumentException; + + /** + * + * @return + */ + protected ITriplePatternFragment createEmptyTriplePatternFragment() + { + return new TriplePatternFragmentImpl( request.getFragmentURL(), + request.getDatasetURL() ); + } + + /** + * + * @param triples + * @param totalSize + * @param isLastPage + * @return + */ + protected ITriplePatternFragment createTriplePatternFragment( + final Model triples, + final long totalSize, + final boolean isLastPage ) + { + return new TriplePatternFragmentImpl( triples, + totalSize, + request.getFragmentURL(), + request.getDatasetURL(), + request.getPageNumber(), + isLastPage ); + } + + } // end of class Worker + +} diff --git a/api/src/main/java/org/linkeddatafragments/datasource/DataSourceBase.java b/api/src/main/java/org/linkeddatafragments/datasource/DataSourceBase.java new file mode 100644 index 000000000..45b669f6a --- /dev/null +++ b/api/src/main/java/org/linkeddatafragments/datasource/DataSourceBase.java @@ -0,0 +1,54 @@ +package org.linkeddatafragments.datasource; + +/** + * The base class for an {@link IDataSource} + * + * @author Miel Vander Sande + * @author Bart Hanssens + */ +public abstract class DataSourceBase implements IDataSource { + + /** + * Get the datasource title + */ + protected String title; + + /** + * Get the datasource description + */ + protected String description; + + /** + * Create a base for a {@link IDataSource} + * + * @param title the datasource title + * @param description the datasource description + */ + public DataSourceBase(String title, String description) { + this.title = title; + this.description = description; + } + + /** + * Get the datasource description + * + * @return + */ + @Override + public String getDescription() { + return this.description; + }; + + /** + * Get the datasource title + * + * @return + */ + @Override + public String getTitle() { + return this.title; + }; + + @Override + public void close() {} +} diff --git a/api/src/main/java/org/linkeddatafragments/datasource/DataSourceFactory.java b/api/src/main/java/org/linkeddatafragments/datasource/DataSourceFactory.java new file mode 100644 index 000000000..d23741d54 --- /dev/null +++ b/api/src/main/java/org/linkeddatafragments/datasource/DataSourceFactory.java @@ -0,0 +1,35 @@ +package org.linkeddatafragments.datasource; + +import com.google.gson.JsonObject; +import org.linkeddatafragments.exceptions.DataSourceCreationException; +import org.linkeddatafragments.exceptions.UnknownDataSourceTypeException; + +/** + * + * @author Miel Vander Sande + * @author Bart Hanssens + * @author Olaf Hartig + */ +public class DataSourceFactory { + /** + * Create a datasource using a JSON config + * + * @param config + * @return datasource interface + * @throws DataSourceCreationException + */ + public static IDataSource create(JsonObject config) throws DataSourceCreationException { + String title = config.getAsJsonPrimitive("title").getAsString(); + String description = config.getAsJsonPrimitive("description").getAsString(); + String typeName = config.getAsJsonPrimitive("type").getAsString(); + + JsonObject settings = config.getAsJsonObject("settings"); + + final IDataSourceType type = DataSourceTypesRegistry.getType(typeName); + if ( type == null ) + throw new UnknownDataSourceTypeException(typeName); + + return type.createDataSource( title, description, settings ); + } + +} diff --git a/api/src/main/java/org/linkeddatafragments/datasource/DataSourceTypesRegistry.java b/api/src/main/java/org/linkeddatafragments/datasource/DataSourceTypesRegistry.java new file mode 100644 index 000000000..f502c6f69 --- /dev/null +++ b/api/src/main/java/org/linkeddatafragments/datasource/DataSourceTypesRegistry.java @@ -0,0 +1,51 @@ +package org.linkeddatafragments.datasource; + +import java.util.HashMap; +import java.util.Map; + +/** + * A registry of {@link IDataSourceType}s. + * + * @author Olaf Hartig + */ +public class DataSourceTypesRegistry +{ + private static Map registry = + new HashMap(); + + /** + * + * @param typeName + * @return + */ + public static synchronized IDataSourceType getType( final String typeName ) + { + return registry.get( typeName ); + } + + /** + * + * @param typeName + * @return + */ + public static synchronized boolean isRegistered( final String typeName ) + { + return registry.containsKey( typeName ); + } + + /** + * + * @param typeName + * @param type + */ + public static synchronized void register( final String typeName, + final IDataSourceType type ) + { + if ( registry.containsKey(typeName) ) { + throw new IllegalArgumentException( "The registry already " + + "contains a type with the name '" + typeName + "'." ); + } + registry.put( typeName, type ); + } + +} diff --git a/api/src/main/java/org/linkeddatafragments/datasource/IDataSource.java b/api/src/main/java/org/linkeddatafragments/datasource/IDataSource.java new file mode 100644 index 000000000..971e18422 --- /dev/null +++ b/api/src/main/java/org/linkeddatafragments/datasource/IDataSource.java @@ -0,0 +1,38 @@ +package org.linkeddatafragments.datasource; + +import org.linkeddatafragments.fragments.IFragmentRequestParser; + +import java.io.Closeable; + +/** + * A data source of Linked Data Fragments. + * + * @author Ruben Verborgh + * @author Olaf Hartig + */ +public interface IDataSource extends Closeable { + + /** + * + * @return + */ + public String getTitle(); + + /** + * + * @return + */ + public String getDescription(); + + /** + * Returns a data source specific {@link IFragmentRequestParser}. + * @return + */ + IFragmentRequestParser getRequestParser(); + + /** + * Returns a data source specific {@link IFragmentRequestProcessor}. + * @return + */ + IFragmentRequestProcessor getRequestProcessor(); +} diff --git a/api/src/main/java/org/linkeddatafragments/datasource/IDataSourceType.java b/api/src/main/java/org/linkeddatafragments/datasource/IDataSourceType.java new file mode 100644 index 000000000..b9a19f760 --- /dev/null +++ b/api/src/main/java/org/linkeddatafragments/datasource/IDataSourceType.java @@ -0,0 +1,33 @@ +package org.linkeddatafragments.datasource; + +import com.google.gson.JsonObject; +import org.linkeddatafragments.exceptions.DataSourceCreationException; + +/** + * Represents types of {@link IDataSource}s that can be used to provide some + * Linked Data Fragments interface. + * + * @author Olaf Hartig + */ +public interface IDataSourceType +{ + /** + * Creates a data source of this type. + * + * @param title + * The title of the data source (as given in the config file). + * + * @param description + * The description of the data source (as given in the config file). + * + * @param settings + * The properties of the data source to be created; usually, these + * properties are given in the config file of the LDF server. + * @return + * @throws org.linkeddatafragments.exceptions.DataSourceCreationException + */ + IDataSource createDataSource(final String title, + final String description, + final JsonObject settings) + throws DataSourceCreationException; +} diff --git a/api/src/main/java/org/linkeddatafragments/datasource/IFragmentRequestProcessor.java b/api/src/main/java/org/linkeddatafragments/datasource/IFragmentRequestProcessor.java new file mode 100644 index 000000000..b3f48bef1 --- /dev/null +++ b/api/src/main/java/org/linkeddatafragments/datasource/IFragmentRequestProcessor.java @@ -0,0 +1,26 @@ +package org.linkeddatafragments.datasource; + +import org.linkeddatafragments.fragments.ILinkedDataFragment; +import org.linkeddatafragments.fragments.ILinkedDataFragmentRequest; + +import java.io.Closeable; + +/** + * Processes {@link ILinkedDataFragmentRequest}s and returns + * the requested {@link ILinkedDataFragment}s. + * + * @author Olaf Hartig + */ +public interface IFragmentRequestProcessor extends Closeable +{ + + /** + * + * @param request + * @return + * @throws IllegalArgumentException + */ + ILinkedDataFragment createRequestedFragment( + final ILinkedDataFragmentRequest request) + throws IllegalArgumentException; +} diff --git a/api/src/main/java/org/linkeddatafragments/datasource/index/IndexDataSource.java b/api/src/main/java/org/linkeddatafragments/datasource/index/IndexDataSource.java new file mode 100644 index 000000000..90536702e --- /dev/null +++ b/api/src/main/java/org/linkeddatafragments/datasource/index/IndexDataSource.java @@ -0,0 +1,47 @@ +package org.linkeddatafragments.datasource.index; + +import org.linkeddatafragments.datasource.DataSourceBase; +import org.linkeddatafragments.datasource.IDataSource; +import org.linkeddatafragments.datasource.IFragmentRequestProcessor; +import org.linkeddatafragments.fragments.IFragmentRequestParser; +import org.linkeddatafragments.fragments.tpf.TPFRequestParserForJenaBackends; + +import java.util.HashMap; + +/** + * An Index data source provides an overview of all available datasets. + * + * @author Miel Vander Sande + * @author Olaf Hartig + */ +public class IndexDataSource extends DataSourceBase { + + /** + * The request processor + * + */ + protected final IndexRequestProcessorForTPFs requestProcessor; + + /** + * + * @param baseUrl + * @param datasources + */ + public IndexDataSource(String baseUrl, HashMap datasources) { + super("Index", "List of all datasources"); + requestProcessor = new IndexRequestProcessorForTPFs( baseUrl, datasources ); + } + + @Override + public IFragmentRequestParser getRequestParser() + { + return TPFRequestParserForJenaBackends.getInstance(); + } + + @Override + public IFragmentRequestProcessor getRequestProcessor() + { + return requestProcessor; + } + +} diff --git a/api/src/main/java/org/linkeddatafragments/datasource/index/IndexRequestProcessorForTPFs.java b/api/src/main/java/org/linkeddatafragments/datasource/index/IndexRequestProcessorForTPFs.java new file mode 100644 index 000000000..35e632ab1 --- /dev/null +++ b/api/src/main/java/org/linkeddatafragments/datasource/index/IndexRequestProcessorForTPFs.java @@ -0,0 +1,146 @@ +package org.linkeddatafragments.datasource.index; + + +import org.apache.jena.rdf.model.Model; +import org.apache.jena.rdf.model.ModelFactory; +import org.apache.jena.rdf.model.Property; +import org.apache.jena.rdf.model.RDFNode; +import org.apache.jena.rdf.model.Resource; +import org.apache.jena.rdf.model.ResourceFactory; +import org.apache.jena.rdf.model.StmtIterator; +import org.apache.jena.rdf.model.impl.PropertyImpl; +import org.apache.jena.rdf.model.impl.ResourceImpl; +import org.linkeddatafragments.datasource.AbstractRequestProcessorForTriplePatterns; +import org.linkeddatafragments.datasource.IDataSource; +import org.linkeddatafragments.datasource.IFragmentRequestProcessor; +import org.linkeddatafragments.fragments.ILinkedDataFragment; +import org.linkeddatafragments.fragments.tpf.ITriplePatternElement; +import org.linkeddatafragments.fragments.tpf.ITriplePatternFragmentRequest; + +import java.util.HashMap; +import java.util.Map; + +/** + * Implementation of {@link IFragmentRequestProcessor} that processes + * {@link ITriplePatternFragmentRequest}s over an index that provides + * an overview of all available datasets. + * + * @author Miel Vander Sande + * @author Olaf Hartig + */ +public class IndexRequestProcessorForTPFs + extends AbstractRequestProcessorForTriplePatterns +{ + final static String RDF = "http://www.w3.org/1999/02/22-rdf-syntax-ns#"; + final static String RDFS = "http://www.w3.org/2000/01/rdf-schema#"; + final static String DC = "http://purl.org/dc/terms/"; + final static String VOID = "http://rdfs.org/ns/void#"; + + private final Model model; + + /** + * + * @param baseUrl + * @param datasources + */ + public IndexRequestProcessorForTPFs( + final String baseUrl, + final HashMap datasources ) + { + this.model = ModelFactory.createDefaultModel(); + + for (Map.Entry entry : datasources.entrySet()) { + String datasourceName = entry.getKey(); + IDataSource datasource = entry.getValue(); + + Resource datasourceUrl = new ResourceImpl(baseUrl + "/" + datasourceName); + + model.add(datasourceUrl, new PropertyImpl(RDF + "type"), VOID + "Dataset"); + model.add(datasourceUrl, new PropertyImpl(RDFS + "label"), datasource.getTitle()); + model.add(datasourceUrl, new PropertyImpl(DC + "title"), datasource.getTitle()); + model.add(datasourceUrl, new PropertyImpl(DC + "description"), datasource.getDescription()); + } + } + + /** + * + * @param request + * @return + * @throws IllegalArgumentException + */ + @Override + protected Worker getTPFSpecificWorker( + final ITriplePatternFragmentRequest request ) + throws IllegalArgumentException + { + return new Worker( request ); + } + + /** + * Worker for the index + */ + protected class Worker + extends AbstractRequestProcessorForTriplePatterns.Worker + { + + /** + * Creates a Worker for the datasource index + * + * @param req + */ + public Worker( + final ITriplePatternFragmentRequest req ) + { + super( req ); + } + + /** + * + * @param s + * @param p + * @param o + * @param offset + * @param limit + * @return + */ + @Override + protected ILinkedDataFragment createFragment( + final ITriplePatternElement s, + final ITriplePatternElement p, + final ITriplePatternElement o, + final long offset, + final long limit ) + { + // FIXME: The following algorithm is incorrect for cases in which + // the requested triple pattern contains a specific variable + // multiple times; + // e.g., (?x foaf:knows ?x ) or (_:bn foaf:knows _:bn) + // see https://github.com/LinkedDataFragments/Server.Java/issues/25 + + final Resource subject = s.isVariable() ? null + : s.asConstantTerm().asResource(); + final Property predicate = p.isVariable() ? null + : ResourceFactory.createProperty(p.asConstantTerm().asResource().getURI()); + final RDFNode object = o.isVariable() ? null + : o.asConstantTerm(); + + StmtIterator listStatements = model.listStatements(subject, predicate, object); + Model result = ModelFactory.createDefaultModel(); + + long index = 0; + while (listStatements.hasNext() && index < offset) { + listStatements.next(); + index++; + } + + while (listStatements.hasNext() && index < (offset + limit)) { + result.add(listStatements.next()); + } + + final boolean isLastPage = ( result.size() < offset + limit ); + return createTriplePatternFragment( result, result.size(), isLastPage ); + } + + } // end of class Worker + +} diff --git a/api/src/main/java/org/linkeddatafragments/datasource/tdb/JenaTDBBasedRequestProcessorForTPFs.java b/api/src/main/java/org/linkeddatafragments/datasource/tdb/JenaTDBBasedRequestProcessorForTPFs.java new file mode 100644 index 000000000..804d9482c --- /dev/null +++ b/api/src/main/java/org/linkeddatafragments/datasource/tdb/JenaTDBBasedRequestProcessorForTPFs.java @@ -0,0 +1,165 @@ +package org.linkeddatafragments.datasource.tdb; + +import org.apache.jena.query.Dataset; +import org.apache.jena.query.Query; +import org.apache.jena.query.QueryExecution; +import org.apache.jena.query.QueryExecutionFactory; +import org.apache.jena.query.QueryFactory; +import org.apache.jena.query.QuerySolution; +import org.apache.jena.query.QuerySolutionMap; +import org.apache.jena.query.ResultSet; +import org.apache.jena.query.Syntax; +import org.apache.jena.rdf.model.Literal; +import org.apache.jena.rdf.model.Model; +import org.apache.jena.rdf.model.ModelFactory; +import org.apache.jena.rdf.model.RDFNode; +import org.apache.jena.tdb.TDBFactory; +import org.linkeddatafragments.datasource.AbstractRequestProcessorForTriplePatterns; +import org.linkeddatafragments.datasource.IFragmentRequestProcessor; +import org.linkeddatafragments.fragments.ILinkedDataFragment; +import org.linkeddatafragments.fragments.tpf.ITriplePatternElement; +import org.linkeddatafragments.fragments.tpf.ITriplePatternFragmentRequest; + +import java.io.File; + +/** + * Implementation of {@link IFragmentRequestProcessor} that processes + * {@link ITriplePatternFragmentRequest}s over data stored in Jena TDB. + * + * @author Bart Hanssens + * @author Olaf Hartig + */ +public class JenaTDBBasedRequestProcessorForTPFs + extends AbstractRequestProcessorForTriplePatterns +{ + private final Dataset tdb; + private final String sparql = "CONSTRUCT WHERE { ?s ?p ?o } " + + "ORDER BY ?s ?p ?o"; + + private final String count = "SELECT (COUNT(?s) AS ?count) WHERE { ?s ?p ?o }"; + + private final Query query = QueryFactory.create(sparql, Syntax.syntaxSPARQL_11); + private final Query countQuery = QueryFactory.create(count, Syntax.syntaxSPARQL_11); + + /** + * + * @param request + * @return + * @throws IllegalArgumentException + */ + @Override + protected Worker getTPFSpecificWorker( + final ITriplePatternFragmentRequest request ) + throws IllegalArgumentException + { + return new Worker( request ); + } + + /** + * + */ + protected class Worker + extends AbstractRequestProcessorForTriplePatterns.Worker + { + + /** + * + * @param req + */ + public Worker( + final ITriplePatternFragmentRequest req ) + { + super( req ); + } + + /** + * + * @param subject + * @param predicate + * @param object + * @param offset + * @param limit + * @return + */ + @Override + protected ILinkedDataFragment createFragment( + final ITriplePatternElement subject, + final ITriplePatternElement predicate, + final ITriplePatternElement object, + final long offset, + final long limit ) + { + // FIXME: The following algorithm is incorrect for cases in which + // the requested triple pattern contains a specific variable + // multiple times; + // e.g., (?x foaf:knows ?x ) or (_:bn foaf:knows _:bn) + // see https://github.com/LinkedDataFragments/Server.Java/issues/24 + + Model model = tdb.getDefaultModel(); + QuerySolutionMap map = new QuerySolutionMap(); + if ( ! subject.isVariable() ) { + map.add("s", subject.asConstantTerm()); + } + if ( ! predicate.isVariable() ) { + map.add("p", predicate.asConstantTerm()); + } + if ( ! object.isVariable() ) { + map.add("o", object.asConstantTerm()); + } + + query.setOffset(offset); + query.setLimit(limit); + + Model triples = ModelFactory.createDefaultModel(); + + try (QueryExecution qexec = QueryExecutionFactory.create(query, model, map)) { + qexec.execConstruct(triples); + } + + if (triples.isEmpty()) { + return createEmptyTriplePatternFragment(); + } + + // Try to get an estimate + long size = triples.size(); + long estimate = -1; + + try (QueryExecution qexec = QueryExecutionFactory.create(countQuery, model, map)) { + ResultSet results = qexec.execSelect(); + if (results.hasNext()) { + QuerySolution soln = results.nextSolution() ; + Literal literal = soln.getLiteral("count"); + estimate = literal.getLong(); + } + } + + /*GraphStatisticsHandler stats = model.getGraph().getStatisticsHandler(); + if (stats != null) { + Node s = (subject != null) ? subject.asNode() : null; + Node p = (predicate != null) ? predicate.asNode() : null; + Node o = (object != null) ? object.asNode() : null; + estimate = stats.getStatistic(s, p, o); + }*/ + + // No estimate or incorrect + if (estimate < offset + size) { + estimate = (size == limit) ? offset + size + 1 : offset + size; + } + + // create the fragment + final boolean isLastPage = ( estimate < offset + limit ); + return createTriplePatternFragment( triples, estimate, isLastPage ); + } + + } // end of class Worker + + + /** + * Constructor + * + * @param tdbdir directory used for TDB backing + */ + public JenaTDBBasedRequestProcessorForTPFs(File tdbdir) { + this.tdb = TDBFactory.createDataset(tdbdir.getAbsolutePath()); + } +} diff --git a/api/src/main/java/org/linkeddatafragments/datasource/tdb/JenaTDBDataSource.java b/api/src/main/java/org/linkeddatafragments/datasource/tdb/JenaTDBDataSource.java new file mode 100644 index 000000000..58e35b3ae --- /dev/null +++ b/api/src/main/java/org/linkeddatafragments/datasource/tdb/JenaTDBDataSource.java @@ -0,0 +1,47 @@ +package org.linkeddatafragments.datasource.tdb; + +import org.linkeddatafragments.datasource.DataSourceBase; +import org.linkeddatafragments.datasource.IFragmentRequestProcessor; +import org.linkeddatafragments.fragments.IFragmentRequestParser; +import org.linkeddatafragments.fragments.tpf.TPFRequestParserForJenaBackends; + +import java.io.File; + +/** + * Experimental Jena TDB-backed data source of Basic Linked Data Fragments. + * + * @author Bart Hanssens + * @author Olaf Hartig + */ +public class JenaTDBDataSource extends DataSourceBase { + + /** + * The request processor + * + */ + protected final JenaTDBBasedRequestProcessorForTPFs requestProcessor; + + @Override + public IFragmentRequestParser getRequestParser() + { + return TPFRequestParserForJenaBackends.getInstance(); + } + + @Override + public IFragmentRequestProcessor getRequestProcessor() + { + return requestProcessor; + } + + /** + * Constructor + * + * @param title + * @param description + * @param tdbdir directory used for TDB backing + */ + public JenaTDBDataSource(String title, String description, File tdbdir) { + super(title, description); + requestProcessor = new JenaTDBBasedRequestProcessorForTPFs( tdbdir ); + } +} diff --git a/api/src/main/java/org/linkeddatafragments/datasource/tdb/JenaTDBDataSourceType.java b/api/src/main/java/org/linkeddatafragments/datasource/tdb/JenaTDBDataSourceType.java new file mode 100644 index 000000000..bbebca71f --- /dev/null +++ b/api/src/main/java/org/linkeddatafragments/datasource/tdb/JenaTDBDataSourceType.java @@ -0,0 +1,34 @@ +package org.linkeddatafragments.datasource.tdb; + +import com.google.gson.JsonObject; +import org.linkeddatafragments.datasource.IDataSource; +import org.linkeddatafragments.datasource.IDataSourceType; +import org.linkeddatafragments.exceptions.DataSourceCreationException; + +import java.io.File; + +/** + * The type of Triple Pattern Fragment data sources that are backed by + * a Jena TDB instance. + * + * @author Olaf Hartig + */ +public class JenaTDBDataSourceType implements IDataSourceType +{ + @Override + public IDataSource createDataSource( final String title, + final String description, + final JsonObject settings ) + throws DataSourceCreationException + { + final String dname = settings.getAsJsonPrimitive("directory").getAsString(); + final File dir = new File( dname ); + + try { + return new JenaTDBDataSource(title, description, dir); + } catch (Exception ex) { + throw new DataSourceCreationException(ex); + } + } + +} diff --git a/api/src/main/java/org/linkeddatafragments/exceptions/DataSourceCreationException.java b/api/src/main/java/org/linkeddatafragments/exceptions/DataSourceCreationException.java new file mode 100644 index 000000000..ea4924ecf --- /dev/null +++ b/api/src/main/java/org/linkeddatafragments/exceptions/DataSourceCreationException.java @@ -0,0 +1,25 @@ +package org.linkeddatafragments.exceptions; + +/** + * + * @author Miel Vander Sande + */ +public class DataSourceCreationException extends DataSourceException { + + /** + * + * @param cause + */ + public DataSourceCreationException(Throwable cause) { + super(cause); + } + + /** + * + * @param datasourceName + * @param message + */ + public DataSourceCreationException(String datasourceName, String message) { + super(datasourceName, "Could not create DataSource - " + message); + } +} diff --git a/api/src/main/java/org/linkeddatafragments/exceptions/DataSourceException.java b/api/src/main/java/org/linkeddatafragments/exceptions/DataSourceException.java new file mode 100644 index 000000000..2f5973de7 --- /dev/null +++ b/api/src/main/java/org/linkeddatafragments/exceptions/DataSourceException.java @@ -0,0 +1,37 @@ +package org.linkeddatafragments.exceptions; + +import org.linkeddatafragments.datasource.IDataSource; + +/** + * + * @author Miel Vander Sande + */ +abstract public class DataSourceException extends Exception { + + /** + * + * @param cause + */ + public DataSourceException(Throwable cause) { + super(cause); + } + + /** + * + * @param datasourceName + * @param message + */ + public DataSourceException(String datasourceName, String message) { + super("Error for datasource '" + datasourceName + "': " + message); + } + + /** + * + * @param datasource + * @param message + */ + public DataSourceException(IDataSource datasource, String message) { + this(datasource.getTitle(), message); + } + +} diff --git a/api/src/main/java/org/linkeddatafragments/exceptions/DataSourceNotFoundException.java b/api/src/main/java/org/linkeddatafragments/exceptions/DataSourceNotFoundException.java new file mode 100644 index 000000000..08dca003c --- /dev/null +++ b/api/src/main/java/org/linkeddatafragments/exceptions/DataSourceNotFoundException.java @@ -0,0 +1,16 @@ +package org.linkeddatafragments.exceptions; + +/** + * + * @author Miel Vander Sande + */ +public class DataSourceNotFoundException extends DataSourceException { + + /** + * + * @param dataSourceName + */ + public DataSourceNotFoundException(String dataSourceName) { + super(dataSourceName, "Datasource not found."); + } +} diff --git a/api/src/main/java/org/linkeddatafragments/exceptions/NoRegisteredMimeTypesException.java b/api/src/main/java/org/linkeddatafragments/exceptions/NoRegisteredMimeTypesException.java new file mode 100644 index 000000000..ad278a64c --- /dev/null +++ b/api/src/main/java/org/linkeddatafragments/exceptions/NoRegisteredMimeTypesException.java @@ -0,0 +1,17 @@ +package org.linkeddatafragments.exceptions; + +/** + * Exception thrown when no mimeTypes are known to the system + * + * @author Miel Vander Sande + */ +public class NoRegisteredMimeTypesException extends Exception { + + /** + * Constructs the exception + */ + public NoRegisteredMimeTypesException() { + super("List of supported mimeTypes is empty."); + } + +} diff --git a/api/src/main/java/org/linkeddatafragments/exceptions/UnknownDataSourceTypeException.java b/api/src/main/java/org/linkeddatafragments/exceptions/UnknownDataSourceTypeException.java new file mode 100644 index 000000000..1b631a09d --- /dev/null +++ b/api/src/main/java/org/linkeddatafragments/exceptions/UnknownDataSourceTypeException.java @@ -0,0 +1,16 @@ +package org.linkeddatafragments.exceptions; + +/** + * + * @author Miel Vander Sande + */ +public class UnknownDataSourceTypeException extends DataSourceCreationException { + + /** + * + * @param type + */ + public UnknownDataSourceTypeException(String type) { + super("", "Type " + type + " does not exist."); + } +} diff --git a/api/src/main/java/org/linkeddatafragments/fragments/FragmentRequestParserBase.java b/api/src/main/java/org/linkeddatafragments/fragments/FragmentRequestParserBase.java new file mode 100644 index 000000000..7d6c8b8c0 --- /dev/null +++ b/api/src/main/java/org/linkeddatafragments/fragments/FragmentRequestParserBase.java @@ -0,0 +1,141 @@ +package org.linkeddatafragments.fragments; + +import org.linkeddatafragments.config.ConfigReader; + +import javax.servlet.http.HttpServletRequest; + +/** + * Base class for implementations of {@link IFragmentRequestParser}. + * + * @author Olaf Hartig + */ +abstract public class FragmentRequestParserBase implements IFragmentRequestParser +{ + @Override + final public ILinkedDataFragmentRequest parseIntoFragmentRequest( + final HttpServletRequest httpRequest, + final ConfigReader config ) + throws IllegalArgumentException + { + return getWorker( httpRequest, config ).createFragmentRequest(); + } + + /** + * + * @param httpRequest + * @param config + * @return + * @throws IllegalArgumentException + */ + abstract protected Worker getWorker( final HttpServletRequest httpRequest, + final ConfigReader config ) + throws IllegalArgumentException; + + /** + * + */ + abstract static protected class Worker + { + + /** + * + */ + public final HttpServletRequest request; + + /** + * + */ + public final ConfigReader config; + + /** + * + */ + public final boolean pageNumberWasRequested; + + /** + * + */ + public final long pageNumber; + + /** + * + * @param request + * @param config + */ + public Worker( final HttpServletRequest request, + final ConfigReader config ) + { + this.request = request; + this.config = config; + + final String givenPageNumber = request.getParameter( + ILinkedDataFragmentRequest.PARAMETERNAME_PAGE ); + if ( givenPageNumber != null ) { + long pageNumber; + try { + pageNumber = Long.parseLong( givenPageNumber ); + } catch (NumberFormatException ex) { + pageNumber = 1L; + } + this.pageNumber = ( pageNumber > 0 ) ? pageNumber : 1L; + this.pageNumberWasRequested = true; + } + else { + this.pageNumber = 1L; + this.pageNumberWasRequested = false; + } + } + + /** + * + * @return + * @throws IllegalArgumentException + */ + abstract public ILinkedDataFragmentRequest createFragmentRequest() + throws IllegalArgumentException; + + /** + * + * @return + */ + public String getFragmentURL() { + final String datasetURL = getDatasetURL(); + final String query = request.getQueryString(); + return query == null ? datasetURL : (datasetURL + "?" + query); + } + + /** + * + * @return + */ + public String getDatasetURL() { + return extractBaseURL( request, config ) + request.getRequestURI(); + } + + } // end of class Worker + + + // ----- HELPERS --------- + + /** + * + * @param request + * @param config + * @return + */ + + public static String extractBaseURL( final HttpServletRequest request, + final ConfigReader config ) { + if (config.getBaseURL() != null) { + return config.getBaseURL(); + } else if ((request.getServerPort() == 80) + || (request.getServerPort() == 443)) { + return request.getScheme() + "://" + + request.getServerName(); + } else { + return request.getScheme() + "://" + + request.getServerName() + ":" + request.getServerPort(); + } + } + +} diff --git a/api/src/main/java/org/linkeddatafragments/fragments/IFragmentRequestParser.java b/api/src/main/java/org/linkeddatafragments/fragments/IFragmentRequestParser.java new file mode 100644 index 000000000..113621076 --- /dev/null +++ b/api/src/main/java/org/linkeddatafragments/fragments/IFragmentRequestParser.java @@ -0,0 +1,29 @@ +package org.linkeddatafragments.fragments; + +import org.linkeddatafragments.config.ConfigReader; + +import javax.servlet.http.HttpServletRequest; + +/** + * Parses HTTP requests into specific {@link ILinkedDataFragmentRequest}s. + * + * @author Olaf Hartig + */ +public interface IFragmentRequestParser +{ + /** + * Parses the given HTTP request into a specific + * {@link ILinkedDataFragmentRequest}. + * + * @param httpRequest + * @param config + * @return + * @throws IllegalArgumentException + * If the given HTTP request cannot be interpreted (perhaps due to + * missing request parameters). + */ + ILinkedDataFragmentRequest parseIntoFragmentRequest( + final HttpServletRequest httpRequest, + final ConfigReader config) + throws IllegalArgumentException; +} diff --git a/api/src/main/java/org/linkeddatafragments/fragments/ILinkedDataFragment.java b/api/src/main/java/org/linkeddatafragments/fragments/ILinkedDataFragment.java new file mode 100644 index 000000000..5fa14aa98 --- /dev/null +++ b/api/src/main/java/org/linkeddatafragments/fragments/ILinkedDataFragment.java @@ -0,0 +1,79 @@ +package org.linkeddatafragments.fragments; + +import org.apache.jena.rdf.model.StmtIterator; + +import java.io.Closeable; + +/** + * Represents any possible Linked Data Fragment. + * + * @author Olaf Hartig + */ +public interface ILinkedDataFragment extends Closeable +{ + /** + * Returns an iterator over the RDF data of this fragment (possibly only + * partial if the data is paged, as indicated by {@link #isPageOnly()}). + * @return + */ + StmtIterator getTriples(); + + /** + * Returns true if {@link #getTriples()} returns a page of data only. + * In this case, {@link #getPageNumber()} can be used to obtain the + * corresponding page number. + * @return + */ + boolean isPageOnly(); + + /** + * Returns the number of the page of data returned by {@link #getTriples()} + * if the data is paged (that is, if {@link #isPageOnly()} returns true). + * + * If the data is not paged, this method throws an exception. + * + * @return + * @throws UnsupportedOperationException + * If the data of this fragment is not paged. + */ + long getPageNumber() throws UnsupportedOperationException; + + /** + * Returns true if {@link #getTriples()} returns a page of data only and + * this is the last page of the fragment. + * + * If the data is not paged (i.e., if {@link #isPageOnly()} returns false), + * this method throws an exception. + * + * @return + * @throws UnsupportedOperationException + * If the data of this fragment is not paged. + */ + boolean isLastPage() throws UnsupportedOperationException; + + /** + * Returns the maximum number of triples per page if {@link #getTriples()} + * returns a page of data only (that is, if {@link #isPageOnly()} returns + * true). + * + * If the data is not paged, this method throws an exception. + * + * @return + * @throws UnsupportedOperationException + * If the data of this fragment is not paged. + */ + long getMaxPageSize() throws UnsupportedOperationException; + + /** + * Returns an iterator over the metadata of this fragment. + * @return + */ + StmtIterator getMetadata(); + + /** + * Returns an iterator over an RDF description of the controls associated + * with this fragment. + * @return + */ + StmtIterator getControls(); +} diff --git a/api/src/main/java/org/linkeddatafragments/fragments/ILinkedDataFragmentRequest.java b/api/src/main/java/org/linkeddatafragments/fragments/ILinkedDataFragmentRequest.java new file mode 100644 index 000000000..ed9aa5df5 --- /dev/null +++ b/api/src/main/java/org/linkeddatafragments/fragments/ILinkedDataFragmentRequest.java @@ -0,0 +1,48 @@ +package org.linkeddatafragments.fragments; + +/** + * Basis for representing a request of some type of Linked Data Fragment (LDF). + * + * @author Olaf Hartig + */ +public interface ILinkedDataFragmentRequest +{ + + /** + * + */ + public final static long TRIPLESPERPAGE = 100L; + + /** + * + */ + public final static String PARAMETERNAME_PAGE = "page"; + + /** + * Returns the URL of the requested LDF. + * @return + */ + String getFragmentURL(); + + /** + * Returns the URL of the dataset to which the requested LDF belongs. + * @return + */ + String getDatasetURL(); + + /** + * Returns true if the request is for a specific page of the requested + * fragment. In this case, {@link #getPageNumber()} can be used to obtain + * the requested page number. + * @return + */ + boolean isPageRequest(); + + /** + * Returns the number of the page requested for the LDF; if this is not a + * page-based request (that is, if {@link #isPageRequest()} returns true), + * then this method returns 1. + * @return + */ + long getPageNumber(); +} diff --git a/api/src/main/java/org/linkeddatafragments/fragments/LinkedDataFragmentBase.java b/api/src/main/java/org/linkeddatafragments/fragments/LinkedDataFragmentBase.java new file mode 100644 index 000000000..9ac16bd9b --- /dev/null +++ b/api/src/main/java/org/linkeddatafragments/fragments/LinkedDataFragmentBase.java @@ -0,0 +1,189 @@ +package org.linkeddatafragments.fragments; + +import org.apache.http.client.utils.URIBuilder; +import org.apache.jena.rdf.model.Literal; +import org.apache.jena.rdf.model.Model; +import org.apache.jena.rdf.model.ModelFactory; +import org.apache.jena.rdf.model.Resource; +import org.apache.jena.rdf.model.StmtIterator; +import org.linkeddatafragments.util.CommonResources; + +import java.net.URISyntaxException; + + +/** + * Base class of any implementation of {@link ILinkedDataFragment} that uses + * paging. + * + * @author Olaf Hartig + */ +public abstract class LinkedDataFragmentBase implements ILinkedDataFragment +{ + + /** + * + */ + public final String fragmentURL; + + /** + * + */ + public final String datasetURL; + + /** + * + */ + public final long pageNumber; + + /** + * + */ + public final boolean isLastPage; + + /** + * + * @param fragmentURL + * @param datasetURL + * @param pageNumber + * @param isLastPage + */ + protected LinkedDataFragmentBase( final String fragmentURL, + final String datasetURL, + final long pageNumber, + final boolean isLastPage ) + { + this.fragmentURL = fragmentURL; + this.datasetURL = datasetURL; + this.pageNumber = pageNumber; + this.isLastPage = isLastPage; + } + + /** + * Does nothing. May be overridden by subclasses that hold some objects + * that need to be closed (such as iterators from the underlying data + * source). + */ + @Override + public void close() {} + + @Override + public boolean isPageOnly() { + return true; + } + + @Override + public long getPageNumber() { + return pageNumber; + } + + @Override + public boolean isLastPage() { + return isLastPage; + } + + @Override + public long getMaxPageSize() { + return ILinkedDataFragmentRequest.TRIPLESPERPAGE; + } + + /** + * This implementation uses {@link #addMetadata(Model)}, which should be + * overridden in subclasses (instead of overriding this method). + * @return + */ + @Override + public StmtIterator getMetadata() + { + final Model output = ModelFactory.createDefaultModel(); + addMetadata( output ); + return output.listStatements(); + } + + /** + * This implementation uses {@link #addControls(Model)}, which should be + * overridden in subclasses (instead of overriding this method). + * @return + */ + @Override + public StmtIterator getControls() + { + final Model output = ModelFactory.createDefaultModel(); + addControls( output ); + return output.listStatements(); + } + + /** + * Adds some basic metadata to the given RDF model. + * This method may be overridden in subclasses. + * @param model + */ + public void addMetadata( final Model model ) + { + final Resource datasetId = model.createResource( getDatasetURI() ); + final Resource fragmentId = model.createResource( fragmentURL ); + + datasetId.addProperty( CommonResources.RDF_TYPE, CommonResources.VOID_DATASET ); + datasetId.addProperty( CommonResources.RDF_TYPE, CommonResources.HYDRA_COLLECTION ); + datasetId.addProperty( CommonResources.VOID_SUBSET, fragmentId ); + + Literal itemsPerPage = model.createTypedLiteral(this.getMaxPageSize()); + datasetId.addProperty( CommonResources.HYDRA_ITEMSPERPAGE, itemsPerPage); + + fragmentId.addProperty( CommonResources.RDF_TYPE, CommonResources.HYDRA_COLLECTION ); + fragmentId.addProperty( CommonResources.RDF_TYPE, CommonResources.HYDRA_PAGEDCOLLECTION ); + } + + /** + * Adds an RDF description of page links to the given RDF model. + * This method may be overridden in subclasses. + * @param model + */ + public void addControls( final Model model ) + { + final URIBuilder pagedURL; + try { + pagedURL = new URIBuilder( fragmentURL ); + } + catch ( URISyntaxException e ) { + throw new IllegalArgumentException( e ); + } + + final Resource fragmentId = model.createResource( fragmentURL ); + + final Resource firstPageId = + model.createResource( + pagedURL.setParameter(ILinkedDataFragmentRequest.PARAMETERNAME_PAGE, + "1").toString() ); + + fragmentId.addProperty( CommonResources.HYDRA_FIRSTPAGE, firstPageId ); + + if ( pageNumber > 1) { + final String prevPageNumber = Long.toString( pageNumber - 1 ); + final Resource prevPageId = + model.createResource( + pagedURL.setParameter(ILinkedDataFragmentRequest.PARAMETERNAME_PAGE, + prevPageNumber).toString() ); + + fragmentId.addProperty( CommonResources.HYDRA_PREVIOUSPAGE, prevPageId ); + } + + if ( ! isLastPage ) { + final String nextPageNumber = Long.toString( pageNumber + 1 ); + final Resource nextPageId = + model.createResource( + pagedURL.setParameter(ILinkedDataFragmentRequest.PARAMETERNAME_PAGE, + nextPageNumber).toString() ); + + fragmentId.addProperty( CommonResources.HYDRA_NEXTPAGE, nextPageId ); + } + } + + /** + * + * @return + */ + public String getDatasetURI() { + return datasetURL + "#dataset"; + } + +} diff --git a/api/src/main/java/org/linkeddatafragments/fragments/LinkedDataFragmentRequestBase.java b/api/src/main/java/org/linkeddatafragments/fragments/LinkedDataFragmentRequestBase.java new file mode 100644 index 000000000..c53ae5a45 --- /dev/null +++ b/api/src/main/java/org/linkeddatafragments/fragments/LinkedDataFragmentRequestBase.java @@ -0,0 +1,81 @@ +package org.linkeddatafragments.fragments; + +/** + * Base class for implementations of {@link ILinkedDataFragmentRequest}. + * + * @author Olaf Hartig + */ +public abstract class LinkedDataFragmentRequestBase + implements ILinkedDataFragmentRequest +{ + + /** + * + */ + public final String fragmentURL; + + /** + * + */ + public final String datasetURL; + + /** + * + */ + public final boolean pageNumberWasRequested; + + /** + * + */ + public final long pageNumber; + + /** + * + * @param fragmentURL + * @param datasetURL + * @param pageNumberWasRequested + * @param pageNumber + */ + public LinkedDataFragmentRequestBase( final String fragmentURL, + final String datasetURL, + final boolean pageNumberWasRequested, + final long pageNumber ) + { + this.fragmentURL = fragmentURL; + this.datasetURL = datasetURL; + this.pageNumberWasRequested = pageNumberWasRequested; + this.pageNumber = (pageNumberWasRequested) ? pageNumber : 1L; + } + + @Override + public String getFragmentURL() { + return fragmentURL; + } + + @Override + public String getDatasetURL() { + return datasetURL; + } + + @Override + public boolean isPageRequest() { + return pageNumberWasRequested; + } + + @Override + public long getPageNumber() { + return pageNumber; + } + + @Override + public String toString() + { + return "LinkedDataFragmentRequest(" + + "class: " + getClass().getName() + + ", fragmentURL: " + fragmentURL + + ", isPageRequest: " + pageNumberWasRequested + + ", pageNumber: " + pageNumber + + ")"; + } + +} diff --git a/api/src/main/java/org/linkeddatafragments/fragments/tpf/ITriplePatternElement.java b/api/src/main/java/org/linkeddatafragments/fragments/tpf/ITriplePatternElement.java new file mode 100644 index 000000000..6c6d928aa --- /dev/null +++ b/api/src/main/java/org/linkeddatafragments/fragments/tpf/ITriplePatternElement.java @@ -0,0 +1,89 @@ +package org.linkeddatafragments.fragments.tpf; + +/** + * Represents an element of a triple pattern (i.e., subject, predicate, object). + * + * @param type for representing constants in triple patterns + * (i.e., URIs and literals) + * @param type for representing named variables in triple patterns + * @param type for representing anonymous variables in triple + * patterns (i.e., variables denoted by a blank node) + * + * @author Olaf Hartig + */ +public interface ITriplePatternElement +{ + /** + * Returns true if this element is a variable (specific or unspecified). + * @return + */ + boolean isVariable(); + + /** + * Returns true if this element is a specific variable, and false if either + * it is not a variable (but a URI or literal) or it is some variable that + * is not specified. The latter (unspecified variables) is possible because + * when a client requests a triple pattern fragment, it may omit triple + * pattern related parameters. + * @return + */ + boolean isSpecificVariable(); + + /** + * Returns true if this element is a specific variable that has a name + * (i.e., it is denoted by a string that begins with a question mark), + * and false if either it is not a specific variable or it is a specific + * variable that is denoted by a blank node. + * + * If this element is a specific variable that has a name (that is, this + * method returns true), the named variable can be obtained by the method + * {@link #asNamedVariable()}. + * @return + */ + boolean isNamedVariable(); + + /** + * Returns a representation of this element as a named variable (assuming + * it is a specific variable that has a name). + * + * @return + * @throws UnsupportedOperationException + * If this element is not a specific variable that has a name + * (i.e., if {@link #isNamedVariable()} returns false). + */ + NamedVarType asNamedVariable() throws UnsupportedOperationException; + + /** + * Returns true if this element is a specific variable that does not have + * a name (i.e., it is denoted by a blank node), and false if either it is + * not a specific variable or it is a specific variable that has a name. + * + * If this element is a specific variable denoted by a blank node (that is, + * this method returns true), the blank node can be obtained by the method + * {@link #asAnonymousVariable()}. + * @return + */ + boolean isAnonymousVariable(); + + /** + * Returns a representation of this element as a blank node (assuming + * it is a specific, but non-named variable). + * + * @return + * @throws UnsupportedOperationException + * If this element is not a specific anonymous variable (i.e., + * if {@link #isAnonymousVariable()} returns false). + */ + AnonVarType asAnonymousVariable() throws UnsupportedOperationException; + + /** + * Returns a representation of this element as a constant RDF term (i.e., + * a URI or a literal). + * + * @return + * @throws UnsupportedOperationException + * If this element is not a constant RDF term but a variable + * (i.e., if {@link #isVariable()} returns true). + */ + ConstantTermType asConstantTerm() throws UnsupportedOperationException; +} diff --git a/api/src/main/java/org/linkeddatafragments/fragments/tpf/ITriplePatternFragment.java b/api/src/main/java/org/linkeddatafragments/fragments/tpf/ITriplePatternFragment.java new file mode 100644 index 000000000..9f51b34a7 --- /dev/null +++ b/api/src/main/java/org/linkeddatafragments/fragments/tpf/ITriplePatternFragment.java @@ -0,0 +1,15 @@ +package org.linkeddatafragments.fragments.tpf; + +import org.linkeddatafragments.fragments.ILinkedDataFragment; + +/** + * A Triple Pattern Fragment. + * @author Ruben Verborgh + */ +public interface ITriplePatternFragment extends ILinkedDataFragment { + /** + * Gets the total number of triples in the fragment (can be an estimate). + * @return the total number of triples + */ + public long getTotalSize(); +} diff --git a/api/src/main/java/org/linkeddatafragments/fragments/tpf/ITriplePatternFragmentRequest.java b/api/src/main/java/org/linkeddatafragments/fragments/tpf/ITriplePatternFragmentRequest.java new file mode 100644 index 000000000..ae6d15107 --- /dev/null +++ b/api/src/main/java/org/linkeddatafragments/fragments/tpf/ITriplePatternFragmentRequest.java @@ -0,0 +1,52 @@ +package org.linkeddatafragments.fragments.tpf; + +import org.linkeddatafragments.fragments.ILinkedDataFragmentRequest; + +/** + * Represents a request of a Triple Pattern Fragment (TPF). + * + * @param type for representing constants in triple patterns + * (i.e., URIs and literals) + * @param type for representing named variables in triple patterns + * @param type for representing anonymous variables in triple + * patterns (i.e., variables denoted by a blank node) + * + * @author Olaf Hartig + */ +public interface ITriplePatternFragmentRequest + extends ILinkedDataFragmentRequest +{ + + /** + * + */ + public final static String PARAMETERNAME_SUBJ = "subject"; + + /** + * + */ + public final static String PARAMETERNAME_PRED = "predicate"; + + /** + * + */ + public final static String PARAMETERNAME_OBJ = "object"; + + /** + * Returns the subject position of the requested triple pattern. + * @return + */ + ITriplePatternElement getSubject(); + + /** + * Returns the predicate position of the requested triple pattern. + * @return + */ + ITriplePatternElement getPredicate(); + + /** + * Returns the object position of the requested triple pattern. + * @return + */ + ITriplePatternElement getObject(); +} diff --git a/api/src/main/java/org/linkeddatafragments/fragments/tpf/TPFRequestParser.java b/api/src/main/java/org/linkeddatafragments/fragments/tpf/TPFRequestParser.java new file mode 100644 index 000000000..55270f251 --- /dev/null +++ b/api/src/main/java/org/linkeddatafragments/fragments/tpf/TPFRequestParser.java @@ -0,0 +1,127 @@ +package org.linkeddatafragments.fragments.tpf; + +import org.linkeddatafragments.config.ConfigReader; +import org.linkeddatafragments.fragments.FragmentRequestParserBase; +import org.linkeddatafragments.fragments.IFragmentRequestParser; +import org.linkeddatafragments.fragments.ILinkedDataFragmentRequest; +import org.linkeddatafragments.util.TriplePatternElementParser; + +import javax.servlet.http.HttpServletRequest; + +/** + * An {@link IFragmentRequestParser} for {@link ITriplePatternFragmentRequest}s. + * + * @param + * @param + * + * @author Olaf Hartig + * @param + */ +public class TPFRequestParser + extends FragmentRequestParserBase +{ + public final TriplePatternElementParser elmtParser; + + /** + * + * @param elmtParser + */ + public TPFRequestParser( + final TriplePatternElementParser elmtParser ) + { + this.elmtParser = elmtParser; + } + + /** + * + * @param httpRequest + * @param config + * @return + * @throws IllegalArgumentException + */ + @Override + protected Worker getWorker( final HttpServletRequest httpRequest, + final ConfigReader config ) + throws IllegalArgumentException + { + return new Worker( httpRequest, config ); + } + + /** + * + */ + protected class Worker extends FragmentRequestParserBase.Worker + { + + /** + * + * @param request + * @param config + */ + public Worker( final HttpServletRequest request, + final ConfigReader config ) + { + super( request, config ); + } + + /** + * + * @return + * @throws IllegalArgumentException + */ + @Override + public ILinkedDataFragmentRequest createFragmentRequest() + throws IllegalArgumentException + { + return new TriplePatternFragmentRequestImpl( + getFragmentURL(), + getDatasetURL(), + pageNumberWasRequested, + pageNumber, + getSubject(), + getPredicate(), + getObject() ); + } + + /** + * + * @return + */ + public ITriplePatternElement getSubject() { + return getParameterAsTriplePatternElement( + ITriplePatternFragmentRequest.PARAMETERNAME_SUBJ ); + } + + /** + * + * @return + */ + public ITriplePatternElement getPredicate() { + return getParameterAsTriplePatternElement( + ITriplePatternFragmentRequest.PARAMETERNAME_PRED ); + } + + /** + * + * @return + */ + public ITriplePatternElement getObject() { + return getParameterAsTriplePatternElement( + ITriplePatternFragmentRequest.PARAMETERNAME_OBJ ); + } + + /** + * + * @param paramName + * @return + */ + public ITriplePatternElement + getParameterAsTriplePatternElement( final String paramName ) + { + final String parameter = request.getParameter( paramName ); + return elmtParser.parseIntoTriplePatternElement( parameter ); + } + + } // end of class Worker + +} diff --git a/api/src/main/java/org/linkeddatafragments/fragments/tpf/TPFRequestParserForJenaBackends.java b/api/src/main/java/org/linkeddatafragments/fragments/tpf/TPFRequestParserForJenaBackends.java new file mode 100644 index 000000000..cbd38b9a3 --- /dev/null +++ b/api/src/main/java/org/linkeddatafragments/fragments/tpf/TPFRequestParserForJenaBackends.java @@ -0,0 +1,35 @@ +package org.linkeddatafragments.fragments.tpf; + +import org.apache.jena.rdf.model.RDFNode; +import org.linkeddatafragments.util.TriplePatternElementParserForJena; + +/** + * An {@link TPFRequestParser} for Jena-based backends. + * + * @author Olaf Hartig + */ +public class TPFRequestParserForJenaBackends + extends TPFRequestParser +{ + private static TPFRequestParserForJenaBackends instance = null; + + /** + * + * @return + */ + public static TPFRequestParserForJenaBackends getInstance() + { + if ( instance == null ) { + instance = new TPFRequestParserForJenaBackends(); + } + return instance; + } + + /** + * + */ + protected TPFRequestParserForJenaBackends() + { + super( TriplePatternElementParserForJena.getInstance() ); + } +} diff --git a/api/src/main/java/org/linkeddatafragments/fragments/tpf/TriplePatternElementFactory.java b/api/src/main/java/org/linkeddatafragments/fragments/tpf/TriplePatternElementFactory.java new file mode 100644 index 000000000..3537bdf59 --- /dev/null +++ b/api/src/main/java/org/linkeddatafragments/fragments/tpf/TriplePatternElementFactory.java @@ -0,0 +1,214 @@ +package org.linkeddatafragments.fragments.tpf; + +/** + * A factory for {@link ITriplePatternElement}s. + * + * @param + * type for representing constants in triple patterns (i.e., URIs and + * literals) + * @param + * type for representing named variables in triple patterns + * @param + * type for representing anonymous variables in triple patterns (i.e., + * variables denoted by a blank node) + * + * @author Olaf Hartig + */ +public class TriplePatternElementFactory +{ + + /** + * + * @return + */ + public ITriplePatternElement createUnspecifiedVariable() + { + return new UnspecifiedVariable(); + } + + /** + * + * @param v + * @return + */ + public ITriplePatternElement createNamedVariable(final NVT v ) + { + return new NamedVariable( v ); + } + + /** + * + * @param bnode + * @return + */ + public ITriplePatternElement createAnonymousVariable( + final AVT bnode ) + { + return new AnonymousVariable( bnode ); + } + + /** + * + * @param term + * @return + */ + public ITriplePatternElement createConstantRDFTerm( + final CTT term ) + { + return new ConstantRDFTerm( term ); + } + + /** + * + * @param + * @param + * @param + */ + static abstract public class Variable + implements ITriplePatternElement + { + @Override + public boolean isVariable() { return true; } + @Override + public CTT asConstantTerm() { throw new UnsupportedOperationException(); } + } + + /** + * + * @param + * @param + * @param + */ + static public class UnspecifiedVariable + extends Variable + { + @Override + public boolean isSpecificVariable() { return false; } + @Override + public boolean isNamedVariable() { return false; } + @Override + public NVT asNamedVariable() { throw new UnsupportedOperationException(); } + @Override + public boolean isAnonymousVariable() { return false; } + @Override + public AVT asAnonymousVariable() { throw new UnsupportedOperationException(); } + @Override + public String toString() { return "UnspecifiedVariable"; } + } + + /** + * + * @param + * @param + * @param + */ + static abstract public class SpecificVariable + extends Variable + { + @Override + public boolean isSpecificVariable() { return true; } + } + + /** + * + * @param + * @param + * @param + */ + static public class NamedVariable + extends SpecificVariable + { + + /** + * + */ + protected final NVT v; + + /** + * + * @param variable + */ + public NamedVariable( final NVT variable ) { v = variable; } + @Override + public boolean isNamedVariable() { return true; } + @Override + public NVT asNamedVariable() { return v; } + @Override + public boolean isAnonymousVariable() { return false; } + @Override + public AVT asAnonymousVariable() { throw new UnsupportedOperationException(); } + @Override + public String toString() { return "NamedVariable(" + v.toString() + ")"; } + } + + /** + * + * @param + * @param + * @param + */ + static public class AnonymousVariable + extends SpecificVariable + { + + /** + * + */ + protected final AVT bn; + + /** + * + * @param bnode + */ + public AnonymousVariable( final AVT bnode ) { bn = bnode; } + @Override + public boolean isNamedVariable() { return false; } + @Override + public NVT asNamedVariable() { throw new UnsupportedOperationException(); } + @Override + public boolean isAnonymousVariable() { return true; } + @Override + public AVT asAnonymousVariable() { return bn; } + @Override + public String toString() { return "AnonymousVariable(" + bn.toString() + ")"; } + } + + /** + * + * @param + * @param + * @param + */ + static public class ConstantRDFTerm + implements ITriplePatternElement + { + + /** + * + */ + protected final CTT t; + + /** + * + * @param term + */ + public ConstantRDFTerm( final CTT term ) { t = term; } + @Override + public boolean isVariable() { return false; } + @Override + public boolean isSpecificVariable() { return false; } + @Override + public boolean isNamedVariable() { return false; } + @Override + public NVT asNamedVariable() { throw new UnsupportedOperationException(); } + @Override + public boolean isAnonymousVariable() { return false; } + @Override + public AVT asAnonymousVariable() { throw new UnsupportedOperationException(); } + @Override + public CTT asConstantTerm() { return t; } + @Override + public String toString() { return "ConstantRDFTerm(" + t.toString() + ")(type: " + t.getClass().getSimpleName() + ")"; } + } + +} diff --git a/api/src/main/java/org/linkeddatafragments/fragments/tpf/TriplePatternFragmentBase.java b/api/src/main/java/org/linkeddatafragments/fragments/tpf/TriplePatternFragmentBase.java new file mode 100644 index 000000000..132e8ed98 --- /dev/null +++ b/api/src/main/java/org/linkeddatafragments/fragments/tpf/TriplePatternFragmentBase.java @@ -0,0 +1,164 @@ +package org.linkeddatafragments.fragments.tpf; + +import org.apache.jena.datatypes.xsd.XSDDatatype; +import org.apache.jena.rdf.model.Literal; +import org.apache.jena.rdf.model.Model; +import org.apache.jena.rdf.model.Resource; +import org.apache.jena.rdf.model.Statement; +import org.apache.jena.rdf.model.StmtIterator; +import org.apache.jena.util.iterator.NiceIterator; +import org.linkeddatafragments.fragments.LinkedDataFragmentBase; +import org.linkeddatafragments.util.CommonResources; + +import java.util.NoSuchElementException; + + +/** + * Base class for implementations of {@link ITriplePatternFragment}. + * + * @author Ruben Verborgh + * @author Olaf Hartig + */ +abstract public class TriplePatternFragmentBase extends LinkedDataFragmentBase + implements ITriplePatternFragment +{ + private final long totalSize; + + /** + * Creates an empty Triple Pattern Fragment. + * @param fragmentURL + * @param datasetURL + */ + public TriplePatternFragmentBase( final String fragmentURL, + final String datasetURL ) { + this( 0L, fragmentURL, datasetURL, 1, true ); + } + + /** + * Creates an empty Triple Pattern Fragment page. + * @param fragmentURL + * @param isLastPage + * @param datasetURL + * @param pageNumber + */ + public TriplePatternFragmentBase( final String fragmentURL, + final String datasetURL, + final long pageNumber, + final boolean isLastPage ) { + this( 0L, fragmentURL, datasetURL, pageNumber, isLastPage ); + } + + /** + * Creates a new Triple Pattern Fragment. + * @param totalSize the total size + * @param fragmentURL + * @param datasetURL + * @param pageNumber + * @param isLastPage + */ + public TriplePatternFragmentBase( long totalSize, + final String fragmentURL, + final String datasetURL, + final long pageNumber, + final boolean isLastPage ) { + super( fragmentURL, datasetURL, pageNumber, isLastPage ); + this.totalSize = totalSize < 0L ? 0L : totalSize; + } + + @Override + public StmtIterator getTriples() { + if ( totalSize == 0L ) + return emptyStmtIterator; + else + return getNonEmptyStmtIterator(); + } + + /** + * + * @return + */ + abstract protected StmtIterator getNonEmptyStmtIterator(); + + @Override + public long getTotalSize() { + return totalSize; + } + + @Override + public void addMetadata( final Model model ) + { + super.addMetadata( model ); + + final Resource fragmentId = model.createResource( fragmentURL ); + + final Literal totalTyped = model.createTypedLiteral( totalSize, + XSDDatatype.XSDinteger ); + final Literal limitTyped = model.createTypedLiteral( getMaxPageSize(), + XSDDatatype.XSDinteger ); + + fragmentId.addLiteral( CommonResources.VOID_TRIPLES, totalTyped ); + fragmentId.addLiteral( CommonResources.HYDRA_TOTALITEMS, totalTyped ); + fragmentId.addLiteral( CommonResources.HYDRA_ITEMSPERPAGE, limitTyped ); + } + + @Override + public void addControls( final Model model ) + { + super.addControls( model ); + + final Resource datasetId = model.createResource( getDatasetURI() ); + + final Resource triplePattern = model.createResource(); + final Resource subjectMapping = model.createResource(); + final Resource predicateMapping = model.createResource(); + final Resource objectMapping = model.createResource(); + + datasetId.addProperty( CommonResources.HYDRA_SEARCH, triplePattern ); + + triplePattern.addProperty( CommonResources.HYDRA_TEMPLATE, getTemplate() ); + triplePattern.addProperty( CommonResources.HYDRA_MAPPING, subjectMapping ); + triplePattern.addProperty( CommonResources.HYDRA_MAPPING, predicateMapping ); + triplePattern.addProperty( CommonResources.HYDRA_MAPPING, objectMapping ); + + subjectMapping.addProperty( CommonResources.HYDRA_VARIABLE, ITriplePatternFragmentRequest.PARAMETERNAME_SUBJ ); + subjectMapping.addProperty( CommonResources.HYDRA_PROPERTY, CommonResources.RDF_SUBJECT ); + + predicateMapping.addProperty( CommonResources.HYDRA_VARIABLE, ITriplePatternFragmentRequest.PARAMETERNAME_PRED ); + predicateMapping.addProperty( CommonResources.HYDRA_PROPERTY, CommonResources.RDF_PREDICATE ); + + objectMapping.addProperty( CommonResources.HYDRA_VARIABLE, ITriplePatternFragmentRequest.PARAMETERNAME_OBJ ); + objectMapping.addProperty( CommonResources.HYDRA_PROPERTY, CommonResources.RDF_OBJECT ); + } + + /** + * + * @return + */ + public String getTemplate() { + return datasetURL + "{?" + + ITriplePatternFragmentRequest.PARAMETERNAME_SUBJ + "," + + ITriplePatternFragmentRequest.PARAMETERNAME_PRED + "," + + ITriplePatternFragmentRequest.PARAMETERNAME_OBJ + "}"; + } + + /** + * + */ + public static final StmtIterator emptyStmtIterator = new EmptyStmtIterator(); + + /** + * + */ + public static class EmptyStmtIterator + extends NiceIterator + implements StmtIterator + { + + /** + * + * @return + */ + public Statement nextStatement() { throw new NoSuchElementException(); } + } + +} diff --git a/api/src/main/java/org/linkeddatafragments/fragments/tpf/TriplePatternFragmentImpl.java b/api/src/main/java/org/linkeddatafragments/fragments/tpf/TriplePatternFragmentImpl.java new file mode 100644 index 000000000..3c78d4b52 --- /dev/null +++ b/api/src/main/java/org/linkeddatafragments/fragments/tpf/TriplePatternFragmentImpl.java @@ -0,0 +1,72 @@ +package org.linkeddatafragments.fragments.tpf; + +import org.apache.jena.rdf.model.Model; +import org.apache.jena.rdf.model.StmtIterator; + + +/** + * Implementation of {@link ITriplePatternFragment}. + * + * @author Olaf Hartig + */ +public class TriplePatternFragmentImpl extends TriplePatternFragmentBase +{ + + /** + * + */ + protected final Model triples; + + /** + * Creates an empty Triple Pattern Fragment. + * @param fragmentURL + * @param datasetURL + */ + public TriplePatternFragmentImpl( final String fragmentURL, + final String datasetURL ) { + this( null, 0L, fragmentURL, datasetURL, 1, true ); + } + + /** + * Creates an empty Triple Pattern Fragment page. + * @param fragmentURL + * @param datasetURL + * @param isLastPage + * @param pageNumber + */ + public TriplePatternFragmentImpl( final String fragmentURL, + final String datasetURL, + final long pageNumber, + final boolean isLastPage ) { + this( null, 0L, fragmentURL, datasetURL, pageNumber, isLastPage ); + } + + /** + * Creates a new Triple Pattern Fragment. + * @param triples the triples (possibly partial) + * @param totalSize the total size + * @param fragmentURL + * @param datasetURL + * @param isLastPage + * @param pageNumber + */ + public TriplePatternFragmentImpl( final Model triples, + long totalSize, + final String fragmentURL, + final String datasetURL, + final long pageNumber, + final boolean isLastPage ) { + super( totalSize, fragmentURL, datasetURL, pageNumber, isLastPage ); + this.triples = triples; + } + + /** + * + * @return + */ + @Override + protected StmtIterator getNonEmptyStmtIterator() { + return triples.listStatements(); + } + +} diff --git a/api/src/main/java/org/linkeddatafragments/fragments/tpf/TriplePatternFragmentRequestImpl.java b/api/src/main/java/org/linkeddatafragments/fragments/tpf/TriplePatternFragmentRequestImpl.java new file mode 100644 index 000000000..59424630b --- /dev/null +++ b/api/src/main/java/org/linkeddatafragments/fragments/tpf/TriplePatternFragmentRequestImpl.java @@ -0,0 +1,96 @@ +package org.linkeddatafragments.fragments.tpf; + +import org.linkeddatafragments.fragments.LinkedDataFragmentRequestBase; + +/** + * An implementation of {@link ITriplePatternFragmentRequest}. + * + * @author Olaf Hartig + * @param + * @param + * @param + */ +public class TriplePatternFragmentRequestImpl + extends LinkedDataFragmentRequestBase + implements ITriplePatternFragmentRequest +{ + + /** + * + */ + public final ITriplePatternElement subject; + + /** + * + */ + public final ITriplePatternElement predicate; + + /** + * + */ + public final ITriplePatternElement object; + + /** + * + * @param fragmentURL + * @param datasetURL + * @param pageNumberWasRequested + * @param pageNumber + * @param subject + * @param predicate + * @param object + */ + public TriplePatternFragmentRequestImpl( final String fragmentURL, + final String datasetURL, + final boolean pageNumberWasRequested, + final long pageNumber, + final ITriplePatternElement subject, + final ITriplePatternElement predicate, + final ITriplePatternElement object ) + { + super( fragmentURL, datasetURL, pageNumberWasRequested, pageNumber ); + + if ( subject == null ) + throw new IllegalArgumentException(); + + if ( predicate == null ) + throw new IllegalArgumentException(); + + if ( object == null ) + throw new IllegalArgumentException(); + + this.subject = subject; + this.predicate = predicate; + this.object = object; + } + + @Override + public ITriplePatternElement getSubject() { + return subject; + } + + @Override + public ITriplePatternElement getPredicate() { + return predicate; + } + + @Override + public ITriplePatternElement getObject() { + return object; + } + + @Override + public String toString() + { + return "TriplePatternFragmentRequest(" + + "class: " + getClass().getName() + + ", subject: " + subject.toString() + + ", predicate: " + predicate.toString() + + ", object: " + object.toString() + + ", fragmentURL: " + fragmentURL + + ", isPageRequest: " + pageNumberWasRequested + + ", pageNumber: " + pageNumber + + ")"; + } + +} diff --git a/api/src/main/java/org/linkeddatafragments/servlet/LinkedDataFragmentServlet.java b/api/src/main/java/org/linkeddatafragments/servlet/LinkedDataFragmentServlet.java new file mode 100644 index 000000000..881dd4fd5 --- /dev/null +++ b/api/src/main/java/org/linkeddatafragments/servlet/LinkedDataFragmentServlet.java @@ -0,0 +1,213 @@ +package org.linkeddatafragments.servlet; + +import com.google.gson.JsonObject; +import org.apache.jena.riot.Lang; +import org.linkeddatafragments.config.ConfigReader; +import org.linkeddatafragments.datasource.DataSourceFactory; +import org.linkeddatafragments.datasource.DataSourceTypesRegistry; +import org.linkeddatafragments.datasource.IDataSource; +import org.linkeddatafragments.datasource.IDataSourceType; +import org.linkeddatafragments.datasource.index.IndexDataSource; +import org.linkeddatafragments.exceptions.DataSourceNotFoundException; +import org.linkeddatafragments.fragments.FragmentRequestParserBase; +import org.linkeddatafragments.fragments.ILinkedDataFragment; +import org.linkeddatafragments.fragments.ILinkedDataFragmentRequest; +import org.linkeddatafragments.util.MIMEParse; +import org.linkeddatafragments.views.ILinkedDataFragmentWriter; +import org.linkeddatafragments.views.LinkedDataFragmentWriterFactory; + +import javax.servlet.ServletConfig; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map.Entry; + +/** + * Servlet that responds with a Linked Data Fragment. + * + * @author Ruben Verborgh + * @author Bart Hanssens + * @author Olaf Hartig + */ +public class LinkedDataFragmentServlet extends HttpServlet { + + private final static long serialVersionUID = 1L; + + // Parameters + + /** + * + */ + public final static String CFGFILE = "configFile"; + + private ConfigReader config; + private final HashMap dataSources = new HashMap<>(); + private final Collection mimeTypes = new ArrayList<>(); + + private File getConfigFile(ServletConfig config) throws IOException { + String path = config.getServletContext().getRealPath("/"); + if (path == null) { + // this can happen when running standalone + path = System.getProperty("user.dir"); + } + File cfg = new File(path, "config-example.json"); + if (config.getInitParameter(CFGFILE) != null) { + cfg = new File(config.getInitParameter(CFGFILE)); + } + if (!cfg.exists()) { + throw new IOException("Configuration file " + cfg + " not found."); + } + if (!cfg.isFile()) { + throw new IOException("Configuration file " + cfg + " is not a file."); + } + return cfg; + } + + /** + * + * @param servletConfig + * @throws ServletException + */ + @Override + public void init(ServletConfig servletConfig) throws ServletException { + try { + // load the configuration + File configFile = getConfigFile(servletConfig); + config = new ConfigReader(new FileReader(configFile)); + + // register data source types + for ( Entry typeEntry : config.getDataSourceTypes().entrySet() ) { + DataSourceTypesRegistry.register( typeEntry.getKey(), + typeEntry.getValue() ); + } + + // register data sources + for (Entry dataSource : config.getDataSources().entrySet()) { + dataSources.put(dataSource.getKey(), DataSourceFactory.create(dataSource.getValue())); + } + + // register content types + MIMEParse.register("text/html"); + MIMEParse.register(Lang.TTL.getHeaderString()); + MIMEParse.register(Lang.JSONLD.getHeaderString()); + MIMEParse.register(Lang.NTRIPLES.getHeaderString()); + MIMEParse.register(Lang.RDFXML.getHeaderString()); + } catch (Exception e) { + throw new ServletException(e); + } + } + + /** + * + */ + @Override + public void destroy() + { + for ( IDataSource dataSource : dataSources.values() ) { + try { + dataSource.close(); + } + catch( Exception e ) { + // ignore + } + } + } + + /** + * Get the datasource + * + * @param request + * @return + * @throws IOException + */ + private IDataSource getDataSource(HttpServletRequest request) throws DataSourceNotFoundException { + String contextPath = request.getContextPath(); + String requestURI = request.getRequestURI(); + + String path = contextPath == null + ? requestURI + : requestURI.substring(contextPath.length()); + + if (path.equals("/") || path.isEmpty()) { + final String baseURL = FragmentRequestParserBase.extractBaseURL(request, config); + return new IndexDataSource(baseURL, dataSources); + } + + String dataSourceName = path.substring(1); + IDataSource dataSource = dataSources.get(dataSourceName); + if (dataSource == null) { + throw new DataSourceNotFoundException(dataSourceName); + } + return dataSource; + } + + /** + * + * @param request + * @param response + * @throws ServletException + */ + @Override + public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException { + ILinkedDataFragment fragment = null; + try { + // do conneg + String bestMatch = MIMEParse.bestMatch(request.getHeader("Accept")); + + // set additional response headers + response.setHeader("Server", "Linked Data Fragments Server"); + response.setContentType(bestMatch); + response.setCharacterEncoding("utf-8"); + + // create a writer depending on the best matching mimeType + ILinkedDataFragmentWriter writer = LinkedDataFragmentWriterFactory.create(config.getPrefixes(), dataSources, bestMatch); + + try { + + final IDataSource dataSource = getDataSource( request ); + + final ILinkedDataFragmentRequest ldfRequest = + dataSource.getRequestParser() + .parseIntoFragmentRequest( request, config ); + + fragment = dataSource.getRequestProcessor() + .createRequestedFragment( ldfRequest ); + + writer.writeFragment(response.getOutputStream(), dataSource, fragment, ldfRequest); + + } catch (DataSourceNotFoundException ex) { + try { + response.setStatus(404); + writer.writeNotFound(response.getOutputStream(), request); + } catch (Exception ex1) { + throw new ServletException(ex1); + } + } catch (Exception e) { + response.setStatus(500); + writer.writeError(response.getOutputStream(), e); + } + + } catch (Exception e) { + throw new ServletException(e); + } + finally { + // close the fragment + if ( fragment != null ) { + try { + fragment.close(); + } + catch ( Exception e ) { + // ignore + } + } + } + } + +} diff --git a/api/src/main/java/org/linkeddatafragments/util/CommonResources.java b/api/src/main/java/org/linkeddatafragments/util/CommonResources.java new file mode 100644 index 000000000..d5ca95f76 --- /dev/null +++ b/api/src/main/java/org/linkeddatafragments/util/CommonResources.java @@ -0,0 +1,139 @@ +package org.linkeddatafragments.util; + +import org.apache.jena.rdf.model.Property; +import org.apache.jena.rdf.model.ResourceFactory; + +/** + * + * @author mielvandersande + */ +@SuppressWarnings("javadoc") +/** + * All common URIs needed for parsing and serializations + */ +public class CommonResources { + + /** + * + */ + public final static String RDF = "http://www.w3.org/1999/02/22-rdf-syntax-ns#"; + + /** + * + */ + public final static Property RDF_TYPE = createProperty(RDF + "type"); + + /** + * + */ + public final static Property RDF_SUBJECT = createProperty(RDF + "subject"); + + /** + * + */ + public final static Property RDF_PREDICATE = createProperty(RDF + "predicate"); + + /** + * + */ + public final static Property RDF_OBJECT = createProperty(RDF + "object"); + + /** + * + */ + public final static String VOID = "http://rdfs.org/ns/void#"; + + /** + * + */ + public final static Property VOID_TRIPLES = createProperty(VOID + "triples"); + + /** + * + */ + public final static Property VOID_SUBSET = createProperty(VOID + "subset"); + + /** + * + */ + public final static Property VOID_DATASET = createProperty(VOID + "Dataset"); + + /** + * + */ + public final static String HYDRA = "http://www.w3.org/ns/hydra/core#"; + + /** + * + */ + public final static Property HYDRA_TOTALITEMS = createProperty(HYDRA + "totalItems"); + + /** + * + */ + public final static Property HYDRA_ITEMSPERPAGE = createProperty(HYDRA + "itemsPerPage"); + + /** + * + */ + public final static Property HYDRA_SEARCH = createProperty(HYDRA + "search"); + + /** + * + */ + public final static Property HYDRA_TEMPLATE = createProperty(HYDRA + "template"); + + /** + * + */ + public final static Property HYDRA_MAPPING = createProperty(HYDRA + "mapping"); + + /** + * + */ + public final static Property HYDRA_VARIABLE = createProperty(HYDRA + "variable"); + + /** + * + */ + public final static Property HYDRA_PROPERTY = createProperty(HYDRA + "property"); + + /** + * + */ + public final static Property HYDRA_COLLECTION = createProperty(HYDRA + "Collection"); + + /** + * + */ + public final static Property HYDRA_PAGEDCOLLECTION = createProperty(HYDRA + "PagedCollection"); + + /** + * + */ + public final static Property HYDRA_FIRSTPAGE = createProperty(HYDRA + "firstPage"); + + /** + * + */ + public final static Property HYDRA_LASTPAGE = createProperty(HYDRA + "lastPage"); + + /** + * + */ + public final static Property HYDRA_NEXTPAGE = createProperty(HYDRA + "nextPage"); + + /** + * + */ + public final static Property HYDRA_PREVIOUSPAGE = createProperty(HYDRA + "previousPage"); + + /** + * + */ + public final static Property INVALID_URI = createProperty("urn:invalid"); + + private static Property createProperty(String uri) { + return ResourceFactory.createProperty(uri); + } +} diff --git a/api/src/main/java/org/linkeddatafragments/util/MIMEParse.java b/api/src/main/java/org/linkeddatafragments/util/MIMEParse.java new file mode 100644 index 000000000..1bc679071 --- /dev/null +++ b/api/src/main/java/org/linkeddatafragments/util/MIMEParse.java @@ -0,0 +1,299 @@ +package org.linkeddatafragments.util; + +import org.apache.commons.lang3.math.NumberUtils; +import org.apache.commons.lang3.StringUtils; +import org.linkeddatafragments.exceptions.NoRegisteredMimeTypesException; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +/** + * MIME-Type Parser + * + * This class provides basic functions for handling mime-types. It can handle + * matching mime-types against a list of media-ranges. See section 14.1 of the + * HTTP specification [RFC 2616] for a complete explanation. + * + * http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.1 + * + * A port to Java of Joe Gregorio's MIME-Type Parser: + * + * http://code.google.com/p/mimeparse/ + * + * Ported by Tom Zellman. + * Extended by Miel Vander Sande + * + */ +public final class MIMEParse +{ + private final static List mimeTypes = new ArrayList<>(); + + /** + * Register mimeType in collection + * @param mimeType + */ + public static void register(String mimeType) { + mimeTypes.add(mimeType); + } + + + /** + * Parse results container + */ + protected static class ParseResults + { + String type; + + String subType; + + // !a dictionary of all the parameters for the media range + Map params; + + @Override + public String toString() + { + StringBuffer s = new StringBuffer("('" + type + "', '" + subType + + "', {"); + for (String k : params.keySet()) + s.append("'" + k + "':'" + params.get(k) + "',"); + return s.append("})").toString(); + } + } + + /** + * Carves up a mime-type and returns a ParseResults object + * + * For example, the media range 'application/xhtml;q=0.5' would get parsed + * into: + * + * ('application', 'xhtml', {'q', '0.5'}) + * @param mimeType + * @return + */ + protected static ParseResults parseMimeType(String mimeType) + { + String[] parts = StringUtils.split(mimeType, ";"); + ParseResults results = new ParseResults(); + results.params = new HashMap(); + + for (int i = 1; i < parts.length; ++i) + { + String p = parts[i]; + String[] subParts = StringUtils.split(p, '='); + if (subParts.length == 2) + results.params.put(subParts[0].trim(), subParts[1].trim()); + } + String fullType = parts[0].trim(); + + // Java URLConnection class sends an Accept header that includes a + // single "*" - Turn it into a legal wildcard. + if (fullType.equals("*")) + fullType = "*/*"; + String[] types = StringUtils.split(fullType, "/"); + results.type = types[0].trim(); + results.subType = types[1].trim(); + return results; + } + + /** + * Carves up a media range and returns a ParseResults. + * + * For example, the media range 'application/*;q=0.5' would get parsed into: + * + * ('application', '*', {'q', '0.5'}) + * + * In addition this function also guarantees that there is a value for 'q' + * in the params dictionary, filling it in with a proper default if + * necessary. + * + * @param range + * @return + */ + protected static ParseResults parseMediaRange(String range) + { + ParseResults results = parseMimeType(range); + String q = results.params.get("q"); + float f = NumberUtils.toFloat(q, 1); + if (StringUtils.isBlank(q) || f < 0 || f > 1) + results.params.put("q", "1"); + return results; + } + + /** + * Structure for holding a fitness/quality combo + */ + protected static class FitnessAndQuality implements + Comparable + { + int fitness; + + float quality; + + String mimeType; // optionally used + + /** + * + * @param fitness + * @param quality + */ + public FitnessAndQuality(int fitness, float quality) + { + this.fitness = fitness; + this.quality = quality; + } + + public int compareTo(FitnessAndQuality o) + { + if (fitness == o.fitness) + { + if (quality == o.quality) + return 0; + else + return quality < o.quality ? -1 : 1; + } + else + return fitness < o.fitness ? -1 : 1; + } + } + + /** + * Find the best match for a given mimeType against a list of media_ranges + * that have already been parsed by MimeParse.parseMediaRange(). Returns a + * tuple of the fitness value and the value of the 'q' quality parameter of + * the best match, or (-1, 0) if no match was found. Just as for + * quality_parsed(), 'parsed_ranges' must be a list of parsed media ranges. + * + * @param mimeType + * @param parsedRanges + * @return + */ + protected static FitnessAndQuality fitnessAndQualityParsed(String mimeType, + Collection parsedRanges) + { + int bestFitness = -1; + float bestFitQ = 0; + ParseResults target = parseMediaRange(mimeType); + + for (ParseResults range : parsedRanges) + { + if ((target.type.equals(range.type) || range.type.equals("*") || target.type + .equals("*")) + && (target.subType.equals(range.subType) + || range.subType.equals("*") || target.subType + .equals("*"))) + { + for (String k : target.params.keySet()) + { + int paramMatches = 0; + if (!k.equals("q") && range.params.containsKey(k) + && target.params.get(k).equals(range.params.get(k))) + { + paramMatches++; + } + int fitness = (range.type.equals(target.type)) ? 100 : 0; + fitness += (range.subType.equals(target.subType)) ? 10 : 0; + fitness += paramMatches; + if (fitness > bestFitness) + { + bestFitness = fitness; + bestFitQ = NumberUtils + .toFloat(range.params.get("q"), 0); + } + } + } + } + return new FitnessAndQuality(bestFitness, bestFitQ); + } + + /** + * Find the best match for a given mime-type against a list of ranges that + * have already been parsed by parseMediaRange(). Returns the 'q' quality + * parameter of the best match, 0 if no match was found. This function + * bahaves the same as quality() except that 'parsed_ranges' must be a list + * of parsed media ranges. + * + * @param mimeType + * @param parsedRanges + * @return + */ + protected static float qualityParsed(String mimeType, + Collection parsedRanges) + { + return fitnessAndQualityParsed(mimeType, parsedRanges).quality; + } + + /** + * Returns the quality 'q' of a mime-type when compared against the + * mediaRanges in ranges. For example: + * + * @param mimeType + * @param ranges + * @return + */ + public static float quality(String mimeType, String ranges) + { + List results = new LinkedList(); + for (String r : StringUtils.split(ranges, ',')) + results.add(parseMediaRange(r)); + return qualityParsed(mimeType, results); + } + + /** + * Takes a list of supported mime-types and finds the best match for all the + * media-ranges listed in header. The value of header must be a string that + * conforms to the format of the HTTP Accept: header. The value of + * 'supported' is a list of mime-types. + * + * MimeParse.bestMatch(Arrays.asList(new String[]{"application/xbel+xml", + * "text/xml"}), "text/*;q=0.5,*; q=0.1") 'text/xml' + * + * @param supported + * @param header + * @return + * @throws org.linkeddatafragments.exceptions.NoRegisteredMimeTypesException + */ + public static String bestMatch(List supported, String header) throws NoRegisteredMimeTypesException + { + if (supported.isEmpty()) + throw new NoRegisteredMimeTypesException(); + + List parseResults = new LinkedList(); + List weightedMatches = new LinkedList(); + for (String r : StringUtils.split(header, ',')) + parseResults.add(parseMediaRange(r)); + + for (String s : supported) + { + FitnessAndQuality fitnessAndQuality = fitnessAndQualityParsed(s, + parseResults); + fitnessAndQuality.mimeType = s; + weightedMatches.add(fitnessAndQuality); + } + Collections.sort(weightedMatches); + + FitnessAndQuality lastOne = weightedMatches + .get(weightedMatches.size() - 1); + return Float.compare(lastOne.quality, 0.0f) != 0 ? lastOne.mimeType : supported.get(0); + } + + /** + * + * @param header + * @return + * @throws NoRegisteredMimeTypesException + */ + public static String bestMatch(String header) throws NoRegisteredMimeTypesException + { + return bestMatch(mimeTypes, header); + } + + // hidden + private MIMEParse() + { + } +} diff --git a/api/src/main/java/org/linkeddatafragments/util/RDFTermParser.java b/api/src/main/java/org/linkeddatafragments/util/RDFTermParser.java new file mode 100644 index 000000000..eafa401b5 --- /dev/null +++ b/api/src/main/java/org/linkeddatafragments/util/RDFTermParser.java @@ -0,0 +1,116 @@ +package org.linkeddatafragments.util; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * Parses strings (as obtained from HTTP request parameters) into RDF terms. + * + * @param type for representing RDF terms + * + * @author Olaf Hartig + */ +abstract public class RDFTermParser +{ + + /** + * + */ + public static final Pattern STRINGPATTERN + = Pattern.compile("^\"(.*)\"(?:@(.*)|\\^\\^]*)>?)?$"); + + /** + * + * @param param + * @return + */ + public TermType parseIntoRDFNode( final String param ) + { + if ( param == null || param.isEmpty() ) + return handleUnparsableParameter( param ); + + // identify the kind of RDF term based on the first character + char firstChar = param.charAt(0); + switch ( firstChar ) + { + // blank node + case '_': + return createBlankNode( param ); + + // angular brackets indicate a URI + case '<': + return createURI( param.substring(1, param.length()-1) ); + + // quotes indicate a string + case '"': + Matcher matcher = STRINGPATTERN.matcher( param ); + if ( matcher.matches() ) { + String label = matcher.group(1); + String langTag = matcher.group(2); + String typeURI = matcher.group(3); + + if ( langTag != null ) + return createLanguageLiteral( label, langTag ); + + else if ( typeURI != null ) + return createTypedLiteral( label, typeURI ); + + else + return createPlainLiteral( label ); + } + else + return handleUnparsableParameter( param ); + + // assume it is a URI without angular brackets + default: + return createURI( param ); + } + } + + /** + * + * @param label + * @return + */ + abstract public TermType createBlankNode( final String label ); + + /** + * + * @param uri + * @return + */ + abstract public TermType createURI( final String uri ); + + /** + * + * @param label + * @param typeURI + * @return + */ + abstract public TermType createTypedLiteral( final String label, + final String typeURI ); + + /** + * + * @param label + * @param langTag + * @return + */ + abstract public TermType createLanguageLiteral( final String label, + final String langTag ); + + /** + * + * @param label + * @return + */ + abstract public TermType createPlainLiteral( final String label ); + + /** + * + * @param param + * @return + */ + abstract public TermType handleUnparsableParameter( final String param ); + +} diff --git a/api/src/main/java/org/linkeddatafragments/util/TriplePatternElementParser.java b/api/src/main/java/org/linkeddatafragments/util/TriplePatternElementParser.java new file mode 100644 index 000000000..9621bab09 --- /dev/null +++ b/api/src/main/java/org/linkeddatafragments/util/TriplePatternElementParser.java @@ -0,0 +1,80 @@ +package org.linkeddatafragments.util; + +import org.linkeddatafragments.fragments.tpf.ITriplePatternElement; +import org.linkeddatafragments.fragments.tpf.TriplePatternElementFactory; + +/** + * Parses strings (as obtained from HTTP request parameters) into + * {@link ITriplePatternElement}s. + * + * @param type for representing constants in triple patterns + * (i.e., URIs and literals) + * @param type for representing named variables in triple patterns + * @param type for representing anonymous variables in triple + * patterns (i.e., variables denoted by a blank node) + * + * @author Olaf Hartig + * @author Ruben Verborgh + */ +abstract public + class TriplePatternElementParser + extends RDFTermParser +{ + + /** + * + */ + public final TriplePatternElementFactory + factory = new TriplePatternElementFactory(); + + /** + * + * @param param + * @return + */ + public ITriplePatternElement + parseIntoTriplePatternElement( final String param ) + { + // nothing or empty indicates an unspecified variable + if ( param == null || param.isEmpty() ) + return factory.createUnspecifiedVariable(); + + // identify the kind of RDF term based on the first character + char firstChar = param.charAt(0); + switch ( firstChar ) + { + // specific variable that has a name + case '?': + { + final String varName = param.substring(1); + final NamedVarType var = createNamedVariable( varName ); + return factory.createNamedVariable( var ); + } + + // specific variable that is denoted by a blank node + case '_': + { + final AnonVarType var = createAnonymousVariable( param ); + return factory.createAnonymousVariable( var ); + } + + // assume it is an RDF term + default: + return factory.createConstantRDFTerm( parseIntoRDFNode(param) ); + } + } + + /** + * + * @param varName + * @return + */ + abstract public NamedVarType createNamedVariable( final String varName ); + + /** + * + * @param label + * @return + */ + abstract public AnonVarType createAnonymousVariable( final String label ); +} diff --git a/api/src/main/java/org/linkeddatafragments/util/TriplePatternElementParserForJena.java b/api/src/main/java/org/linkeddatafragments/util/TriplePatternElementParserForJena.java new file mode 100644 index 000000000..a54114d9b --- /dev/null +++ b/api/src/main/java/org/linkeddatafragments/util/TriplePatternElementParserForJena.java @@ -0,0 +1,128 @@ +package org.linkeddatafragments.util; + +import org.apache.jena.datatypes.RDFDatatype; +import org.apache.jena.datatypes.TypeMapper; +import org.apache.jena.rdf.model.RDFNode; +import org.apache.jena.rdf.model.ResourceFactory; + +/** + * A {@link TriplePatternElementParser} for Jena-based backends. + * + * @author Olaf Hartig + */ +public class TriplePatternElementParserForJena + extends TriplePatternElementParser +{ + private static TriplePatternElementParserForJena instance = null; + + /** + * + * @return + */ + public static TriplePatternElementParserForJena getInstance() + { + if ( instance == null ) { + instance = new TriplePatternElementParserForJena(); + } + return instance; + } + + /** + * + */ + protected TriplePatternElementParserForJena() {} + + /** + * + * @param varName + * @return + */ + @Override + public String createNamedVariable( final String varName ) + { + return varName; + } + + /** + * + * @param label + * @return + */ + @Override + public String createAnonymousVariable( final String label ) + { + return label; + } + + /** + * + * @param label + * @return + */ + @Override + public RDFNode createBlankNode(final String label ) + { + return ResourceFactory.createResource(); + } + + /** + * + * @param uri + * @return + */ + @Override + public RDFNode createURI(final String uri ) + { + return ResourceFactory.createResource( uri ); + } + + /** + * + * @param label + * @param typeURI + * @return + */ + @Override + public RDFNode createTypedLiteral(final String label, + final String typeURI ) + { + final RDFDatatype dt = TypeMapper.getInstance() + .getSafeTypeByName( typeURI ); + return ResourceFactory.createTypedLiteral( label, dt ); + } + + /** + * + * @param label + * @param languageTag + * @return + */ + @Override + public RDFNode createLanguageLiteral(final String label, + final String languageTag ) + { + return ResourceFactory.createLangLiteral( label, languageTag ); + } + + /** + * + * @param label + * @return + */ + @Override + public RDFNode createPlainLiteral(final String label ) + { + return ResourceFactory.createPlainLiteral( label ); + } + + /** + * + * @param parameter + * @return + */ + @Override + public RDFNode handleUnparsableParameter(final String parameter ) + { + return CommonResources.INVALID_URI; + } +} diff --git a/api/src/main/java/org/linkeddatafragments/views/HtmlTriplePatternFragmentWriterImpl.java b/api/src/main/java/org/linkeddatafragments/views/HtmlTriplePatternFragmentWriterImpl.java new file mode 100644 index 000000000..da5ba7abe --- /dev/null +++ b/api/src/main/java/org/linkeddatafragments/views/HtmlTriplePatternFragmentWriterImpl.java @@ -0,0 +1,145 @@ +package org.linkeddatafragments.views; + +import freemarker.template.Configuration; +import freemarker.template.Template; +import freemarker.template.TemplateException; +import freemarker.template.TemplateExceptionHandler; +import org.apache.jena.rdf.model.RDFNode; +import org.apache.jena.rdf.model.Statement; +import org.apache.jena.rdf.model.StmtIterator; +import org.linkeddatafragments.datasource.IDataSource; +import org.linkeddatafragments.datasource.index.IndexDataSource; +import org.linkeddatafragments.fragments.ILinkedDataFragment; +import org.linkeddatafragments.fragments.tpf.ITriplePatternFragment; +import org.linkeddatafragments.fragments.tpf.ITriplePatternFragmentRequest; + +import javax.servlet.ServletOutputStream; +import javax.servlet.http.HttpServletRequest; +import java.io.IOException; +import java.io.OutputStreamWriter; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +//TODO: Refactor to a composable & flexible architecture using DataSource types, fragments types and request types + +/** + * Serializes an {@link ILinkedDataFragment} to the HTML format + * + * @author Miel Vander Sande + */ +public class HtmlTriplePatternFragmentWriterImpl extends TriplePatternFragmentWriterBase implements ILinkedDataFragmentWriter { + private final Configuration cfg; + + private final Template indexTemplate; + private final Template datasourceTemplate; + private final Template notfoundTemplate; + private final Template errorTemplate; + + private final String HYDRA = "http://www.w3.org/ns/hydra/core#"; + + /** + * + * @param prefixes + * @param datasources + * @throws IOException + */ + public HtmlTriplePatternFragmentWriterImpl(Map prefixes, HashMap datasources) throws IOException { + super(prefixes, datasources); + + cfg = new Configuration(Configuration.VERSION_2_3_22); + cfg.setClassForTemplateLoading(getClass(), "/views"); + cfg.setDefaultEncoding("UTF-8"); + cfg.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER); + + indexTemplate = cfg.getTemplate("index.ftl.html"); + datasourceTemplate = cfg.getTemplate("datasource.ftl.html"); + notfoundTemplate = cfg.getTemplate("notfound.ftl.html"); + errorTemplate = cfg.getTemplate("error.ftl.html"); + } + + /** + * + * @param outputStream + * @param datasource + * @param fragment + * @param tpfRequest + * @throws IOException + * @throws TemplateException + */ + @Override + public void writeFragment(ServletOutputStream outputStream, IDataSource datasource, ITriplePatternFragment fragment, ITriplePatternFragmentRequest tpfRequest) throws IOException, TemplateException{ + Map data = new HashMap(); + + // base.ftl.html + data.put("assetsPath", "assets/"); + data.put("header", datasource.getTitle()); + data.put("date", new Date()); + + // fragment.ftl.html + data.put("datasourceUrl", tpfRequest.getDatasetURL()); + data.put("datasource", datasource); + + // Parse controls to template variables + StmtIterator controls = fragment.getControls(); + while (controls.hasNext()) { + Statement control = controls.next(); + + String predicate = control.getPredicate().getURI(); + RDFNode object = control.getObject(); + if (!object.isAnon()) { + String value = object.isURIResource() ? object.asResource().getURI() : object.asLiteral().getLexicalForm(); + data.put(predicate.replaceFirst(HYDRA, ""), value); + } + } + + // Add metadata + data.put("totalEstimate", fragment.getTotalSize()); + data.put("itemsPerPage", fragment.getMaxPageSize()); + + // Add triples and datasources + List triples = fragment.getTriples().toList(); + data.put("triples", triples); + data.put("datasources", getDatasources()); + + // Calculate start and end triple number + Long start = ((tpfRequest.getPageNumber() - 1) * fragment.getMaxPageSize()) + 1; + data.put("start", start); + data.put("end", (start - 1) + (triples.size() < fragment.getMaxPageSize() ? triples.size() : fragment.getMaxPageSize())); + + // Compose query object + Map query = new HashMap(); + query.put("subject", !tpfRequest.getSubject().isVariable() ? tpfRequest.getSubject().asConstantTerm() : ""); + query.put("predicate", !tpfRequest.getPredicate().isVariable() ? tpfRequest.getPredicate().asConstantTerm() : ""); + query.put("object", !tpfRequest.getObject().isVariable() ? tpfRequest.getObject().asConstantTerm() : ""); + data.put("query", query); + + // Get the template (uses cache internally) + Template temp = datasource instanceof IndexDataSource ? indexTemplate : datasourceTemplate; + + // Merge data-model with template + temp.process(data, new OutputStreamWriter(outputStream)); + } + + @Override + public void writeNotFound(ServletOutputStream outputStream, HttpServletRequest request) throws Exception { + Map data = new HashMap(); + data.put("assetsPath", "assets/"); + data.put("datasources", getDatasources()); + data.put("date", new Date()); + data.put("url", request.getRequestURL().toString()); + + notfoundTemplate.process(data, new OutputStreamWriter(outputStream)); + } + + @Override + public void writeError(ServletOutputStream outputStream, Exception ex) throws Exception { + Map data = new HashMap(); + data.put("assetsPath", "assets/"); + data.put("date", new Date()); + data.put("error", ex); + + errorTemplate.process(data, new OutputStreamWriter(outputStream)); + } +} diff --git a/api/src/main/java/org/linkeddatafragments/views/ILinkedDataFragmentWriter.java b/api/src/main/java/org/linkeddatafragments/views/ILinkedDataFragmentWriter.java new file mode 100644 index 000000000..adaf9cbda --- /dev/null +++ b/api/src/main/java/org/linkeddatafragments/views/ILinkedDataFragmentWriter.java @@ -0,0 +1,44 @@ +package org.linkeddatafragments.views; + +import org.linkeddatafragments.datasource.IDataSource; +import org.linkeddatafragments.fragments.ILinkedDataFragment; +import org.linkeddatafragments.fragments.ILinkedDataFragmentRequest; + +import javax.servlet.ServletOutputStream; +import javax.servlet.http.HttpServletRequest; + +/** + * Represents a possible writer to serialize an {@link ILinkedDataFragment} object + * + * @author Miel Vander Sande + */ +public interface ILinkedDataFragmentWriter { + /** + * Writes a 404 Not Found error + * + * @param outputStream The response stream to write to + * @param request Request that is unable to answer + * @throws Exception Error that occurs while serializing + */ + public void writeNotFound(ServletOutputStream outputStream, HttpServletRequest request) throws Exception; + + /** + * Writes a 5XX error + * + * @param outputStream The response stream to write to + * @param ex Exception that occurred + * @throws Exception Error that occurs while serializing + */ + public void writeError(ServletOutputStream outputStream, Exception ex) throws Exception; + + /** + * Serializes and writes a {@link ILinkedDataFragment} + * + * @param outputStream The response stream to write to + * @param datasource + * @param fragment + * @param ldfRequest Parsed request for fragment + * @throws Exception Error that occurs while serializing + */ + public void writeFragment(ServletOutputStream outputStream, IDataSource datasource, ILinkedDataFragment fragment, ILinkedDataFragmentRequest ldfRequest) throws Exception; +} diff --git a/api/src/main/java/org/linkeddatafragments/views/LinkedDataFragmentWriterBase.java b/api/src/main/java/org/linkeddatafragments/views/LinkedDataFragmentWriterBase.java new file mode 100644 index 000000000..0e7aed847 --- /dev/null +++ b/api/src/main/java/org/linkeddatafragments/views/LinkedDataFragmentWriterBase.java @@ -0,0 +1,42 @@ +package org.linkeddatafragments.views; + +import org.linkeddatafragments.datasource.IDataSource; + +import java.util.HashMap; +import java.util.Map; + +/** + * Base class of any implementation of {@link ILinkedDataFragmentWriter}. + * + * @author Miel Vander Sande + */ +public abstract class LinkedDataFragmentWriterBase implements ILinkedDataFragmentWriter { + private final Map prefixes; + private final HashMap datasources; + + /** + * + * @param prefixes + * @param datasources + */ + public LinkedDataFragmentWriterBase(Map prefixes, HashMap datasources) { + this.prefixes = prefixes; + this.datasources = datasources; + } + + /** + * + * @return + */ + public Map getPrefixes() { + return prefixes; + } + + /** + * + * @return + */ + public HashMap getDatasources() { + return datasources; + } +} diff --git a/api/src/main/java/org/linkeddatafragments/views/LinkedDataFragmentWriterFactory.java b/api/src/main/java/org/linkeddatafragments/views/LinkedDataFragmentWriterFactory.java new file mode 100644 index 000000000..efc868cc5 --- /dev/null +++ b/api/src/main/java/org/linkeddatafragments/views/LinkedDataFragmentWriterFactory.java @@ -0,0 +1,35 @@ +package org.linkeddatafragments.views; + +import org.linkeddatafragments.datasource.IDataSource; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +/** + * A factory for {@link ILinkedDataFragmentWriter}s. + * + * @author Miel Vander Sande + */ +public class LinkedDataFragmentWriterFactory { + + private final static String HTML = "text/html"; + + /** + * Creates {@link ILinkedDataFragmentWriter} for a given mimeType + * + * @param prefixes Configured prefixes to be used in serialization + * @param datasources Configured datasources + * @param mimeType mimeType to create writer for + * @return created writer + * @throws IOException + */ + public static ILinkedDataFragmentWriter create(Map prefixes, HashMap datasources, String mimeType) throws IOException { + switch (mimeType) { + case HTML: + return new HtmlTriplePatternFragmentWriterImpl(prefixes, datasources); + default: + return new RdfWriterImpl(prefixes, datasources, mimeType); + } + } +} diff --git a/api/src/main/java/org/linkeddatafragments/views/RdfWriterImpl.java b/api/src/main/java/org/linkeddatafragments/views/RdfWriterImpl.java new file mode 100644 index 000000000..f355fdb75 --- /dev/null +++ b/api/src/main/java/org/linkeddatafragments/views/RdfWriterImpl.java @@ -0,0 +1,55 @@ +package org.linkeddatafragments.views; + +import org.apache.jena.rdf.model.Model; +import org.apache.jena.rdf.model.ModelFactory; +import org.apache.jena.riot.Lang; +import org.apache.jena.riot.RDFDataMgr; +import org.apache.jena.riot.RDFLanguages; +import org.linkeddatafragments.datasource.IDataSource; +import org.linkeddatafragments.fragments.ILinkedDataFragment; +import org.linkeddatafragments.fragments.ILinkedDataFragmentRequest; + +import javax.servlet.ServletOutputStream; +import javax.servlet.http.HttpServletRequest; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +/** + * Serializes an {@link ILinkedDataFragment} to an RDF format + * + * @author Miel Vander Sande + */ +public class RdfWriterImpl extends LinkedDataFragmentWriterBase implements ILinkedDataFragmentWriter { + + private final Lang contentType; + + public RdfWriterImpl(Map prefixes, HashMap datasources, String mimeType) { + super(prefixes, datasources); + this.contentType = RDFLanguages.contentTypeToLang(mimeType); + } + + @Override + public void writeNotFound(ServletOutputStream outputStream, HttpServletRequest request) throws IOException { + outputStream.println(request.getRequestURL().toString() + " not found!"); + outputStream.close(); + } + + @Override + public void writeError(ServletOutputStream outputStream, Exception ex) throws IOException { + outputStream.println(ex.getMessage()); + outputStream.close(); + } + + @Override + public void writeFragment(ServletOutputStream outputStream, IDataSource datasource, ILinkedDataFragment fragment, ILinkedDataFragmentRequest ldfRequest) throws Exception { + final Model output = ModelFactory.createDefaultModel(); + output.setNsPrefixes(getPrefixes()); + output.add(fragment.getMetadata()); + output.add(fragment.getTriples()); + output.add(fragment.getControls()); + + RDFDataMgr.write(outputStream, output, contentType); + } + +} diff --git a/api/src/main/java/org/linkeddatafragments/views/TriplePatternFragmentWriterBase.java b/api/src/main/java/org/linkeddatafragments/views/TriplePatternFragmentWriterBase.java new file mode 100644 index 000000000..43ebc3e3b --- /dev/null +++ b/api/src/main/java/org/linkeddatafragments/views/TriplePatternFragmentWriterBase.java @@ -0,0 +1,46 @@ +package org.linkeddatafragments.views; + +import freemarker.template.TemplateException; +import org.linkeddatafragments.datasource.IDataSource; +import org.linkeddatafragments.fragments.ILinkedDataFragment; +import org.linkeddatafragments.fragments.ILinkedDataFragmentRequest; +import org.linkeddatafragments.fragments.tpf.ITriplePatternFragment; +import org.linkeddatafragments.fragments.tpf.ITriplePatternFragmentRequest; + +import javax.servlet.ServletOutputStream; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +/** + * Base class of any implementation for ITriplePatternFragment. + * + * @author Miel Vander Sande + */ +public abstract class TriplePatternFragmentWriterBase extends LinkedDataFragmentWriterBase implements ILinkedDataFragmentWriter { + + /** + * + * @param prefixes + * @param datasources + */ + public TriplePatternFragmentWriterBase(Map prefixes, HashMap datasources) { + super(prefixes, datasources); + } + + @Override + public void writeFragment(ServletOutputStream outputStream, IDataSource datasource, ILinkedDataFragment fragment, ILinkedDataFragmentRequest ldfRequest) throws Exception { + writeFragment(outputStream, datasource, (ITriplePatternFragment) fragment, (ITriplePatternFragmentRequest) ldfRequest); + } + + /** + * + * @param outputStream + * @param datasource + * @param fragment + * @param tpfRequest + * @throws IOException + * @throws TemplateException + */ + abstract public void writeFragment(ServletOutputStream outputStream, IDataSource datasource, ITriplePatternFragment fragment, ITriplePatternFragmentRequest tpfRequest) throws IOException, TemplateException; +} diff --git a/api/src/main/java/org/vivoweb/linkeddatafragments/datasource/rdfservice/RDFServiceBasedRequestProcessorForTPFs.java b/api/src/main/java/org/vivoweb/linkeddatafragments/datasource/rdfservice/RDFServiceBasedRequestProcessorForTPFs.java new file mode 100644 index 000000000..91daa8c61 --- /dev/null +++ b/api/src/main/java/org/vivoweb/linkeddatafragments/datasource/rdfservice/RDFServiceBasedRequestProcessorForTPFs.java @@ -0,0 +1,171 @@ +package org.vivoweb.linkeddatafragments.datasource.rdfservice; + +import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest; +import edu.cornell.mannlib.vitro.webapp.dao.jena.QueryUtils; +import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFService; +import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFServiceException; +import edu.cornell.mannlib.vitro.webapp.rdfservice.ResultSetConsumer; +import org.apache.jena.atlas.io.StringWriterI; +import org.apache.jena.graph.Node; +import org.apache.jena.graph.NodeFactory; +import org.apache.jena.graph.Triple; +import org.apache.jena.query.Dataset; +import org.apache.jena.query.Query; +import org.apache.jena.query.QueryExecution; +import org.apache.jena.query.QueryExecutionFactory; +import org.apache.jena.query.QueryFactory; +import org.apache.jena.query.QuerySolution; +import org.apache.jena.query.QuerySolutionMap; +import org.apache.jena.query.ResultSet; +import org.apache.jena.query.Syntax; +import org.apache.jena.rdf.model.Literal; +import org.apache.jena.rdf.model.Model; +import org.apache.jena.rdf.model.ModelFactory; +import org.apache.jena.rdf.model.RDFNode; +import org.apache.jena.rdf.model.Statement; +import org.apache.jena.rdf.model.StmtIterator; +import org.apache.jena.riot.out.NodeFormatter; +import org.apache.jena.riot.out.NodeFormatterTTL; +import org.apache.jena.tdb.TDBFactory; +import org.linkeddatafragments.datasource.AbstractRequestProcessorForTriplePatterns; +import org.linkeddatafragments.datasource.IFragmentRequestProcessor; +import org.linkeddatafragments.fragments.ILinkedDataFragment; +import org.linkeddatafragments.fragments.tpf.ITriplePatternElement; +import org.linkeddatafragments.fragments.tpf.ITriplePatternFragmentRequest; + +import java.io.File; + +public class RDFServiceBasedRequestProcessorForTPFs + extends AbstractRequestProcessorForTriplePatterns +{ + private static RDFService rdfService; + + public static void setRDFService(RDFService pRDFService) { + rdfService = pRDFService; + } + + @Override + protected Worker getTPFSpecificWorker( + final ITriplePatternFragmentRequest request ) + throws IllegalArgumentException + { + return new Worker( request ); + } + + /** + * + */ + protected class Worker + extends AbstractRequestProcessorForTriplePatterns.Worker + { + public Worker( + final ITriplePatternFragmentRequest req ) + { + super( req ); + } + + private void appendNode(StringBuilder builder, RDFNode node) { + if (node.isLiteral()) { + builder.append(literalToString(node.asLiteral())); + } else if (node.isURIResource()) { + builder.append('<' + node.asResource().getURI() + '>'); + } + } + + private String literalToString(Literal l) { + StringWriterI sw = new StringWriterI(); + NodeFormatter fmt = new NodeFormatterTTL(null, null); + fmt.formatLiteral(sw, l.asNode()); + return sw.toString(); + } + + private Node skolemize(Node node) { + if (node != null && node.isBlank()) { + return NodeFactory.createURI("bnode://" + node.getBlankNodeLabel()); + } + + return node; + } + + private RDFNode deskolemize(RDFNode node) { + if (node == null) { + return null; + } + + if (node.isResource()) { + String uri = node.asResource().getURI(); + if (uri != null && uri.startsWith("bnode://")) { + String bnodeId = uri.substring(8); + return ModelFactory.createDefaultModel().asRDFNode( + NodeFactory.createBlankNode(bnodeId) + ); + } + } + + return node; + } + + @Override + protected ILinkedDataFragment createFragment( + final ITriplePatternElement subject, + final ITriplePatternElement predicate, + final ITriplePatternElement object, + final long offset, + final long limit ) + { + try { + RDFNode nSubject = subject.isVariable() ? null : deskolemize(subject.asConstantTerm()); + RDFNode nPredicate = predicate.isVariable() ? null : deskolemize(predicate.asConstantTerm()); + RDFNode nObject = object.isVariable() ? null : deskolemize(object.asConstantTerm()); + + Model triples = rdfService.getTriples(nSubject, nPredicate, nObject, limit, offset); + if (triples == null || triples.isEmpty()) { + return createEmptyTriplePatternFragment(); + } + + if (triples.size() > 0) { + Model replacedBlankNodes = ModelFactory.createDefaultModel(); + StmtIterator iter = triples.listStatements(); + while (iter.hasNext()) { + Statement oldStmt = iter.next(); + Triple t = oldStmt.asTriple(); + replacedBlankNodes.add( + replacedBlankNodes.asStatement( + new Triple( + skolemize(t.getSubject()), + skolemize(t.getPredicate()), + skolemize(t.getObject()) + ) + ) + ); + } + + triples = replacedBlankNodes; + } + + long size = triples.size(); + long estimate = -1; + estimate = rdfService.countTriples(nSubject, nPredicate, nObject); + + // No estimate or incorrect + if (estimate < offset + size) { + estimate = (size == limit) ? offset + size + 1 : offset + size; + } + + // create the fragment + final boolean isLastPage = ( estimate < offset + limit ); + return createTriplePatternFragment( triples, estimate, isLastPage ); + } catch (RDFServiceException e) { + return createEmptyTriplePatternFragment(); + } + } + + } // end of class Worker + + + /** + * Constructor + */ + public RDFServiceBasedRequestProcessorForTPFs() { + } +} diff --git a/api/src/main/java/org/vivoweb/linkeddatafragments/datasource/rdfservice/RDFServiceDataSource.java b/api/src/main/java/org/vivoweb/linkeddatafragments/datasource/rdfservice/RDFServiceDataSource.java new file mode 100644 index 000000000..7511aad96 --- /dev/null +++ b/api/src/main/java/org/vivoweb/linkeddatafragments/datasource/rdfservice/RDFServiceDataSource.java @@ -0,0 +1,46 @@ +package org.vivoweb.linkeddatafragments.datasource.rdfservice; + +import org.linkeddatafragments.datasource.DataSourceBase; +import org.linkeddatafragments.datasource.IFragmentRequestProcessor; +import org.linkeddatafragments.fragments.IFragmentRequestParser; +import org.linkeddatafragments.fragments.tpf.TPFRequestParserForJenaBackends; + +import java.io.File; + +/** + * Experimental Jena TDB-backed data source of Basic Linked Data Fragments. + * + * @author Bart Hanssens + * @author Olaf Hartig + */ +public class RDFServiceDataSource extends DataSourceBase { + + /** + * The request processor + * + */ + protected final RDFServiceBasedRequestProcessorForTPFs requestProcessor; + + @Override + public IFragmentRequestParser getRequestParser() + { + return TPFRequestParserForJenaBackends.getInstance(); + } + + @Override + public IFragmentRequestProcessor getRequestProcessor() + { + return requestProcessor; + } + + /** + * Constructor + * + * @param title + * @param description + */ + public RDFServiceDataSource(String title, String description) { + super(title, description); + requestProcessor = new RDFServiceBasedRequestProcessorForTPFs(); + } +} diff --git a/api/src/main/java/org/vivoweb/linkeddatafragments/datasource/rdfservice/RDFServiceDataSourceType.java b/api/src/main/java/org/vivoweb/linkeddatafragments/datasource/rdfservice/RDFServiceDataSourceType.java new file mode 100644 index 000000000..417466dba --- /dev/null +++ b/api/src/main/java/org/vivoweb/linkeddatafragments/datasource/rdfservice/RDFServiceDataSourceType.java @@ -0,0 +1,31 @@ +package org.vivoweb.linkeddatafragments.datasource.rdfservice; + +import com.google.gson.JsonObject; +import org.linkeddatafragments.datasource.IDataSource; +import org.linkeddatafragments.datasource.IDataSourceType; +import org.linkeddatafragments.exceptions.DataSourceCreationException; + +import java.io.File; + +/** + * The type of Triple Pattern Fragment data sources that are backed by + * a Jena TDB instance. + * + * @author Olaf Hartig + */ +public class RDFServiceDataSourceType implements IDataSourceType +{ + @Override + public IDataSource createDataSource( final String title, + final String description, + final JsonObject settings ) + throws DataSourceCreationException + { + try { + return new RDFServiceDataSource(title, description); + } catch (Exception ex) { + throw new DataSourceCreationException(ex); + } + } + +} diff --git a/api/src/main/java/org/vivoweb/linkeddatafragments/servlet/VitroLinkedDataFragmentServlet.java b/api/src/main/java/org/vivoweb/linkeddatafragments/servlet/VitroLinkedDataFragmentServlet.java new file mode 100644 index 000000000..3e326e772 --- /dev/null +++ b/api/src/main/java/org/vivoweb/linkeddatafragments/servlet/VitroLinkedDataFragmentServlet.java @@ -0,0 +1,272 @@ +package org.vivoweb.linkeddatafragments.servlet; + +import com.google.gson.JsonObject; +import edu.cornell.mannlib.vitro.webapp.beans.Ontology; +import edu.cornell.mannlib.vitro.webapp.controller.VitroHttpServlet; +import edu.cornell.mannlib.vitro.webapp.dao.OntologyDao; +import edu.cornell.mannlib.vitro.webapp.modelaccess.ModelAccess; +import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFService; +import org.apache.commons.io.IOUtils; +import org.apache.jena.riot.Lang; +import org.linkeddatafragments.config.ConfigReader; +import org.linkeddatafragments.datasource.DataSourceFactory; +import org.linkeddatafragments.datasource.DataSourceTypesRegistry; +import org.linkeddatafragments.datasource.IDataSource; +import org.linkeddatafragments.datasource.IDataSourceType; +import org.linkeddatafragments.datasource.index.IndexDataSource; +import org.linkeddatafragments.exceptions.DataSourceNotFoundException; +import org.linkeddatafragments.fragments.FragmentRequestParserBase; +import org.linkeddatafragments.fragments.ILinkedDataFragment; +import org.linkeddatafragments.fragments.ILinkedDataFragmentRequest; +import org.linkeddatafragments.util.MIMEParse; +import org.linkeddatafragments.views.ILinkedDataFragmentWriter; +import org.vivoweb.linkeddatafragments.views.HtmlTriplePatternFragmentWriterImpl; +import org.vivoweb.linkeddatafragments.views.LinkedDataFragmentWriterFactory; +import org.vivoweb.linkeddatafragments.datasource.rdfservice.RDFServiceBasedRequestProcessorForTPFs; +import org.vivoweb.linkeddatafragments.datasource.rdfservice.RDFServiceDataSource; +import org.vivoweb.linkeddatafragments.datasource.rdfservice.RDFServiceDataSourceType; + +import javax.servlet.ServletConfig; +import javax.servlet.ServletContext; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.StringReader; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map.Entry; + +/** + * Servlet that responds with a Linked Data Fragment. + */ +public class VitroLinkedDataFragmentServlet extends VitroHttpServlet { + + private final static long serialVersionUID = 1L; + + private ConfigReader config; + private final HashMap dataSources = new HashMap<>(); + private final Collection mimeTypes = new ArrayList<>(); + + private File getConfigFile(ServletConfig config) throws IOException { + String path = config.getServletContext().getRealPath("/"); + if (path == null) { + // this can happen when running standalone + path = System.getProperty("user.dir"); + } + File cfg = new File(path, "config-example.json"); + if (!cfg.exists()) { + throw new IOException("Configuration file " + cfg + " not found."); + } + if (!cfg.isFile()) { + throw new IOException("Configuration file " + cfg + " is not a file."); + } + return cfg; + } + + @Override + public void init(ServletConfig servletConfig) throws ServletException { + try { + ServletContext ctx = servletConfig.getServletContext(); + RDFService rdfService = ModelAccess.on(ctx).getRDFService(); + RDFServiceBasedRequestProcessorForTPFs.setRDFService(rdfService); + + OntologyDao dao = ModelAccess.on(ctx).getWebappDaoFactory().getOntologyDao(); + + // load the configuration + config = new ConfigReader(new StringReader(getConfigJson(dao))); + + // register data source types + for ( Entry typeEntry : config.getDataSourceTypes().entrySet() ) { + if (!DataSourceTypesRegistry.isRegistered(typeEntry.getKey())) { + DataSourceTypesRegistry.register( typeEntry.getKey(), + typeEntry.getValue() ); + } + } + + // register data sources + for (Entry dataSource : config.getDataSources().entrySet()) { + dataSources.put(dataSource.getKey(), DataSourceFactory.create(dataSource.getValue())); + } + + // register content types + MIMEParse.register("text/html"); + MIMEParse.register(Lang.TTL.getHeaderString()); + MIMEParse.register(Lang.JSONLD.getHeaderString()); + MIMEParse.register(Lang.NTRIPLES.getHeaderString()); + MIMEParse.register(Lang.RDFXML.getHeaderString()); + + HtmlTriplePatternFragmentWriterImpl.setContextPath(servletConfig.getServletContext().getContextPath()); + } catch (Exception e) { + throw new ServletException(e); + } + } + + @Override + public void destroy() + { + for ( IDataSource dataSource : dataSources.values() ) { + try { + dataSource.close(); + } + catch( Exception e ) { + // ignore + } + } + } + + private IDataSource getDataSource(HttpServletRequest request) throws DataSourceNotFoundException { + String contextPath = request.getContextPath(); + String requestURI = request.getRequestURI(); + + String path = contextPath == null + ? requestURI + : requestURI.substring(contextPath.length()); + + if (path.startsWith("/tpf")) { + path = path.substring(4); + } + + if (path.equals("/") || path.isEmpty()) { + final String baseURL = FragmentRequestParserBase.extractBaseURL(request, config); + return new IndexDataSource(baseURL, dataSources); + } + + String dataSourceName = path.substring(1); + IDataSource dataSource = dataSources.get(dataSourceName); + if (dataSource == null) { + throw new DataSourceNotFoundException(dataSourceName); + } + + return dataSource; + } + + @Override + public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException { + int fileNamePos = request.getRequestURI().toLowerCase().lastIndexOf("tpf/assets/"); + if (fileNamePos > 0) { + try { + String fileName = request.getRequestURI().substring(fileNamePos + 11); + InputStream in = VitroLinkedDataFragmentServlet.class.getResourceAsStream(fileName); + if (in != null) { + IOUtils.copy(in, response.getOutputStream()); + } + return; + } catch (IOException ioe) { + } + } + + ILinkedDataFragment fragment = null; + try { + // do conneg + String bestMatch = MIMEParse.bestMatch(request.getHeader("Accept")); + + // set additional response headers + response.setHeader("Server", "Linked Data Fragments Server"); + response.setContentType(bestMatch); + response.setCharacterEncoding("utf-8"); + + // create a writer depending on the best matching mimeType + ILinkedDataFragmentWriter writer = LinkedDataFragmentWriterFactory.create(config.getPrefixes(), dataSources, bestMatch); + + try { + + final IDataSource dataSource = getDataSource( request ); + + final ILinkedDataFragmentRequest ldfRequest = + dataSource.getRequestParser() + .parseIntoFragmentRequest( request, config ); + + fragment = dataSource.getRequestProcessor() + .createRequestedFragment( ldfRequest ); + + response.setHeader("Access-Control-Allow-Origin", "*"); + writer.writeFragment(response.getOutputStream(), dataSource, fragment, ldfRequest); + + } catch (DataSourceNotFoundException ex) { + try { + response.setStatus(404); + writer.writeNotFound(response.getOutputStream(), request); + } catch (Exception ex1) { + throw new ServletException(ex1); + } + } catch (Exception e) { + response.setStatus(500); + writer.writeError(response.getOutputStream(), e); + } + + } catch (Exception e) { + throw new ServletException(e); + } + finally { + // close the fragment + if ( fragment != null ) { + try { + fragment.close(); + } + catch ( Exception e ) { + // ignore + } + } + } + } + + private String getConfigJson(OntologyDao dao) { + StringBuilder configJson = new StringBuilder(); + configJson.append("{\n"); + configJson.append(" \"title\": \"Linked Data Fragments server\",\n"); + configJson.append("\n"); + configJson.append(" \"datasourcetypes\": {\n"); + configJson.append(" \"RDFServiceDatasource\": \"" + RDFServiceDataSourceType.class.getCanonicalName() + "\"\n"); + configJson.append(" },\n"); + configJson.append("\n"); + configJson.append(" \"datasources\": {\n"); + configJson.append(" \"core\": {\n"); + configJson.append(" \"title\": \"core\",\n"); + configJson.append(" \"type\": \"RDFServiceDatasource\",\n"); + configJson.append(" \"description\": \"All data\"\n"); + configJson.append(" }\n"); + configJson.append(" },\n"); + configJson.append("\n"); + configJson.append(" \"prefixes\": {\n"); + configJson.append(" \"rdf\": \"http://www.w3.org/1999/02/22-rdf-syntax-ns#\",\n"); + configJson.append(" \"rdfs\": \"http://www.w3.org/2000/01/rdf-schema#\",\n"); + configJson.append(" \"hydra\": \"http://www.w3.org/ns/hydra/core#\",\n"); + configJson.append(" \"void\": \"http://rdfs.org/ns/void#\""); + + List onts = dao.getAllOntologies(); + if (onts != null) { + for (Ontology ont : onts) { + if (ont != null && ont.getPrefix() != null) { + switch (ont.getPrefix()) { + case "rdf": + case "rdfs": + case "hydra": + case "void": + break; + + default: + configJson.append(",\n"); + configJson.append(" \""); + configJson.append(ont.getPrefix()); + configJson.append("\": \""); + configJson.append(ont.getURI()); + configJson.append("\""); + break; + } + } + } + } + + configJson.append(" }\n"); + configJson.append("}\n"); + + return configJson.toString(); + } +} diff --git a/api/src/main/java/org/vivoweb/linkeddatafragments/views/HtmlTriplePatternFragmentWriterImpl.java b/api/src/main/java/org/vivoweb/linkeddatafragments/views/HtmlTriplePatternFragmentWriterImpl.java new file mode 100644 index 000000000..f46a66b30 --- /dev/null +++ b/api/src/main/java/org/vivoweb/linkeddatafragments/views/HtmlTriplePatternFragmentWriterImpl.java @@ -0,0 +1,221 @@ +package org.vivoweb.linkeddatafragments.views; + +import freemarker.template.Configuration; +import freemarker.template.Template; +import freemarker.template.TemplateException; +import freemarker.template.TemplateExceptionHandler; +import org.apache.jena.atlas.io.StringWriterI; +import org.apache.jena.rdf.model.Literal; +import org.apache.jena.rdf.model.RDFNode; +import org.apache.jena.rdf.model.Statement; +import org.apache.jena.rdf.model.StmtIterator; +import org.apache.jena.rdf.model.impl.LiteralImpl; +import org.apache.jena.riot.out.NodeFormatter; +import org.apache.jena.riot.out.NodeFormatterTTL; +import org.linkeddatafragments.datasource.IDataSource; +import org.linkeddatafragments.datasource.index.IndexDataSource; +import org.linkeddatafragments.fragments.ILinkedDataFragment; +import org.linkeddatafragments.fragments.tpf.ITriplePatternElement; +import org.linkeddatafragments.fragments.tpf.ITriplePatternFragment; +import org.linkeddatafragments.fragments.tpf.ITriplePatternFragmentRequest; +import org.linkeddatafragments.views.ILinkedDataFragmentWriter; +import org.linkeddatafragments.views.TriplePatternFragmentWriterBase; + +import javax.servlet.ServletOutputStream; +import javax.servlet.http.HttpServletRequest; +import java.io.IOException; +import java.io.OutputStreamWriter; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +//TODO: Refactor to a composable & flexible architecture using DataSource types, fragments types and request types + +/** + * Serializes an {@link ILinkedDataFragment} to the HTML format + * + * @author Miel Vander Sande + */ +public class HtmlTriplePatternFragmentWriterImpl extends TriplePatternFragmentWriterBase implements ILinkedDataFragmentWriter { + private final Configuration cfg; + + private final Template indexTemplate; + private final Template datasourceTemplate; + private final Template notfoundTemplate; + private final Template errorTemplate; + + private final String HYDRA = "http://www.w3.org/ns/hydra/core#"; + + private static String contextPath; + + public static void setContextPath(String path) { + contextPath = path; + if (!contextPath.endsWith("/")) { + contextPath += "/"; + } + } + + /** + * + * @param prefixes + * @param datasources + * @throws IOException + */ + public HtmlTriplePatternFragmentWriterImpl(Map prefixes, HashMap datasources) throws IOException { + super(prefixes, datasources); + + cfg = new Configuration(Configuration.VERSION_2_3_23); + cfg.setClassForTemplateLoading(getClass(), "/tpf"); + cfg.setDefaultEncoding("UTF-8"); + cfg.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER); + + indexTemplate = cfg.getTemplate("index.ftl.html"); + datasourceTemplate = cfg.getTemplate("datasource.ftl.html"); + notfoundTemplate = cfg.getTemplate("notfound.ftl.html"); + errorTemplate = cfg.getTemplate("error.ftl.html"); + } + + /** + * + * @param outputStream + * @param datasource + * @param fragment + * @param tpfRequest + * @throws IOException + * @throws TemplateException + */ + @Override + public void writeFragment(ServletOutputStream outputStream, IDataSource datasource, ITriplePatternFragment fragment, ITriplePatternFragmentRequest tpfRequest) throws IOException, TemplateException{ + Map data = new HashMap(); + + // base.ftl.html + data.put("homePath", (contextPath != null ? contextPath : "") + "tpf"); + data.put("assetsPath", (contextPath != null ? contextPath : "") + "tpf/assets/"); + data.put("header", datasource.getTitle()); + data.put("date", new Date()); + + // fragment.ftl.html + data.put("datasourceUrl", tpfRequest.getDatasetURL()); + data.put("datasource", datasource); + + // Parse controls to template variables + StmtIterator controls = fragment.getControls(); + while (controls.hasNext()) { + Statement control = controls.next(); + + String predicate = control.getPredicate().getURI(); + RDFNode object = control.getObject(); + if (!object.isAnon()) { + String value = object.isURIResource() ? object.asResource().getURI() : object.asLiteral().getLexicalForm(); + data.put(predicate.replaceFirst(HYDRA, ""), value); + } + } + + // Add metadata + data.put("totalEstimate", fragment.getTotalSize()); + data.put("itemsPerPage", fragment.getMaxPageSize()); + + // Add triples and datasources + List triples = fragment.getTriples().toList(); + data.put("triples", triples); + data.put("datasources", getDatasources()); + + // Calculate start and end triple number + Long start = ((tpfRequest.getPageNumber() - 1) * fragment.getMaxPageSize()) + 1; + data.put("start", start); + data.put("end", (start - 1) + (triples.size() < fragment.getMaxPageSize() ? triples.size() : fragment.getMaxPageSize())); + + // Compose query object + Map query = new HashMap(); + query.put("subject", !tpfRequest.getSubject().isVariable() ? handleCT(tpfRequest.getSubject().asConstantTerm()) : ""); + query.put("predicate", !tpfRequest.getPredicate().isVariable() ? handleCT(tpfRequest.getPredicate().asConstantTerm()) : ""); + query.put("object", !tpfRequest.getObject().isVariable() ? handleCT(tpfRequest.getObject().asConstantTerm()) : ""); + query.put("pattern", makeQueryPattern(tpfRequest)); + data.put("query", query); + + // Get the template (uses cache internally) + Template temp = datasource instanceof IndexDataSource ? indexTemplate : datasourceTemplate; + + // Merge data-model with template + temp.process(data, new OutputStreamWriter(outputStream)); + } + + private String makeQueryPattern(ITriplePatternFragmentRequest tpfRequest) { + StringBuilder pattern = new StringBuilder(); + + ITriplePatternElement subject = tpfRequest.getSubject(); + ITriplePatternElement predicate = tpfRequest.getPredicate(); + ITriplePatternElement object = tpfRequest.getObject(); + + pattern.append("{"); + + if ( ! subject.isVariable() ) { + appendNode(pattern.append(' '), subject.asConstantTerm()); + } else { + pattern.append(" ?s"); + } + + + if ( ! predicate.isVariable() ) { + appendNode(pattern.append(' '), predicate.asConstantTerm()); + } else { + pattern.append(" ?p"); + } + + if ( ! object.isVariable() ) { + appendNode(pattern.append(' '), object.asConstantTerm()); + } else { + pattern.append(" ?o"); + } + + pattern.append(" }"); + return pattern.toString(); + } + + private void appendNode(StringBuilder builder, RDFNode node) { + if (node.isLiteral()) { + builder.append(literalToString(node.asLiteral())); + } else if (node.isURIResource()) { + builder.append('<' + node.asResource().getURI() + '>'); + } + } + + private String literalToString(Literal l) { + StringWriterI sw = new StringWriterI(); + NodeFormatter fmt = new NodeFormatterTTL(null, null); + fmt.formatLiteral(sw, l.asNode()); + return sw.toString(); + } + + private Object handleCT(Object obj) { + if (obj instanceof LiteralImpl) { + return ((LiteralImpl)obj).asNode().toString(); + } + + return obj; + } + + @Override + public void writeNotFound(ServletOutputStream outputStream, HttpServletRequest request) throws Exception { + Map data = new HashMap(); + data.put("homePath", (contextPath != null ? contextPath : "") + "tpf"); + data.put("assetsPath", (contextPath != null ? contextPath : "") + "tpf/assets/"); + data.put("datasources", getDatasources()); + data.put("date", new Date()); + data.put("url", request.getRequestURL().toString()); + + notfoundTemplate.process(data, new OutputStreamWriter(outputStream)); + } + + @Override + public void writeError(ServletOutputStream outputStream, Exception ex) throws Exception { + Map data = new HashMap(); + data.put("homePath", (contextPath != null ? contextPath : "") + "tpf"); + data.put("assetsPath", (contextPath != null ? contextPath : "") + "tpf/assets/"); + data.put("date", new Date()); + data.put("error", ex); + + errorTemplate.process(data, new OutputStreamWriter(outputStream)); + } +} diff --git a/api/src/main/java/org/vivoweb/linkeddatafragments/views/LinkedDataFragmentWriterFactory.java b/api/src/main/java/org/vivoweb/linkeddatafragments/views/LinkedDataFragmentWriterFactory.java new file mode 100644 index 000000000..ebc933f9f --- /dev/null +++ b/api/src/main/java/org/vivoweb/linkeddatafragments/views/LinkedDataFragmentWriterFactory.java @@ -0,0 +1,36 @@ +package org.vivoweb.linkeddatafragments.views; + +import org.linkeddatafragments.datasource.IDataSource; +import org.linkeddatafragments.views.ILinkedDataFragmentWriter; +import org.linkeddatafragments.views.RdfWriterImpl; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +/** + * A factory for {@link ILinkedDataFragmentWriter}s. + * + * @author Miel Vander Sande + */ +public class LinkedDataFragmentWriterFactory { + + private final static String HTML = "text/html"; + + /** + * Creates {@link ILinkedDataFragmentWriter} for a given mimeType + * + * @param prefixes Configured prefixes to be used in serialization + * @param datasources Configured datasources + * @param mimeType mimeType to create writer for + * @return created writer + */ + public static ILinkedDataFragmentWriter create(Map prefixes, HashMap datasources, String mimeType) throws IOException { + switch (mimeType) { + case HTML: + return new HtmlTriplePatternFragmentWriterImpl(prefixes, datasources); + default: + return new RdfWriterImpl(prefixes, datasources, mimeType); + } + } +} diff --git a/api/src/main/resources/org/vivoweb/linkeddatafragments/servlet/favicon.ico b/api/src/main/resources/org/vivoweb/linkeddatafragments/servlet/favicon.ico new file mode 100644 index 000000000..65418193a Binary files /dev/null and b/api/src/main/resources/org/vivoweb/linkeddatafragments/servlet/favicon.ico differ diff --git a/api/src/main/resources/org/vivoweb/linkeddatafragments/servlet/ldf-client-ui-packaged.js b/api/src/main/resources/org/vivoweb/linkeddatafragments/servlet/ldf-client-ui-packaged.js new file mode 100644 index 000000000..2f2c5776c --- /dev/null +++ b/api/src/main/resources/org/vivoweb/linkeddatafragments/servlet/ldf-client-ui-packaged.js @@ -0,0 +1,64 @@ +/*! + * jQuery JavaScript Library v2.1.0 + * http://jquery.com/ + * + * Includes Sizzle.js + * http://sizzlejs.com/ + * + * Copyright 2005, 2014 jQuery Foundation, Inc. and other contributors + * Released under the MIT license + * http://jquery.org/license + * + * Date: 2014-01-23T21:10Z + */ +!function(e,t){"object"==typeof module&&"object"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(e,t){function n(e){var t=e.length,n=ee.type(e);return"function"!==n&&!ee.isWindow(e)&&(!(1!==e.nodeType||!t)||("array"===n||0===t||"number"==typeof t&&t>0&&t-1 in e))}function i(e,t,n){if(ee.isFunction(t))return ee.grep(e,function(e,i){return!!t.call(e,i,e)!==n});if(t.nodeType)return ee.grep(e,function(e){return e===t!==n});if("string"==typeof t){if(ae.test(t))return ee.filter(t,e,n);t=ee.filter(t,e)}return ee.grep(e,function(e){return X.call(t,e)>=0!==n})}function r(e,t){for(;(e=e[t])&&1!==e.nodeType;);return e}function s(e){var t=pe[e]={};return ee.each(e.match(fe)||[],function(e,n){t[n]=!0}),t}function o(){K.removeEventListener("DOMContentLoaded",o,!1),e.removeEventListener("load",o,!1),ee.ready()}function a(){Object.defineProperty(this.cache={},0,{get:function(){return{}}}),this.expando=ee.expando+Math.random()}function l(e,t,n){var i;if(void 0===n&&1===e.nodeType)if(i="data-"+t.replace(be,"-$1").toLowerCase(),n=e.getAttribute(i),"string"==typeof n){try{n="true"===n||"false"!==n&&("null"===n?null:+n+""===n?+n:ye.test(n)?ee.parseJSON(n):n)}catch(e){}ve.set(e,t,n)}else n=void 0;return n}function c(){return!0}function u(){return!1}function h(){try{return K.activeElement}catch(e){}}function d(e,t){return ee.nodeName(e,"table")&&ee.nodeName(11!==t.nodeType?t:t.firstChild,"tr")?e.getElementsByTagName("tbody")[0]||e.appendChild(e.ownerDocument.createElement("tbody")):e}function f(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function p(e){var t=Re.exec(e.type);return t?e.type=t[1]:e.removeAttribute("type"),e}function _(e,t){for(var n=0,i=e.length;n")).appendTo(t.documentElement),t=$e[0].contentDocument,t.write(),t.close(),n=y(e,t),$e.detach()),Me[e]=n),n}function x(e,t,n){var i,r,s,o,a=e.style;return n=n||Be(e),n&&(o=n.getPropertyValue(t)||n[t]),n&&(""!==o||ee.contains(e.ownerDocument,e)||(o=ee.style(e,t)),We.test(o)&&Fe.test(t)&&(i=a.width,r=a.minWidth,s=a.maxWidth,a.minWidth=a.maxWidth=a.width=o,o=n.width,a.width=i,a.minWidth=r,a.maxWidth=s)),void 0!==o?o+"":o}function w(e,t){return{get:function(){return e()?void delete this.get:(this.get=t).apply(this,arguments)}}}function T(e,t){if(t in e)return t;for(var n=t[0].toUpperCase()+t.slice(1),i=t,r=Ye.length;r--;)if(t=Ye[r]+n,t in e)return t;return i}function k(e,t,n){var i=Ue.exec(t);return i?Math.max(0,i[1]-(n||0))+(i[2]||"px"):t}function C(e,t,n,i,r){for(var s=n===(i?"border":"content")?4:"width"===t?1:0,o=0;s<4;s+=2)"margin"===n&&(o+=ee.css(e,n+we[s],!0,r)),i?("content"===n&&(o-=ee.css(e,"padding"+we[s],!0,r)),"margin"!==n&&(o-=ee.css(e,"border"+we[s]+"Width",!0,r))):(o+=ee.css(e,"padding"+we[s],!0,r),"padding"!==n&&(o+=ee.css(e,"border"+we[s]+"Width",!0,r)));return o}function N(e,t,n){var i=!0,r="width"===t?e.offsetWidth:e.offsetHeight,s=Be(e),o="border-box"===ee.css(e,"boxSizing",!1,s);if(r<=0||null==r){if(r=x(e,t,s),(r<0||null==r)&&(r=e.style[t]),We.test(r))return r;i=o&&(J.boxSizingReliable()||r===e.style[t]),r=parseFloat(r)||0}return r+C(e,t,n||(o?"border":"content"),i,s)+"px"}function j(e,t){for(var n,i,r,s=[],o=0,a=e.length;o=0&&n=0},isPlainObject:function(e){if("object"!==ee.type(e)||e.nodeType||ee.isWindow(e))return!1;try{if(e.constructor&&!Y.call(e.constructor.prototype,"isPrototypeOf"))return!1}catch(e){return!1}return!0},isEmptyObject:function(e){var t;for(t in e)return!1;return!0},type:function(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?Q[V.call(e)]||"object":typeof e},globalEval:function(e){var t,n=eval;e=ee.trim(e),e&&(1===e.indexOf("use strict")?(t=K.createElement("script"),t.text=e,K.head.appendChild(t).parentNode.removeChild(t)):n(e))},camelCase:function(e){return e.replace(te,"ms-").replace(ne,ie)},nodeName:function(e,t){return e.nodeName&&e.nodeName.toLowerCase()===t.toLowerCase()},each:function(e,t,i){var r,s=0,o=e.length,a=n(e);if(i){if(a)for(;sk.cacheLength&&delete e[t.shift()],e[n+" "]=i}var t=[];return e}function i(e){return e[$]=!0,e}function r(e){var t=A.createElement("div");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function s(e,t){for(var n=e.split("|"),i=e.length;i--;)k.attrHandle[n[i]]=t}function o(e,t){var n=t&&e,i=n&&1===e.nodeType&&1===t.nodeType&&(~t.sourceIndex||V)-(~e.sourceIndex||V);if(i)return i;if(n)for(;n=n.nextSibling;)if(n===t)return-1;return e?1:-1}function a(e){return function(t){var n=t.nodeName.toLowerCase();return"input"===n&&t.type===e}}function l(e){return function(t){var n=t.nodeName.toLowerCase();return("input"===n||"button"===n)&&t.type===e}}function c(e){return i(function(t){return t=+t,i(function(n,i){for(var r,s=e([],n.length,t),o=s.length;o--;)n[r=s[o]]&&(n[r]=!(i[r]=n[r]))})})}function u(e){return e&&typeof e.getElementsByTagName!==Q&&e}function h(){}function d(e,n){var i,r,s,o,a,l,c,u=z[e+" "];if(u)return n?0:u.slice(0);for(a=e,l=[],c=k.preFilter;a;){i&&!(r=ce.exec(a))||(r&&(a=a.slice(r[0].length)||a),l.push(s=[])),i=!1,(r=ue.exec(a))&&(i=r.shift(),s.push({value:i,type:r[0].replace(le," ")}),a=a.slice(i.length));for(o in k.filter)!(r=pe[o].exec(a))||c[o]&&!(r=c[o](r))||(i=r.shift(),s.push({value:i,type:o,matches:r}),a=a.slice(i.length));if(!i)break}return n?a.length:a?t.error(e):z(e,l).slice(0)}function f(e){for(var t=0,n=e.length,i="";t1?function(t,n,i){for(var r=e.length;r--;)if(!e[r](t,n,i))return!1;return!0}:e[0]}function g(e,t,n,i,r){for(var s,o=[],a=0,l=e.length,c=null!=t;a-1&&(i[c]=!(o[c]=h))}}else v=g(v===o?v.splice(p,v.length):v),s?s(null,o,v,l):Z.apply(o,v)})}function v(e){for(var t,n,i,r=e.length,s=k.relative[e[0].type],o=s||k.relative[" "],a=s?1:0,l=p(function(e){return e===t},o,!0),c=p(function(e){return te.call(t,e)>-1},o,!0),u=[function(e,n,i){return!s&&(i||n!==S)||((t=n).nodeType?l(e,n,i):c(e,n,i))}];a1&&_(u),a>1&&f(e.slice(0,a-1).concat({value:" "===e[a-2].type?"*":""})).replace(le,"$1"),n,a0,s=e.length>0,o=function(i,o,a,l,c){var u,h,d,f=0,p="0",_=i&&[],m=[],v=S,y=i||s&&k.find.TAG("*",c),b=F+=null==v?1:Math.random()||.1,x=y.length;for(c&&(S=o!==A&&o);p!==x&&null!=(u=y[p]);p++){if(s&&u){for(h=0;d=e[h++];)if(d(u,o,a)){l.push(u);break}c&&(F=b)}r&&((u=!d&&u)&&f--,i&&_.push(u))}if(f+=p,r&&p!==f){for(h=0;d=n[h++];)d(_,m,o,a);if(i){if(f>0)for(;p--;)_[p]||m[p]||(m[p]=J.call(l));m=g(m)}Z.apply(l,m),c&&!i&&m.length>0&&f+n.length>1&&t.uniqueSort(l)}return c&&(F=b,S=v),_};return r?i(o):o}function b(e,n,i){for(var r=0,s=n.length;r2&&"ID"===(o=s[0]).type&&T.getById&&9===t.nodeType&&L&&k.relative[s[1].type]){if(t=(k.find.ID(o.matches[0].replace(xe,we),t)||[])[0],!t)return n;e=e.slice(s.shift().value.length)}for(r=pe.needsContext.test(e)?0:s.length;r--&&(o=s[r],!k.relative[a=o.type]);)if((l=k.find[a])&&(i=l(o.matches[0].replace(xe,we),ye.test(s[0].type)&&u(t.parentNode)||t))){if(s.splice(r,1),e=i.length&&f(s),!e)return Z.apply(n,i),n;break}}return j(e,c)(i,t,!L,n,ye.test(e)&&u(t.parentNode)||t),n}var w,T,k,C,N,j,S,E,q,D,A,O,L,I,R,H,P,$="sizzle"+-new Date,M=e.document,F=0,W=0,B=n(),z=n(),U=n(),X=function(e,t){return e===t&&(q=!0),0},Q="undefined",V=1<<31,Y={}.hasOwnProperty,G=[],J=G.pop,K=G.push,Z=G.push,ee=G.slice,te=G.indexOf||function(e){for(var t=0,n=this.length;t+~]|"+ie+")"+ie+"*"),he=new RegExp("="+ie+"*([^\\]'\"]*?)"+ie+"*\\]","g"),de=new RegExp(ae),fe=new RegExp("^"+se+"$"),pe={ID:new RegExp("^#("+re+")"),CLASS:new RegExp("^\\.("+re+")"),TAG:new RegExp("^("+re.replace("w","w*")+")"),ATTR:new RegExp("^"+oe),PSEUDO:new RegExp("^"+ae),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+ie+"*(even|odd|(([+-]|)(\\d*)n|)"+ie+"*(?:([+-]|)"+ie+"*(\\d+)|))"+ie+"*\\)|)","i"),bool:new RegExp("^(?:"+ne+")$","i"),needsContext:new RegExp("^"+ie+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+ie+"*((?:-\\d)?\\d*)"+ie+"*\\)|)(?=[^-]|$)","i")},_e=/^(?:input|select|textarea|button)$/i,ge=/^h\d$/i,me=/^[^{]+\{\s*\[native \w/,ve=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ye=/[+~]/,be=/'|\\/g,xe=new RegExp("\\\\([\\da-f]{1,6}"+ie+"?|("+ie+")|.)","ig"),we=function(e,t,n){var i="0x"+t-65536;return i!==i||n?t:i<0?String.fromCharCode(i+65536):String.fromCharCode(i>>10|55296,1023&i|56320)};try{Z.apply(G=ee.call(M.childNodes),M.childNodes),G[M.childNodes.length].nodeType}catch(e){Z={apply:G.length?function(e,t){K.apply(e,ee.call(t))}:function(e,t){for(var n=e.length,i=0;e[n++]=t[i++];);e.length=n-1}}}T=t.support={},N=t.isXML=function(e){var t=e&&(e.ownerDocument||e).documentElement;return!!t&&"HTML"!==t.nodeName},D=t.setDocument=function(e){var t,n=e?e.ownerDocument||e:M,i=n.defaultView;return n!==A&&9===n.nodeType&&n.documentElement?(A=n,O=n.documentElement,L=!N(n),i&&i!==i.top&&(i.addEventListener?i.addEventListener("unload",function(){D()},!1):i.attachEvent&&i.attachEvent("onunload",function(){D()})),T.attributes=r(function(e){return e.className="i",!e.getAttribute("className")}),T.getElementsByTagName=r(function(e){return e.appendChild(n.createComment("")),!e.getElementsByTagName("*").length}),T.getElementsByClassName=me.test(n.getElementsByClassName)&&r(function(e){return e.innerHTML="
",e.firstChild.className="i",2===e.getElementsByClassName("i").length}),T.getById=r(function(e){return O.appendChild(e).id=$,!n.getElementsByName||!n.getElementsByName($).length}),T.getById?(k.find.ID=function(e,t){if(typeof t.getElementById!==Q&&L){var n=t.getElementById(e);return n&&n.parentNode?[n]:[]}},k.filter.ID=function(e){var t=e.replace(xe,we);return function(e){return e.getAttribute("id")===t}}):(delete k.find.ID,k.filter.ID=function(e){var t=e.replace(xe,we);return function(e){var n=typeof e.getAttributeNode!==Q&&e.getAttributeNode("id");return n&&n.value===t}}),k.find.TAG=T.getElementsByTagName?function(e,t){if(typeof t.getElementsByTagName!==Q)return t.getElementsByTagName(e)}:function(e,t){var n,i=[],r=0,s=t.getElementsByTagName(e);if("*"===e){for(;n=s[r++];)1===n.nodeType&&i.push(n);return i}return s},k.find.CLASS=T.getElementsByClassName&&function(e,t){if(typeof t.getElementsByClassName!==Q&&L)return t.getElementsByClassName(e)},R=[],I=[],(T.qsa=me.test(n.querySelectorAll))&&(r(function(e){e.innerHTML="",e.querySelectorAll("[t^='']").length&&I.push("[*^$]="+ie+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||I.push("\\["+ie+"*(?:value|"+ne+")"),e.querySelectorAll(":checked").length||I.push(":checked")}),r(function(e){var t=n.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&I.push("name"+ie+"*[*^$|!~]?="),e.querySelectorAll(":enabled").length||I.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),I.push(",.*:")})),(T.matchesSelector=me.test(H=O.webkitMatchesSelector||O.mozMatchesSelector||O.oMatchesSelector||O.msMatchesSelector))&&r(function(e){T.disconnectedMatch=H.call(e,"div"),H.call(e,"[s!='']:x"),R.push("!=",ae)}),I=I.length&&new RegExp(I.join("|")),R=R.length&&new RegExp(R.join("|")),t=me.test(O.compareDocumentPosition),P=t||me.test(O.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,i=t&&t.parentNode;return e===i||!(!i||1!==i.nodeType||!(n.contains?n.contains(i):e.compareDocumentPosition&&16&e.compareDocumentPosition(i)))}:function(e,t){if(t)for(;t=t.parentNode;)if(t===e)return!0;return!1},X=t?function(e,t){if(e===t)return q=!0,0;var i=!e.compareDocumentPosition-!t.compareDocumentPosition;return i?i:(i=(e.ownerDocument||e)===(t.ownerDocument||t)?e.compareDocumentPosition(t):1,1&i||!T.sortDetached&&t.compareDocumentPosition(e)===i?e===n||e.ownerDocument===M&&P(M,e)?-1:t===n||t.ownerDocument===M&&P(M,t)?1:E?te.call(E,e)-te.call(E,t):0:4&i?-1:1)}:function(e,t){if(e===t)return q=!0,0;var i,r=0,s=e.parentNode,a=t.parentNode,l=[e],c=[t];if(!s||!a)return e===n?-1:t===n?1:s?-1:a?1:E?te.call(E,e)-te.call(E,t):0;if(s===a)return o(e,t);for(i=e;i=i.parentNode;)l.unshift(i);for(i=t;i=i.parentNode;)c.unshift(i);for(;l[r]===c[r];)r++;return r?o(l[r],c[r]):l[r]===M?-1:c[r]===M?1:0},n):A},t.matches=function(e,n){return t(e,null,null,n)},t.matchesSelector=function(e,n){if((e.ownerDocument||e)!==A&&D(e),n=n.replace(he,"='$1']"),T.matchesSelector&&L&&(!R||!R.test(n))&&(!I||!I.test(n)))try{var i=H.call(e,n);if(i||T.disconnectedMatch||e.document&&11!==e.document.nodeType)return i}catch(e){}return t(n,A,null,[e]).length>0},t.contains=function(e,t){return(e.ownerDocument||e)!==A&&D(e),P(e,t)},t.attr=function(e,t){(e.ownerDocument||e)!==A&&D(e);var n=k.attrHandle[t.toLowerCase()],i=n&&Y.call(k.attrHandle,t.toLowerCase())?n(e,t,!L):void 0;return void 0!==i?i:T.attributes||!L?e.getAttribute(t):(i=e.getAttributeNode(t))&&i.specified?i.value:null},t.error=function(e){throw new Error("Syntax error, unrecognized expression: "+e)},t.uniqueSort=function(e){var t,n=[],i=0,r=0;if(q=!T.detectDuplicates,E=!T.sortStable&&e.slice(0),e.sort(X),q){for(;t=e[r++];)t===e[r]&&(i=n.push(r));for(;i--;)e.splice(n[i],1)}return E=null,e},C=t.getText=function(e){var t,n="",i=0,r=e.nodeType;if(r){if(1===r||9===r||11===r){if("string"==typeof e.textContent)return e.textContent;for(e=e.firstChild;e;e=e.nextSibling)n+=C(e)}else if(3===r||4===r)return e.nodeValue}else for(;t=e[i++];)n+=C(t);return n},k=t.selectors={cacheLength:50,createPseudo:i,match:pe,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(xe,we),e[3]=(e[4]||e[5]||"").replace(xe,we),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||t.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&t.error(e[0]),e},PSEUDO:function(e){var t,n=!e[5]&&e[2];return pe.CHILD.test(e[0])?null:(e[3]&&void 0!==e[4]?e[2]=e[4]:n&&de.test(n)&&(t=d(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(xe,we).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=B[e+" "];return t||(t=new RegExp("(^|"+ie+")"+e+"("+ie+"|$)"))&&B(e,function(e){return t.test("string"==typeof e.className&&e.className||typeof e.getAttribute!==Q&&e.getAttribute("class")||"")})},ATTR:function(e,n,i){return function(r){var s=t.attr(r,e);return null==s?"!="===n:!n||(s+="","="===n?s===i:"!="===n?s!==i:"^="===n?i&&0===s.indexOf(i):"*="===n?i&&s.indexOf(i)>-1:"$="===n?i&&s.slice(-i.length)===i:"~="===n?(" "+s+" ").indexOf(i)>-1:"|="===n&&(s===i||s.slice(0,i.length+1)===i+"-"))}},CHILD:function(e,t,n,i,r){var s="nth"!==e.slice(0,3),o="last"!==e.slice(-4),a="of-type"===t;return 1===i&&0===r?function(e){return!!e.parentNode}:function(t,n,l){var c,u,h,d,f,p,_=s!==o?"nextSibling":"previousSibling",g=t.parentNode,m=a&&t.nodeName.toLowerCase(),v=!l&&!a;if(g){if(s){for(;_;){for(h=t;h=h[_];)if(a?h.nodeName.toLowerCase()===m:1===h.nodeType)return!1;p=_="only"===e&&!p&&"nextSibling"}return!0}if(p=[o?g.firstChild:g.lastChild],o&&v){for(u=g[$]||(g[$]={}),c=u[e]||[],f=c[0]===F&&c[1],d=c[0]===F&&c[2],h=f&&g.childNodes[f];h=++f&&h&&h[_]||(d=f=0)||p.pop();)if(1===h.nodeType&&++d&&h===t){u[e]=[F,f,d];break}}else if(v&&(c=(t[$]||(t[$]={}))[e])&&c[0]===F)d=c[1];else for(;(h=++f&&h&&h[_]||(d=f=0)||p.pop())&&((a?h.nodeName.toLowerCase()!==m:1!==h.nodeType)||!++d||(v&&((h[$]||(h[$]={}))[e]=[F,d]),h!==t)););return d-=r,d===i||d%i===0&&d/i>=0}}},PSEUDO:function(e,n){var r,s=k.pseudos[e]||k.setFilters[e.toLowerCase()]||t.error("unsupported pseudo: "+e);return s[$]?s(n):s.length>1?(r=[e,e,"",n],k.setFilters.hasOwnProperty(e.toLowerCase())?i(function(e,t){for(var i,r=s(e,n),o=r.length;o--;)i=te.call(e,r[o]),e[i]=!(t[i]=r[o])}):function(e){return s(e,0,r)}):s}},pseudos:{not:i(function(e){var t=[],n=[],r=j(e.replace(le,"$1"));return r[$]?i(function(e,t,n,i){for(var s,o=r(e,null,i,[]),a=e.length;a--;)(s=o[a])&&(e[a]=!(t[a]=s))}):function(e,i,s){return t[0]=e,r(t,null,s,n),!n.pop()}}),has:i(function(e){return function(n){return t(e,n).length>0}}),contains:i(function(e){return function(t){return(t.textContent||t.innerText||C(t)).indexOf(e)>-1}}),lang:i(function(e){return fe.test(e||"")||t.error("unsupported lang: "+e),e=e.replace(xe,we).toLowerCase(),function(t){var n;do if(n=L?t.lang:t.getAttribute("xml:lang")||t.getAttribute("lang"))return n=n.toLowerCase(),n===e||0===n.indexOf(e+"-");while((t=t.parentNode)&&1===t.nodeType);return!1}}),target:function(t){var n=e.location&&e.location.hash;return n&&n.slice(1)===t.id},root:function(e){return e===O},focus:function(e){return e===A.activeElement&&(!A.hasFocus||A.hasFocus())&&!!(e.type||e.href||~e.tabIndex)},enabled:function(e){return e.disabled===!1},disabled:function(e){return e.disabled===!0},checked:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&!!e.checked||"option"===t&&!!e.selected},selected:function(e){return e.parentNode&&e.parentNode.selectedIndex,e.selected===!0},empty:function(e){for(e=e.firstChild;e;e=e.nextSibling)if(e.nodeType<6)return!1;return!0},parent:function(e){return!k.pseudos.empty(e)},header:function(e){return ge.test(e.nodeName)},input:function(e){return _e.test(e.nodeName)},button:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&"button"===e.type||"button"===t},text:function(e){var t;return"input"===e.nodeName.toLowerCase()&&"text"===e.type&&(null==(t=e.getAttribute("type"))||"text"===t.toLowerCase())},first:c(function(){return[0]}),last:c(function(e,t){return[t-1]}),eq:c(function(e,t,n){return[n<0?n+t:n]}),even:c(function(e,t){for(var n=0;n=0;)e.push(i);return e}),gt:c(function(e,t,n){for(var i=n<0?n+t:n;++i(?:<\/\1>|)$/,ae=/^.[^:#\[\.,]*$/;ee.filter=function(e,t,n){var i=t[0];return n&&(e=":not("+e+")"),1===t.length&&1===i.nodeType?ee.find.matchesSelector(i,e)?[i]:[]:ee.find.matches(e,ee.grep(t,function(e){return 1===e.nodeType}))},ee.fn.extend({find:function(e){var t,n=this.length,i=[],r=this;if("string"!=typeof e)return this.pushStack(ee(e).filter(function(){for(t=0;t1?ee.unique(i):i),i.selector=this.selector?this.selector+" "+e:e,i},filter:function(e){return this.pushStack(i(this,e||[],!1))},not:function(e){return this.pushStack(i(this,e||[],!0))},is:function(e){return!!i(this,"string"==typeof e&&se.test(e)?ee(e):e||[],!1).length}});var le,ce=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,ue=ee.fn.init=function(e,t){var n,i;if(!e)return this;if("string"==typeof e){if(n="<"===e[0]&&">"===e[e.length-1]&&e.length>=3?[null,e,null]:ce.exec(e),!n||!n[1]&&t)return!t||t.jquery?(t||le).find(e):this.constructor(t).find(e);if(n[1]){if(t=t instanceof ee?t[0]:t,ee.merge(this,ee.parseHTML(n[1],t&&t.nodeType?t.ownerDocument||t:K,!0)),oe.test(n[1])&&ee.isPlainObject(t))for(n in t)ee.isFunction(this[n])?this[n](t[n]):this.attr(n,t[n]);return this}return i=K.getElementById(n[2]),i&&i.parentNode&&(this.length=1,this[0]=i),this.context=K,this.selector=e,this}return e.nodeType?(this.context=this[0]=e,this.length=1,this):ee.isFunction(e)?"undefined"!=typeof le.ready?le.ready(e):e(ee):(void 0!==e.selector&&(this.selector=e.selector,this.context=e.context),ee.makeArray(e,this))};ue.prototype=ee.fn,le=ee(K);var he=/^(?:parents|prev(?:Until|All))/,de={children:!0,contents:!0,next:!0,prev:!0};ee.extend({dir:function(e,t,n){for(var i=[],r=void 0!==n;(e=e[t])&&9!==e.nodeType;)if(1===e.nodeType){if(r&&ee(e).is(n))break;i.push(e)}return i},sibling:function(e,t){for(var n=[];e;e=e.nextSibling)1===e.nodeType&&e!==t&&n.push(e);return n}}),ee.fn.extend({has:function(e){var t=ee(e,this),n=t.length;return this.filter(function(){for(var e=0;e-1:1===n.nodeType&&ee.find.matchesSelector(n,e))){s.push(n);break}return this.pushStack(s.length>1?ee.unique(s):s)},index:function(e){return e?"string"==typeof e?X.call(ee(e),this[0]):X.call(this,e.jquery?e[0]:e):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(e,t){return this.pushStack(ee.unique(ee.merge(this.get(),ee(e,t))))},addBack:function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}}),ee.each({parent:function(e){var t=e.parentNode;return t&&11!==t.nodeType?t:null},parents:function(e){return ee.dir(e,"parentNode")},parentsUntil:function(e,t,n){return ee.dir(e,"parentNode",n)},next:function(e){return r(e,"nextSibling")},prev:function(e){return r(e,"previousSibling")},nextAll:function(e){return ee.dir(e,"nextSibling")},prevAll:function(e){return ee.dir(e,"previousSibling")},nextUntil:function(e,t,n){return ee.dir(e,"nextSibling",n)},prevUntil:function(e,t,n){return ee.dir(e,"previousSibling",n)},siblings:function(e){return ee.sibling((e.parentNode||{}).firstChild,e)},children:function(e){return ee.sibling(e.firstChild)},contents:function(e){return e.contentDocument||ee.merge([],e.childNodes)}},function(e,t){ee.fn[e]=function(n,i){var r=ee.map(this,t,n);return"Until"!==e.slice(-5)&&(i=n),i&&"string"==typeof i&&(r=ee.filter(i,r)),this.length>1&&(de[e]||ee.unique(r),he.test(e)&&r.reverse()),this.pushStack(r)}});var fe=/\S+/g,pe={};ee.Callbacks=function(e){e="string"==typeof e?pe[e]||s(e):ee.extend({},e);var t,n,i,r,o,a,l=[],c=!e.once&&[],u=function(s){for(t=e.memory&&s,n=!0,a=r||0,r=0,o=l.length,i=!0;l&&a-1;)l.splice(n,1),i&&(n<=o&&o--,n<=a&&a--)}),this},has:function(e){return e?ee.inArray(e,l)>-1:!(!l||!l.length)},empty:function(){return l=[],o=0,this},disable:function(){return l=c=t=void 0,this},disabled:function(){return!l},lock:function(){return c=void 0,t||h.disable(),this},locked:function(){return!c},fireWith:function(e,t){return!l||n&&!c||(t=t||[],t=[e,t.slice?t.slice():t],i?c.push(t):u(t)),this},fire:function(){return h.fireWith(this,arguments),this},fired:function(){return!!n}};return h},ee.extend({Deferred:function(e){var t=[["resolve","done",ee.Callbacks("once memory"),"resolved"],["reject","fail",ee.Callbacks("once memory"),"rejected"],["notify","progress",ee.Callbacks("memory")]],n="pending",i={state:function(){return n},always:function(){return r.done(arguments).fail(arguments),this},then:function(){var e=arguments;return ee.Deferred(function(n){ee.each(t,function(t,s){var o=ee.isFunction(e[t])&&e[t];r[s[1]](function(){var e=o&&o.apply(this,arguments);e&&ee.isFunction(e.promise)?e.promise().done(n.resolve).fail(n.reject).progress(n.notify):n[s[0]+"With"](this===i?n.promise():this,o?[e]:arguments)})}),e=null}).promise()},promise:function(e){return null!=e?ee.extend(e,i):i}},r={};return i.pipe=i.then,ee.each(t,function(e,s){var o=s[2],a=s[3];i[s[1]]=o.add,a&&o.add(function(){n=a},t[1^e][2].disable,t[2][2].lock),r[s[0]]=function(){return r[s[0]+"With"](this===r?i:this,arguments),this},r[s[0]+"With"]=o.fireWith}),i.promise(r),e&&e.call(r,r),r},when:function(e){var t,n,i,r=0,s=B.call(arguments),o=s.length,a=1!==o||e&&ee.isFunction(e.promise)?o:0,l=1===a?e:ee.Deferred(),c=function(e,n,i){return function(r){n[e]=this,i[e]=arguments.length>1?B.call(arguments):r,i===t?l.notifyWith(n,i):--a||l.resolveWith(n,i)}};if(o>1)for(t=new Array(o),n=new Array(o),i=new Array(o);r0||(_e.resolveWith(K,[ee]),ee.fn.trigger&&ee(K).trigger("ready").off("ready")))}}),ee.ready.promise=function(t){return _e||(_e=ee.Deferred(),"complete"===K.readyState?setTimeout(ee.ready):(K.addEventListener("DOMContentLoaded",o,!1),e.addEventListener("load",o,!1))),_e.promise(t)},ee.ready.promise();var ge=ee.access=function(e,t,n,i,r,s,o){var a=0,l=e.length,c=null==n;if("object"===ee.type(n)){r=!0;for(a in n)ee.access(e,t,a,n[a],!0,s,o)}else if(void 0!==i&&(r=!0,ee.isFunction(i)||(o=!0),c&&(o?(t.call(e,i),t=null):(c=t,t=function(e,t,n){return c.call(ee(e),n)})),t))for(;a1,null,!0)},removeData:function(e){return this.each(function(){ve.remove(this,e)})}}),ee.extend({queue:function(e,t,n){var i;if(e)return t=(t||"fx")+"queue",i=me.get(e,t),n&&(!i||ee.isArray(n)?i=me.access(e,t,ee.makeArray(n)):i.push(n)),i||[]},dequeue:function(e,t){t=t||"fx";var n=ee.queue(e,t),i=n.length,r=n.shift(),s=ee._queueHooks(e,t),o=function(){ee.dequeue(e,t)};"inprogress"===r&&(r=n.shift(),i--),r&&("fx"===t&&n.unshift("inprogress"),delete s.stop,r.call(e,o,s)),!i&&s&&s.empty.fire()},_queueHooks:function(e,t){var n=t+"queueHooks";return me.get(e,n)||me.access(e,n,{empty:ee.Callbacks("once memory").add(function(){me.remove(e,[t+"queue",n])})})}}),ee.fn.extend({queue:function(e,t){var n=2;return"string"!=typeof e&&(t=e,e="fx",n--),arguments.length",J.checkClone=t.cloneNode(!0).cloneNode(!0).lastChild.checked,t.innerHTML="",J.noCloneChecked=!!t.cloneNode(!0).lastChild.defaultValue}();var Ce="undefined";J.focusinBubbles="onfocusin"in e;var Ne=/^key/,je=/^(?:mouse|contextmenu)|click/,Se=/^(?:focusinfocus|focusoutblur)$/,Ee=/^([^.]*)(?:\.(.+)|)$/;ee.event={global:{},add:function(e,t,n,i,r){var s,o,a,l,c,u,h,d,f,p,_,g=me.get(e);if(g)for(n.handler&&(s=n,n=s.handler,r=s.selector),n.guid||(n.guid=ee.guid++),(l=g.events)||(l=g.events={}),(o=g.handle)||(o=g.handle=function(t){return typeof ee!==Ce&&ee.event.triggered!==t.type?ee.event.dispatch.apply(e,arguments):void 0}),t=(t||"").match(fe)||[""],c=t.length;c--;)a=Ee.exec(t[c])||[],f=_=a[1],p=(a[2]||"").split(".").sort(),f&&(h=ee.event.special[f]||{},f=(r?h.delegateType:h.bindType)||f,h=ee.event.special[f]||{},u=ee.extend({type:f,origType:_,data:i,handler:n,guid:n.guid,selector:r,needsContext:r&&ee.expr.match.needsContext.test(r),namespace:p.join(".")},s),(d=l[f])||(d=l[f]=[],d.delegateCount=0,h.setup&&h.setup.call(e,i,p,o)!==!1||e.addEventListener&&e.addEventListener(f,o,!1)),h.add&&(h.add.call(e,u),u.handler.guid||(u.handler.guid=n.guid)),r?d.splice(d.delegateCount++,0,u):d.push(u),ee.event.global[f]=!0)},remove:function(e,t,n,i,r){var s,o,a,l,c,u,h,d,f,p,_,g=me.hasData(e)&&me.get(e);if(g&&(l=g.events)){for(t=(t||"").match(fe)||[""],c=t.length;c--;)if(a=Ee.exec(t[c])||[],f=_=a[1],p=(a[2]||"").split(".").sort(),f){for(h=ee.event.special[f]||{},f=(i?h.delegateType:h.bindType)||f,d=l[f]||[],a=a[2]&&new RegExp("(^|\\.)"+p.join("\\.(?:.*\\.|)")+"(\\.|$)"),o=s=d.length;s--;)u=d[s],!r&&_!==u.origType||n&&n.guid!==u.guid||a&&!a.test(u.namespace)||i&&i!==u.selector&&("**"!==i||!u.selector)||(d.splice(s,1),u.selector&&d.delegateCount--,h.remove&&h.remove.call(e,u));o&&!d.length&&(h.teardown&&h.teardown.call(e,p,g.handle)!==!1||ee.removeEvent(e,f,g.handle),delete l[f])}else for(f in l)ee.event.remove(e,f+t[c],n,i,!0);ee.isEmptyObject(l)&&(delete g.handle,me.remove(e,"events"))}},trigger:function(t,n,i,r){var s,o,a,l,c,u,h,d=[i||K],f=Y.call(t,"type")?t.type:t,p=Y.call(t,"namespace")?t.namespace.split("."):[];if(o=a=i=i||K,3!==i.nodeType&&8!==i.nodeType&&!Se.test(f+ee.event.triggered)&&(f.indexOf(".")>=0&&(p=f.split("."),f=p.shift(),p.sort()),c=f.indexOf(":")<0&&"on"+f,t=t[ee.expando]?t:new ee.Event(f,"object"==typeof t&&t),t.isTrigger=r?2:3,t.namespace=p.join("."),t.namespace_re=t.namespace?new RegExp("(^|\\.)"+p.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,t.result=void 0,t.target||(t.target=i),n=null==n?[t]:ee.makeArray(n,[t]),h=ee.event.special[f]||{},r||!h.trigger||h.trigger.apply(i,n)!==!1)){if(!r&&!h.noBubble&&!ee.isWindow(i)){for(l=h.delegateType||f,Se.test(l+f)||(o=o.parentNode);o;o=o.parentNode)d.push(o), + a=o;a===(i.ownerDocument||K)&&d.push(a.defaultView||a.parentWindow||e)}for(s=0;(o=d[s++])&&!t.isPropagationStopped();)t.type=s>1?l:h.bindType||f,u=(me.get(o,"events")||{})[t.type]&&me.get(o,"handle"),u&&u.apply(o,n),u=c&&o[c],u&&u.apply&&ee.acceptData(o)&&(t.result=u.apply(o,n),t.result===!1&&t.preventDefault());return t.type=f,r||t.isDefaultPrevented()||h._default&&h._default.apply(d.pop(),n)!==!1||!ee.acceptData(i)||c&&ee.isFunction(i[f])&&!ee.isWindow(i)&&(a=i[c],a&&(i[c]=null),ee.event.triggered=f,i[f](),ee.event.triggered=void 0,a&&(i[c]=a)),t.result}},dispatch:function(e){e=ee.event.fix(e);var t,n,i,r,s,o=[],a=B.call(arguments),l=(me.get(this,"events")||{})[e.type]||[],c=ee.event.special[e.type]||{};if(a[0]=e,e.delegateTarget=this,!c.preDispatch||c.preDispatch.call(this,e)!==!1){for(o=ee.event.handlers.call(this,e,l),t=0;(r=o[t++])&&!e.isPropagationStopped();)for(e.currentTarget=r.elem,n=0;(s=r.handlers[n++])&&!e.isImmediatePropagationStopped();)e.namespace_re&&!e.namespace_re.test(s.namespace)||(e.handleObj=s,e.data=s.data,i=((ee.event.special[s.origType]||{}).handle||s.handler).apply(r.elem,a),void 0!==i&&(e.result=i)===!1&&(e.preventDefault(),e.stopPropagation()));return c.postDispatch&&c.postDispatch.call(this,e),e.result}},handlers:function(e,t){var n,i,r,s,o=[],a=t.delegateCount,l=e.target;if(a&&l.nodeType&&(!e.button||"click"!==e.type))for(;l!==this;l=l.parentNode||this)if(l.disabled!==!0||"click"!==e.type){for(i=[],n=0;n=0:ee.find(r,this,null,[l]).length),i[r]&&i.push(s);i.length&&o.push({elem:l,handlers:i})}return a]*)\/>/gi,De=/<([\w:]+)/,Ae=/<|&#?\w+;/,Oe=/<(?:script|style|link)/i,Le=/checked\s*(?:[^=]|=\s*.checked.)/i,Ie=/^$|\/(?:java|ecma)script/i,Re=/^true\/(.*)/,He=/^\s*\s*$/g,Pe={option:[1,""],thead:[1,"","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};Pe.optgroup=Pe.option,Pe.tbody=Pe.tfoot=Pe.colgroup=Pe.caption=Pe.thead,Pe.th=Pe.td,ee.extend({clone:function(e,t,n){var i,r,s,o,a=e.cloneNode(!0),l=ee.contains(e.ownerDocument,e);if(!(J.noCloneChecked||1!==e.nodeType&&11!==e.nodeType||ee.isXMLDoc(e)))for(o=m(a),s=m(e),i=0,r=s.length;i0&&_(o,!l&&m(e,"script")),a},buildFragment:function(e,t,n,i){for(var r,s,o,a,l,c,u=t.createDocumentFragment(),h=[],d=0,f=e.length;d")+a[2],c=a[0];c--;)s=s.lastChild;ee.merge(h,s.childNodes),s=u.firstChild,s.textContent=""}else h.push(t.createTextNode(r));for(u.textContent="",d=0;r=h[d++];)if((!i||ee.inArray(r,i)===-1)&&(l=ee.contains(r.ownerDocument,r),s=m(u.appendChild(r),"script"),l&&_(s),n))for(c=0;r=s[c++];)Ie.test(r.type||"")&&n.push(r);return u},cleanData:function(e){for(var t,n,i,r,s,o,a=ee.event.special,l=0;void 0!==(n=e[l]);l++){if(ee.acceptData(n)&&(s=n[me.expando],s&&(t=me.cache[s]))){if(i=Object.keys(t.events||{}),i.length)for(o=0;void 0!==(r=i[o]);o++)a[r]?ee.event.remove(n,r):ee.removeEvent(n,r,t.handle);me.cache[s]&&delete me.cache[s]}delete ve.cache[n[ve.expando]]}}}),ee.fn.extend({text:function(e){return ge(this,function(e){return void 0===e?ee.text(this):this.empty().each(function(){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||(this.textContent=e)})},null,e,arguments.length)},append:function(){return this.domManip(arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=d(this,e);t.appendChild(e)}})},prepend:function(){return this.domManip(arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=d(this,e);t.insertBefore(e,t.firstChild)}})},before:function(){return this.domManip(arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this)})},after:function(){return this.domManip(arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this.nextSibling)})},remove:function(e,t){for(var n,i=e?ee.filter(e,this):this,r=0;null!=(n=i[r]);r++)t||1!==n.nodeType||ee.cleanData(m(n)),n.parentNode&&(t&&ee.contains(n.ownerDocument,n)&&_(m(n,"script")),n.parentNode.removeChild(n));return this},empty:function(){for(var e,t=0;null!=(e=this[t]);t++)1===e.nodeType&&(ee.cleanData(m(e,!1)),e.textContent="");return this},clone:function(e,t){return e=null!=e&&e,t=null==t?e:t,this.map(function(){return ee.clone(this,e,t)})},html:function(e){return ge(this,function(e){var t=this[0]||{},n=0,i=this.length;if(void 0===e&&1===t.nodeType)return t.innerHTML;if("string"==typeof e&&!Oe.test(e)&&!Pe[(De.exec(e)||["",""])[1].toLowerCase()]){e=e.replace(qe,"<$1>");try{for(;n1&&"string"==typeof d&&!J.checkClone&&Le.test(d))return this.each(function(n){var i=u.eq(n);_&&(e[0]=d.call(this,n,i.html())),i.domManip(e,t)});if(c&&(n=ee.buildFragment(e,this[0].ownerDocument,!1,this),i=n.firstChild,1===n.childNodes.length&&(n=i),i)){for(r=ee.map(m(n,"script"),f),s=r.length;l1)},show:function(){return j(this,!0)},hide:function(){return j(this)},toggle:function(e){return"boolean"==typeof e?e?this.show():this.hide():this.each(function(){Te(this)?ee(this).show():ee(this).hide()})}}),ee.Tween=S,S.prototype={constructor:S,init:function(e,t,n,i,r,s){this.elem=e,this.prop=n,this.easing=r||"swing",this.options=t,this.start=this.now=this.cur(),this.end=i,this.unit=s||(ee.cssNumber[n]?"":"px")},cur:function(){var e=S.propHooks[this.prop];return e&&e.get?e.get(this):S.propHooks._default.get(this)},run:function(e){var t,n=S.propHooks[this.prop];return this.options.duration?this.pos=t=ee.easing[this.easing](e,this.options.duration*e,0,1,this.options.duration):this.pos=t=e,this.now=(this.end-this.start)*t+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),n&&n.set?n.set(this):S.propHooks._default.set(this),this}},S.prototype.init.prototype=S.prototype,S.propHooks={_default:{get:function(e){var t;return null==e.elem[e.prop]||e.elem.style&&null!=e.elem.style[e.prop]?(t=ee.css(e.elem,e.prop,""),t&&"auto"!==t?t:0):e.elem[e.prop]},set:function(e){ee.fx.step[e.prop]?ee.fx.step[e.prop](e):e.elem.style&&(null!=e.elem.style[ee.cssProps[e.prop]]||ee.cssHooks[e.prop])?ee.style(e.elem,e.prop,e.now+e.unit):e.elem[e.prop]=e.now}}},S.propHooks.scrollTop=S.propHooks.scrollLeft={set:function(e){e.elem.nodeType&&e.elem.parentNode&&(e.elem[e.prop]=e.now)}},ee.easing={linear:function(e){return e},swing:function(e){return.5-Math.cos(e*Math.PI)/2}},ee.fx=S.prototype.init,ee.fx.step={};var Ge,Je,Ke=/^(?:toggle|show|hide)$/,Ze=new RegExp("^(?:([+-])=|)("+xe+")([a-z%]*)$","i"),et=/queueHooks$/,tt=[A],nt={"*":[function(e,t){var n=this.createTween(e,t),i=n.cur(),r=Ze.exec(t),s=r&&r[3]||(ee.cssNumber[e]?"":"px"),o=(ee.cssNumber[e]||"px"!==s&&+i)&&Ze.exec(ee.css(n.elem,e)),a=1,l=20;if(o&&o[3]!==s){s=s||o[3],r=r||[],o=+i||1;do a=a||".5",o/=a,ee.style(n.elem,e,o+s);while(a!==(a=n.cur()/i)&&1!==a&&--l)}return r&&(o=n.start=+o||+i||0,n.unit=s,n.end=r[1]?o+(r[1]+1)*r[2]:+r[2]),n}]};ee.Animation=ee.extend(L,{tweener:function(e,t){ee.isFunction(e)?(t=e,e=["*"]):e=e.split(" ");for(var n,i=0,r=e.length;i1)},removeAttr:function(e){return this.each(function(){ee.removeAttr(this,e)})}}),ee.extend({attr:function(e,t,n){var i,r,s=e.nodeType;if(e&&3!==s&&8!==s&&2!==s)return typeof e.getAttribute===Ce?ee.prop(e,t,n):(1===s&&ee.isXMLDoc(e)||(t=t.toLowerCase(),i=ee.attrHooks[t]||(ee.expr.match.bool.test(t)?rt:it)),void 0===n?i&&"get"in i&&null!==(r=i.get(e,t))?r:(r=ee.find.attr(e,t),null==r?void 0:r):null!==n?i&&"set"in i&&void 0!==(r=i.set(e,n,t))?r:(e.setAttribute(t,n+""),n):void ee.removeAttr(e,t))},removeAttr:function(e,t){var n,i,r=0,s=t&&t.match(fe);if(s&&1===e.nodeType)for(;n=s[r++];)i=ee.propFix[n]||n,ee.expr.match.bool.test(n)&&(e[i]=!1),e.removeAttribute(n)},attrHooks:{type:{set:function(e,t){if(!J.radioValue&&"radio"===t&&ee.nodeName(e,"input")){var n=e.value;return e.setAttribute("type",t),n&&(e.value=n),t}}}}}),rt={set:function(e,t,n){return t===!1?ee.removeAttr(e,n):e.setAttribute(n,n),n}},ee.each(ee.expr.match.bool.source.match(/\w+/g),function(e,t){var n=st[t]||ee.find.attr;st[t]=function(e,t,i){var r,s;return i||(s=st[t],st[t]=r,r=null!=n(e,t,i)?t.toLowerCase():null,st[t]=s),r}});var ot=/^(?:input|select|textarea|button)$/i;ee.fn.extend({prop:function(e,t){return ge(this,ee.prop,e,t,arguments.length>1)},removeProp:function(e){return this.each(function(){delete this[ee.propFix[e]||e]})}}),ee.extend({propFix:{for:"htmlFor",class:"className"},prop:function(e,t,n){var i,r,s,o=e.nodeType;if(e&&3!==o&&8!==o&&2!==o)return s=1!==o||!ee.isXMLDoc(e),s&&(t=ee.propFix[t]||t,r=ee.propHooks[t]),void 0!==n?r&&"set"in r&&void 0!==(i=r.set(e,n,t))?i:e[t]=n:r&&"get"in r&&null!==(i=r.get(e,t))?i:e[t]},propHooks:{tabIndex:{get:function(e){return e.hasAttribute("tabindex")||ot.test(e.nodeName)||e.href?e.tabIndex:-1}}}}),J.optSelected||(ee.propHooks.selected={get:function(e){var t=e.parentNode;return t&&t.parentNode&&t.parentNode.selectedIndex,null}}),ee.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){ee.propFix[this.toLowerCase()]=this});var at=/[\t\r\n\f]/g;ee.fn.extend({addClass:function(e){var t,n,i,r,s,o,a="string"==typeof e&&e,l=0,c=this.length;if(ee.isFunction(e))return this.each(function(t){ee(this).addClass(e.call(this,t,this.className))});if(a)for(t=(e||"").match(fe)||[];l=0;)i=i.replace(" "+r+" "," ");o=e?ee.trim(i):"",n.className!==o&&(n.className=o)}return this},toggleClass:function(e,t){var n=typeof e;return"boolean"==typeof t&&"string"===n?t?this.addClass(e):this.removeClass(e):ee.isFunction(e)?this.each(function(n){ee(this).toggleClass(e.call(this,n,this.className,t),t)}):this.each(function(){if("string"===n)for(var t,i=0,r=ee(this),s=e.match(fe)||[];t=s[i++];)r.hasClass(t)?r.removeClass(t):r.addClass(t);else n!==Ce&&"boolean"!==n||(this.className&&me.set(this,"__className__",this.className),this.className=this.className||e===!1?"":me.get(this,"__className__")||"")})},hasClass:function(e){for(var t=" "+e+" ",n=0,i=this.length;n=0)return!0;return!1}});var lt=/\r/g;ee.fn.extend({val:function(e){var t,n,i,r=this[0];{if(arguments.length)return i=ee.isFunction(e),this.each(function(n){var r;1===this.nodeType&&(r=i?e.call(this,n,ee(this).val()):e,null==r?r="":"number"==typeof r?r+="":ee.isArray(r)&&(r=ee.map(r,function(e){return null==e?"":e+""})),t=ee.valHooks[this.type]||ee.valHooks[this.nodeName.toLowerCase()],t&&"set"in t&&void 0!==t.set(this,r,"value")||(this.value=r))});if(r)return t=ee.valHooks[r.type]||ee.valHooks[r.nodeName.toLowerCase()],t&&"get"in t&&void 0!==(n=t.get(r,"value"))?n:(n=r.value,"string"==typeof n?n.replace(lt,""):null==n?"":n)}}}),ee.extend({valHooks:{select:{get:function(e){for(var t,n,i=e.options,r=e.selectedIndex,s="select-one"===e.type||r<0,o=s?null:[],a=s?r+1:i.length,l=r<0?a:s?r:0;l=0)&&(n=!0);return n||(e.selectedIndex=-1),s}}}}),ee.each(["radio","checkbox"],function(){ee.valHooks[this]={set:function(e,t){if(ee.isArray(t))return e.checked=ee.inArray(ee(e).val(),t)>=0}},J.checkOn||(ee.valHooks[this].get=function(e){return null===e.getAttribute("value")?"on":e.value})}),ee.each("blur focus focusin focusout load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup error contextmenu".split(" "),function(e,t){ee.fn[t]=function(e,n){return arguments.length>0?this.on(t,null,e,n):this.trigger(t)}}),ee.fn.extend({hover:function(e,t){return this.mouseenter(e).mouseleave(t||e)},bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,i){return this.on(t,e,n,i)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)}});var ct=ee.now(),ut=/\?/;ee.parseJSON=function(e){return JSON.parse(e+"")},ee.parseXML=function(e){var t,n;if(!e||"string"!=typeof e)return null;try{n=new DOMParser,t=n.parseFromString(e,"text/xml")}catch(e){t=void 0}return t&&!t.getElementsByTagName("parsererror").length||ee.error("Invalid XML: "+e),t};var ht,dt,ft=/#.*$/,pt=/([?&])_=[^&]*/,_t=/^(.*?):[ \t]*([^\r\n]*)$/gm,gt=/^(?:about|app|app-storage|.+-extension|file|res|widget):$/,mt=/^(?:GET|HEAD)$/,vt=/^\/\//,yt=/^([\w.+-]+:)(?:\/\/(?:[^\/?#]*@|)([^\/?#:]*)(?::(\d+)|)|)/,bt={},xt={},wt="*/".concat("*");try{dt=location.href}catch(e){dt=K.createElement("a"),dt.href="",dt=dt.href}ht=yt.exec(dt.toLowerCase())||[],ee.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:dt,type:"GET",isLocal:gt.test(ht[1]),global:!0,processData:!0,async:!0,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":wt,text:"text/plain",html:"text/html",xml:"application/xml, text/xml",json:"application/json, text/javascript"},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText",json:"responseJSON"},converters:{"* text":String,"text html":!0,"text json":ee.parseJSON,"text xml":ee.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(e,t){return t?H(H(e,ee.ajaxSettings),t):H(ee.ajaxSettings,e)},ajaxPrefilter:I(bt),ajaxTransport:I(xt),ajax:function(e,t){function n(e,t,n,o){var l,u,m,v,b,w=t;2!==y&&(y=2,a&&clearTimeout(a),i=void 0,s=o||"",x.readyState=e>0?4:0,l=e>=200&&e<300||304===e,n&&(v=P(h,x,n)),v=$(h,v,x,l),l?(h.ifModified&&(b=x.getResponseHeader("Last-Modified"),b&&(ee.lastModified[r]=b),b=x.getResponseHeader("etag"),b&&(ee.etag[r]=b)),204===e||"HEAD"===h.type?w="nocontent":304===e?w="notmodified":(w=v.state,u=v.data,m=v.error,l=!m)):(m=w,!e&&w||(w="error",e<0&&(e=0))),x.status=e,x.statusText=(t||w)+"",l?p.resolveWith(d,[u,w,x]):p.rejectWith(d,[x,w,m]),x.statusCode(g),g=void 0,c&&f.trigger(l?"ajaxSuccess":"ajaxError",[x,h,l?u:m]),_.fireWith(d,[x,w]),c&&(f.trigger("ajaxComplete",[x,h]),--ee.active||ee.event.trigger("ajaxStop")))}"object"==typeof e&&(t=e,e=void 0),t=t||{};var i,r,s,o,a,l,c,u,h=ee.ajaxSetup({},t),d=h.context||h,f=h.context&&(d.nodeType||d.jquery)?ee(d):ee.event,p=ee.Deferred(),_=ee.Callbacks("once memory"),g=h.statusCode||{},m={},v={},y=0,b="canceled",x={readyState:0,getResponseHeader:function(e){var t;if(2===y){if(!o)for(o={};t=_t.exec(s);)o[t[1].toLowerCase()]=t[2];t=o[e.toLowerCase()]}return null==t?null:t},getAllResponseHeaders:function(){return 2===y?s:null},setRequestHeader:function(e,t){var n=e.toLowerCase();return y||(e=v[n]=v[n]||e,m[e]=t),this},overrideMimeType:function(e){return y||(h.mimeType=e),this},statusCode:function(e){var t;if(e)if(y<2)for(t in e)g[t]=[g[t],e[t]];else x.always(e[x.status]);return this},abort:function(e){var t=e||b;return i&&i.abort(t),n(0,t),this}};if(p.promise(x).complete=_.add,x.success=x.done,x.error=x.fail,h.url=((e||h.url||dt)+"").replace(ft,"").replace(vt,ht[1]+"//"),h.type=t.method||t.type||h.method||h.type,h.dataTypes=ee.trim(h.dataType||"*").toLowerCase().match(fe)||[""],null==h.crossDomain&&(l=yt.exec(h.url.toLowerCase()),h.crossDomain=!(!l||l[1]===ht[1]&&l[2]===ht[2]&&(l[3]||("http:"===l[1]?"80":"443"))===(ht[3]||("http:"===ht[1]?"80":"443")))),h.data&&h.processData&&"string"!=typeof h.data&&(h.data=ee.param(h.data,h.traditional)),R(bt,h,t,x),2===y)return x;c=h.global,c&&0===ee.active++&&ee.event.trigger("ajaxStart"),h.type=h.type.toUpperCase(),h.hasContent=!mt.test(h.type),r=h.url,h.hasContent||(h.data&&(r=h.url+=(ut.test(r)?"&":"?")+h.data,delete h.data),h.cache===!1&&(h.url=pt.test(r)?r.replace(pt,"$1_="+ct++):r+(ut.test(r)?"&":"?")+"_="+ct++)),h.ifModified&&(ee.lastModified[r]&&x.setRequestHeader("If-Modified-Since",ee.lastModified[r]),ee.etag[r]&&x.setRequestHeader("If-None-Match",ee.etag[r])),(h.data&&h.hasContent&&h.contentType!==!1||t.contentType)&&x.setRequestHeader("Content-Type",h.contentType),x.setRequestHeader("Accept",h.dataTypes[0]&&h.accepts[h.dataTypes[0]]?h.accepts[h.dataTypes[0]]+("*"!==h.dataTypes[0]?", "+wt+"; q=0.01":""):h.accepts["*"]);for(u in h.headers)x.setRequestHeader(u,h.headers[u]);if(h.beforeSend&&(h.beforeSend.call(d,x,h)===!1||2===y))return x.abort();b="abort";for(u in{success:1,error:1,complete:1})x[u](h[u]);if(i=R(xt,h,t,x)){x.readyState=1,c&&f.trigger("ajaxSend",[x,h]),h.async&&h.timeout>0&&(a=setTimeout(function(){x.abort("timeout")},h.timeout));try{y=1,i.send(m,n)}catch(e){if(!(y<2))throw e;n(-1,e)}}else n(-1,"No Transport");return x},getJSON:function(e,t,n){return ee.get(e,t,n,"json")},getScript:function(e,t){return ee.get(e,void 0,t,"script")}}),ee.each(["get","post"],function(e,t){ee[t]=function(e,n,i,r){return ee.isFunction(n)&&(r=r||i,i=n,n=void 0),ee.ajax({url:e,type:t,dataType:r,data:n,success:i})}}),ee.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){ee.fn[t]=function(e){return this.on(t,e)}}),ee._evalUrl=function(e){return ee.ajax({url:e,type:"GET",dataType:"script",async:!1,global:!1,throws:!0})},ee.fn.extend({wrapAll:function(e){var t;return ee.isFunction(e)?this.each(function(t){ee(this).wrapAll(e.call(this,t))}):(this[0]&&(t=ee(e,this[0].ownerDocument).eq(0).clone(!0),this[0].parentNode&&t.insertBefore(this[0]),t.map(function(){for(var e=this;e.firstElementChild;)e=e.firstElementChild;return e}).append(this)),this)},wrapInner:function(e){return ee.isFunction(e)?this.each(function(t){ee(this).wrapInner(e.call(this,t))}):this.each(function(){var t=ee(this),n=t.contents();n.length?n.wrapAll(e):t.append(e)})},wrap:function(e){var t=ee.isFunction(e);return this.each(function(n){ee(this).wrapAll(t?e.call(this,n):e)})},unwrap:function(){return this.parent().each(function(){ee.nodeName(this,"body")||ee(this).replaceWith(this.childNodes)}).end()}}),ee.expr.filters.hidden=function(e){return e.offsetWidth<=0&&e.offsetHeight<=0},ee.expr.filters.visible=function(e){return!ee.expr.filters.hidden(e)};var Tt=/%20/g,kt=/\[\]$/,Ct=/\r?\n/g,Nt=/^(?:submit|button|image|reset|file)$/i,jt=/^(?:input|select|textarea|keygen)/i;ee.param=function(e,t){var n,i=[],r=function(e,t){t=ee.isFunction(t)?t():null==t?"":t,i[i.length]=encodeURIComponent(e)+"="+encodeURIComponent(t); +};if(void 0===t&&(t=ee.ajaxSettings&&ee.ajaxSettings.traditional),ee.isArray(e)||e.jquery&&!ee.isPlainObject(e))ee.each(e,function(){r(this.name,this.value)});else for(n in e)M(n,e[n],t,r);return i.join("&").replace(Tt,"+")},ee.fn.extend({serialize:function(){return ee.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var e=ee.prop(this,"elements");return e?ee.makeArray(e):this}).filter(function(){var e=this.type;return this.name&&!ee(this).is(":disabled")&&jt.test(this.nodeName)&&!Nt.test(e)&&(this.checked||!ke.test(e))}).map(function(e,t){var n=ee(this).val();return null==n?null:ee.isArray(n)?ee.map(n,function(e){return{name:t.name,value:e.replace(Ct,"\r\n")}}):{name:t.name,value:n.replace(Ct,"\r\n")}}).get()}}),ee.ajaxSettings.xhr=function(){try{return new XMLHttpRequest}catch(e){}};var St=0,Et={},qt={0:200,1223:204},Dt=ee.ajaxSettings.xhr();e.ActiveXObject&&ee(e).on("unload",function(){for(var e in Et)Et[e]()}),J.cors=!!Dt&&"withCredentials"in Dt,J.ajax=Dt=!!Dt,ee.ajaxTransport(function(e){var t;if(J.cors||Dt&&!e.crossDomain)return{send:function(n,i){var r,s=e.xhr(),o=++St;if(s.open(e.type,e.url,e.async,e.username,e.password),e.xhrFields)for(r in e.xhrFields)s[r]=e.xhrFields[r];e.mimeType&&s.overrideMimeType&&s.overrideMimeType(e.mimeType),e.crossDomain||n["X-Requested-With"]||(n["X-Requested-With"]="XMLHttpRequest");for(r in n)s.setRequestHeader(r,n[r]);t=function(e){return function(){t&&(delete Et[o],t=s.onload=s.onerror=null,"abort"===e?s.abort():"error"===e?i(s.status,s.statusText):i(qt[s.status]||s.status,s.statusText,"string"==typeof s.responseText?{text:s.responseText}:void 0,s.getAllResponseHeaders()))}},s.onload=t(),s.onerror=t("error"),t=Et[o]=t("abort"),s.send(e.hasContent&&e.data||null)},abort:function(){t&&t()}}}),ee.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/(?:java|ecma)script/},converters:{"text script":function(e){return ee.globalEval(e),e}}}),ee.ajaxPrefilter("script",function(e){void 0===e.cache&&(e.cache=!1),e.crossDomain&&(e.type="GET")}),ee.ajaxTransport("script",function(e){if(e.crossDomain){var t,n;return{send:function(i,r){t=ee(" + + diff --git a/api/src/main/resources/tpf/datasource.ftl.html b/api/src/main/resources/tpf/datasource.ftl.html new file mode 100644 index 000000000..97cd09985 --- /dev/null +++ b/api/src/main/resources/tpf/datasource.ftl.html @@ -0,0 +1,7 @@ +<#-- @license ©2015 Miel Vander Sande - Multimedia Lab / iMinds / Ghent University --> +<#assign title = datasource.getTitle() + ' | ' + title!""> +<#include "base.ftl.html"> +<#macro contents> +<#include "fragment.ftl.html"> + +<@display_page/> diff --git a/api/src/main/resources/tpf/error.ftl.html b/api/src/main/resources/tpf/error.ftl.html new file mode 100644 index 000000000..84ce65876 --- /dev/null +++ b/api/src/main/resources/tpf/error.ftl.html @@ -0,0 +1,11 @@ +<#-- @license ©2015 Miel Vander Sande - Multimedia Lab / iMinds / Ghent University --> +<#include "base.ftl.html"> +<#macro contents> +

Error executing your request

+

Your request could not be executed due to an internal server error.

+

Please try reloading the page or return to the index page.

+ +

Error details

+

<#if error??>${(error.getMessage())!error!""}

+ +<@display_page/> \ No newline at end of file diff --git a/api/src/main/resources/tpf/examples.ftl.html b/api/src/main/resources/tpf/examples.ftl.html new file mode 100644 index 000000000..e7feccb52 --- /dev/null +++ b/api/src/main/resources/tpf/examples.ftl.html @@ -0,0 +1,122 @@ +

  • + + +
  • diff --git a/api/src/main/resources/tpf/fragment.ftl.html b/api/src/main/resources/tpf/fragment.ftl.html new file mode 100644 index 000000000..32ce716bf --- /dev/null +++ b/api/src/main/resources/tpf/fragment.ftl.html @@ -0,0 +1,83 @@ +<#-- @license ©2015 Miel Vander Sande - Multimedia Lab / iMinds / Ghent University --> +<#setting url_escaping_charset='UTF-8'> +
    +

    ${datasource.getTitle()?cap_first}

    + +
    +
    + Query ${datasource.getTitle()} by triple pattern +
      +<#list ['subject', 'predicate', 'object'] as component> +
    • + + +
    • + +
    +
    +

    + +

    +
    +
    + +

    Matches in ${datasource.getTitle()} for ${ (query["pattern"]?html)!"" }

    + +
    +<#if (triples?size > 0)> + Showing triples ${ start } to ${ end } of + <#if totalEstimate != end>± + ${ totalEstimate } + with ${ + itemsPerPage + } triples per page. + <@pageLinks/> +<#else> +

    + ${datasource.getTitle()} contains + + no <#if (totalEstimate > 0) >more + + triples that match this pattern. +

    + +
    + + + +<@pageLinks/> + +<#macro pageLinks> + + \ No newline at end of file diff --git a/api/src/main/resources/tpf/index.ftl.html b/api/src/main/resources/tpf/index.ftl.html new file mode 100644 index 000000000..3e8d1cac4 --- /dev/null +++ b/api/src/main/resources/tpf/index.ftl.html @@ -0,0 +1,20 @@ +<#-- @license ©2015 Miel Vander Sande - Multimedia Lab / iMinds / Ghent University --> +<#include "base.ftl.html"> +<#macro contents> +
    +

    Available datasets

    +

    Browse the following datasets as Triple Pattern Fragments:

    +
    + <#if datasources??> + <#list datasources?keys as datasourceName> +
    ${datasources[datasourceName].getTitle() }
    +
    ${ datasources[datasourceName].getDescription()!"" }
    + + +
    +
    + +<#include "client.ftl.html"> + + +<@display_page/> \ No newline at end of file diff --git a/api/src/main/resources/tpf/notfound.ftl.html b/api/src/main/resources/tpf/notfound.ftl.html new file mode 100644 index 000000000..363b8abd9 --- /dev/null +++ b/api/src/main/resources/tpf/notfound.ftl.html @@ -0,0 +1,16 @@ +<#-- @license ©2015 Miel Vander Sande - Multimedia Lab / iMinds / Ghent University --> +<#include "base.ftl.html"> +<#macro contents> +

    Resource not found

    +

    + No resource with URL ${ url!"" } was found. +

    + +

    Available datasets

    + + +<@display_page/> \ No newline at end of file diff --git a/api/src/test/java/edu/cornell/mannlib/vitro/testing/ModelUtilitiesTestHelper.java b/api/src/test/java/edu/cornell/mannlib/vitro/testing/ModelUtilitiesTestHelper.java index 92db82b56..16149d35e 100644 --- a/api/src/test/java/edu/cornell/mannlib/vitro/testing/ModelUtilitiesTestHelper.java +++ b/api/src/test/java/edu/cornell/mannlib/vitro/testing/ModelUtilitiesTestHelper.java @@ -31,6 +31,11 @@ public class ModelUtilitiesTestHelper { createResource(classUri)); } + public static Statement objectProperty(String subjectUri, String propertyUri) { + return createStatement(createResource(subjectUri), + createProperty(propertyUri), createResource()); + } + public static Statement objectProperty(String subjectUri, String propertyUri, String objectUri) { return createStatement(createResource(subjectUri), @@ -43,6 +48,12 @@ public class ModelUtilitiesTestHelper { createProperty(propertyUri), createPlainLiteral(objectValue)); } + public static Statement dataProperty(String subjectUri, String propertyUri, + Float objectValue) { + return createStatement(createResource(subjectUri), + createProperty(propertyUri), createTypedLiteral(objectValue)); + } + public static Statement dataProperty(String subjectUri, String propertyUri, Object objectValue, XSDDatatype dataType) { return createStatement(createResource(subjectUri), diff --git a/api/src/test/java/edu/cornell/mannlib/vitro/webapp/controller/accounts/manageproxies/ProxyRelationshipSelectorTest.java b/api/src/test/java/edu/cornell/mannlib/vitro/webapp/controller/accounts/manageproxies/ProxyRelationshipSelectorTest.java index 703d48a9f..83b7c2721 100644 --- a/api/src/test/java/edu/cornell/mannlib/vitro/webapp/controller/accounts/manageproxies/ProxyRelationshipSelectorTest.java +++ b/api/src/test/java/edu/cornell/mannlib/vitro/webapp/controller/accounts/manageproxies/ProxyRelationshipSelectorTest.java @@ -16,7 +16,6 @@ import java.util.List; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import org.apache.log4j.Level; import org.junit.BeforeClass; import org.junit.Ignore; import org.junit.Test; @@ -29,7 +28,6 @@ import org.apache.jena.rdf.model.ModelFactory; import edu.cornell.mannlib.vitro.testing.AbstractTestClass; import edu.cornell.mannlib.vitro.webapp.controller.accounts.manageproxies.ProxyRelationshipSelectionCriteria.ProxyRelationshipView; import edu.cornell.mannlib.vitro.webapp.controller.accounts.manageproxies.ProxyRelationshipSelector.Context; -import edu.cornell.mannlib.vitro.webapp.utils.SparqlQueryRunner; /** * TODO diff --git a/api/src/test/java/edu/cornell/mannlib/vitro/webapp/controller/api/sparqlquery/SparqlQueryApiExecutorTest.java b/api/src/test/java/edu/cornell/mannlib/vitro/webapp/controller/api/sparqlquery/SparqlQueryApiExecutorTest.java index 7938cf0bd..c9a402b53 100644 --- a/api/src/test/java/edu/cornell/mannlib/vitro/webapp/controller/api/sparqlquery/SparqlQueryApiExecutorTest.java +++ b/api/src/test/java/edu/cornell/mannlib/vitro/webapp/controller/api/sparqlquery/SparqlQueryApiExecutorTest.java @@ -14,7 +14,7 @@ import java.util.TreeSet; import java.util.regex.Matcher; import java.util.regex.Pattern; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.junit.Before; import org.junit.Test; diff --git a/api/src/test/java/edu/cornell/mannlib/vitro/webapp/filters/URLRewritingHttpServletResponseTest.java b/api/src/test/java/edu/cornell/mannlib/vitro/webapp/filters/URLRewritingHttpServletResponseTest.java index 416b26c0a..ae5f6dedf 100644 --- a/api/src/test/java/edu/cornell/mannlib/vitro/webapp/filters/URLRewritingHttpServletResponseTest.java +++ b/api/src/test/java/edu/cornell/mannlib/vitro/webapp/filters/URLRewritingHttpServletResponseTest.java @@ -58,8 +58,8 @@ public class URLRewritingHttpServletResponseTest { @Test - public void test40984(){ urlEncodingStyleA( "/vivo/js/jquery.js", - "/vivo/js/jquery.js"); } + public void test40984(){ urlEncodingStyleA( "/vivo/js/jquery-1.12.4.min.js", + "/vivo/js/jquery-1.12.4.min.js"); } @Test public void test40988(){ urlEncodingStyleA( @@ -289,8 +289,8 @@ public class URLRewritingHttpServletResponseTest { public void test39472(){ urlEncodingStyleA( "/vivo/js/extensions/String.js", "/vivo/js/extensions/String.js"); } @Test - public void test394730(){ urlEncodingStyleA( "/vivo/js/jquery.js", - "/vivo/js/jquery.js"); } + public void test394730(){ urlEncodingStyleA( "/vivo/js/jquery-1.12.4.min.js", + "/vivo/js/jquery-1.12.4.min.js"); } @Test public void test39473(){ urlEncodingStyleA( "/vivo/js/jquery_plugins/jquery.bgiframe.pack.js", @@ -304,8 +304,8 @@ public class URLRewritingHttpServletResponseTest { "/vivo/js/jquery_plugins/ui.datepicker.js", "/vivo/js/jquery_plugins/ui.datepicker.js"); } @Test - public void test14958(){ urlEncodingStyleA( "/vivo/js/jquery.js", - "/vivo/js/jquery.js"); } + public void test14958(){ urlEncodingStyleA( "/vivo/js/jquery-1.12.4.min.js", + "/vivo/js/jquery-1.12.4.min.js"); } @Test public void test14968(){ urlEncodingStyleA( "/vivo/js/jquery_plugins/getURLParam.js", @@ -347,8 +347,8 @@ public class URLRewritingHttpServletResponseTest { "/vivo/js/imageUpload/imageUploadUtils.js"); } @Test public void test184670(){ urlEncodingStyleA( - "/vivo/js/jquery-ui/css/smoothness/jquery-ui-1.8.9.custom.css", - "/vivo/js/jquery-ui/css/smoothness/jquery-ui-1.8.9.custom.css"); } + "/vivo/js/jquery-ui/css/smoothness/jquery-ui-1.12.1.css", + "/vivo/js/jquery-ui/css/smoothness/jquery-ui-1.12.1.css"); } @Test public void test18467(){ urlEncodingStyleA( "/vivo/edit/forms/css/customForm.css", @@ -375,8 +375,8 @@ public class URLRewritingHttpServletResponseTest { public void test18516(){ urlEncodingStyleA( "/vivo/js/extensions/String.js", "/vivo/js/extensions/String.js"); } @Test - public void test18543(){ urlEncodingStyleA( "/vivo/js/jquery.js", - "/vivo/js/jquery.js"); } + public void test18543(){ urlEncodingStyleA( "/vivo/js/jquery-1.12.4.min.js", + "/vivo/js/jquery-1.12.4.min.js"); } @Test public void test185440(){ urlEncodingStyleA( "/vivo/js/jquery_plugins/jquery.bgiframe.pack.js", @@ -391,8 +391,8 @@ public class URLRewritingHttpServletResponseTest { "/vivo/js/jquery_plugins/ui.datepicker.js"); } @Test public void test18546(){ urlEncodingStyleA( - "/vivo/js/jquery-ui/js/jquery-ui-1.8.9.custom.min.js", - "/vivo/js/jquery-ui/js/jquery-ui-1.8.9.custom.min.js"); } + "/vivo/js/jquery-ui/js/jquery-ui-1.12.1.min.js", + "/vivo/js/jquery-ui/js/jquery-ui-1.12.1.min.js"); } @Test public void test185470(){ urlEncodingStyleA( "/vivo/js/customFormUtils.js", "/vivo/js/customFormUtils.js"); } @@ -412,8 +412,8 @@ public class URLRewritingHttpServletResponseTest { public void test271590(){ urlEncodingStyleA( "/vivo/js/extensions/String.js", "/vivo/js/extensions/String.js"); } @Test - public void test27159(){ urlEncodingStyleA( "/vivo/js/jquery.js", - "/vivo/js/jquery.js"); } + public void test27159(){ urlEncodingStyleA( "/vivo/js/jquery-1.12.4.min.js", + "/vivo/js/jquery-1.12.4.min.js"); } @Test public void test27160(){ urlEncodingStyleA( "/vivo/js/jquery_plugins/jquery.bgiframe.pack.js", @@ -438,8 +438,8 @@ public class URLRewritingHttpServletResponseTest { public void test148510(){ urlEncodingStyleA( "/vivo/js/extensions/String.js", "/vivo/js/extensions/String.js"); } @Test - public void test14851(){ urlEncodingStyleA( "/vivo/js/jquery.js", - "/vivo/js/jquery.js"); } + public void test14851(){ urlEncodingStyleA( "/vivo/js/jquery-1.12.4.min.js", + "/vivo/js/jquery-1.12.4.min.js"); } @Test public void test14852(){ urlEncodingStyleA( "/vivo/js/jquery_plugins/jquery.bgiframe.pack.js", @@ -454,8 +454,8 @@ public class URLRewritingHttpServletResponseTest { "/vivo/js/jquery_plugins/ui.datepicker.js"); } @Test public void test43748(){ urlEncodingStyleA( - "/vivo/js/jquery-ui/css/smoothness/jquery-ui-1.8.9.custom.css", - "/vivo/js/jquery-ui/css/smoothness/jquery-ui-1.8.9.custom.css"); } + "/vivo/js/jquery-ui/css/smoothness/jquery-ui-1.12.1.css", + "/vivo/js/jquery-ui/css/smoothness/jquery-ui-1.12.1.css"); } @Test public void test43749(){ urlEncodingStyleA( "/vivo/edit/forms/css/customForm.css", @@ -482,8 +482,8 @@ public class URLRewritingHttpServletResponseTest { public void test43760(){ urlEncodingStyleA( "/vivo/js/extensions/String.js", "/vivo/js/extensions/String.js"); } @Test - public void test437610(){ urlEncodingStyleA( "/vivo/js/jquery.js", - "/vivo/js/jquery.js"); } + public void test437610(){ urlEncodingStyleA( "/vivo/js/jquery-1.12.4.min.js", + "/vivo/js/jquery-1.12.4.min.js"); } @Test public void test43761(){ urlEncodingStyleA( "/vivo/js/jquery_plugins/jquery.bgiframe.pack.js", @@ -498,8 +498,8 @@ public class URLRewritingHttpServletResponseTest { "/vivo/js/jquery_plugins/ui.datepicker.js"); } @Test public void test43763(){ urlEncodingStyleA( - "/vivo/js/jquery-ui/js/jquery-ui-1.8.9.custom.min.js", - "/vivo/js/jquery-ui/js/jquery-ui-1.8.9.custom.min.js"); } + "/vivo/js/jquery-ui/js/jquery-ui-1.12.1.min.js", + "/vivo/js/jquery-ui/js/jquery-ui-1.12.1.min.js"); } @Test public void test437640(){ urlEncodingStyleA( "/vivo/js/customFormUtils.js", "/vivo/js/customFormUtils.js"); } @@ -509,8 +509,8 @@ public class URLRewritingHttpServletResponseTest { "/vivo/edit/forms/js/customFormWithAutocomplete.js"); } @Test public void test14550(){ urlEncodingStyleA( - "/vivo/js/jquery-ui/css/smoothness/jquery-ui-1.8.9.custom.css", - "/vivo/js/jquery-ui/css/smoothness/jquery-ui-1.8.9.custom.css"); } + "/vivo/js/jquery-ui/css/smoothness/jquery-ui-1.12.1.css", + "/vivo/js/jquery-ui/css/smoothness/jquery-ui-1.12.1.css"); } @Test public void test14551(){ urlEncodingStyleA( "/vivo/edit/forms/css/customForm.css", @@ -540,8 +540,8 @@ public class URLRewritingHttpServletResponseTest { public void test14565(){ urlEncodingStyleA( "/vivo/js/extensions/String.js", "/vivo/js/extensions/String.js"); } @Test - public void test145650(){ urlEncodingStyleA( "/vivo/js/jquery.js", - "/vivo/js/jquery.js"); } + public void test145650(){ urlEncodingStyleA( "/vivo/js/jquery-1.12.4.min.js", + "/vivo/js/jquery-1.12.4.min.js"); } @Test public void test145660(){ urlEncodingStyleA( "/vivo/js/jquery_plugins/jquery.bgiframe.pack.js", @@ -556,8 +556,8 @@ public class URLRewritingHttpServletResponseTest { "/vivo/js/jquery_plugins/ui.datepicker.js"); } @Test public void test145680(){ urlEncodingStyleA( - "/vivo/js/jquery-ui/js/jquery-ui-1.8.9.custom.min.js", - "/vivo/js/jquery-ui/js/jquery-ui-1.8.9.custom.min.js"); } + "/vivo/js/jquery-ui/js/jquery-ui-1.12.1.min.js", + "/vivo/js/jquery-ui/js/jquery-ui-1.12.1.min.js"); } @Test public void test14568(){ urlEncodingStyleA( "/vivo/js/customFormUtils.js", "/vivo/js/customFormUtils.js"); } @@ -580,8 +580,8 @@ public class URLRewritingHttpServletResponseTest { public void test29084(){ urlEncodingStyleA( "/vivo/js/extensions/String.js", "/vivo/js/extensions/String.js"); } @Test - public void test29085(){ urlEncodingStyleA( "/vivo/js/jquery.js", - "/vivo/js/jquery.js"); } + public void test29085(){ urlEncodingStyleA( "/vivo/js/jquery-1.12.4.min.js", + "/vivo/js/jquery-1.12.4.min.js"); } @Test public void test290860(){ urlEncodingStyleA( "/vivo/js/jquery_plugins/jquery.bgiframe.pack.js", @@ -599,8 +599,8 @@ public class URLRewritingHttpServletResponseTest { @Test - public void test35560(){ urlEncodingStyleB( "/js/jquery.js", - "/js/jquery.js"); } + public void test35560(){ urlEncodingStyleB( "/js/jquery-1.12.4.min.js", + "/js/jquery-1.12.4.min.js"); } @Test public void test35562(){ urlEncodingStyleB( "/js/jquery_plugins/getURLParam.js", diff --git a/api/src/test/java/edu/cornell/mannlib/vitro/webapp/freemarker/loader/FreemarkerTemplateLoaderTest.java b/api/src/test/java/edu/cornell/mannlib/vitro/webapp/freemarker/loader/FreemarkerTemplateLoaderTest.java index 0ed381c79..f4fa33767 100644 --- a/api/src/test/java/edu/cornell/mannlib/vitro/webapp/freemarker/loader/FreemarkerTemplateLoaderTest.java +++ b/api/src/test/java/edu/cornell/mannlib/vitro/webapp/freemarker/loader/FreemarkerTemplateLoaderTest.java @@ -11,7 +11,7 @@ import java.nio.file.Paths; import java.util.Arrays; import java.util.SortedSet; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.junit.Test; import edu.cornell.mannlib.vitro.webapp.freemarker.loader.FreemarkerTemplateLoader.PathPieces; diff --git a/api/src/test/java/edu/cornell/mannlib/vitro/webapp/i18n/selection/LocaleSelectionSetupTest.java b/api/src/test/java/edu/cornell/mannlib/vitro/webapp/i18n/selection/LocaleSelectionSetupTest.java index 3709d8a43..6d47d889f 100644 --- a/api/src/test/java/edu/cornell/mannlib/vitro/webapp/i18n/selection/LocaleSelectionSetupTest.java +++ b/api/src/test/java/edu/cornell/mannlib/vitro/webapp/i18n/selection/LocaleSelectionSetupTest.java @@ -14,8 +14,8 @@ import java.util.Locale; import javax.servlet.ServletContextEvent; -import org.apache.commons.lang.LocaleUtils; -import org.apache.commons.lang.ObjectUtils; +import org.apache.commons.lang3.LocaleUtils; +import org.apache.commons.lang3.ObjectUtils; import org.apache.log4j.Level; import org.junit.After; import org.junit.Before; @@ -66,7 +66,7 @@ public class LocaleSelectionSetupTest extends AbstractTestClass { @After public void checkExpectations() { if (expectedMessageCounts == null) { - fail("expecteMessages() was not called"); + fail("expectedMessages() was not called"); } String message = compareMessageCount("info", ss.getInfoCount(), @@ -194,7 +194,8 @@ public class LocaleSelectionSetupTest extends AbstractTestClass { public void langaugeIsEmpty() { props.setProperty(PROPERTY_FORCE_LOCALE, "_ES"); lss.contextInitialized(sce); - expectMessages(0, 1, 0); + expectForced("_ES"); + expectMessages(1, 1, 0); } @Test @@ -253,13 +254,14 @@ public class LocaleSelectionSetupTest extends AbstractTestClass { expectMessages(0, 1, 0); } - @Test - public void funkyVariantIsAcceptable() { - props.setProperty(PROPERTY_FORCE_LOCALE, "es_ES_123_aa"); - lss.contextInitialized(sce); - expectForced("es_ES_123_aa"); - expectMessages(1, 1, 0); - } + // This shouldn't really be acceptable, so we won't test that it is +// @Test +// public void funkyVariantIsAcceptable() { +// props.setProperty(PROPERTY_FORCE_LOCALE, "es_ES_123_aa"); +// lss.contextInitialized(sce); +// expectForced("es_ES_123_aa"); +// expectMessages(1, 1, 0); +// } @Test public void localeNotRecognizedProducesWarning() { diff --git a/api/src/test/java/edu/cornell/mannlib/vitro/webapp/imageprocessor/jai/JaiImageProcessorTester.java b/api/src/test/java/edu/cornell/mannlib/vitro/webapp/imageprocessor/jai/JaiImageProcessorTester.java deleted file mode 100644 index 016ebfb1f..000000000 --- a/api/src/test/java/edu/cornell/mannlib/vitro/webapp/imageprocessor/jai/JaiImageProcessorTester.java +++ /dev/null @@ -1,134 +0,0 @@ -/* $This file is distributed under the terms of the license in /doc/license.txt$ */ - -package edu.cornell.mannlib.vitro.webapp.imageprocessor.jai; - -import static edu.cornell.mannlib.vitro.webapp.controller.freemarker.ImageUploadController.THUMBNAIL_HEIGHT; -import static edu.cornell.mannlib.vitro.webapp.controller.freemarker.ImageUploadController.THUMBNAIL_WIDTH; - -import java.awt.Frame; -import java.awt.GridLayout; -import java.awt.event.WindowAdapter; -import java.awt.event.WindowEvent; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; - -import javax.media.jai.RenderedOp; -import javax.media.jai.operator.StreamDescriptor; - -import org.apache.log4j.Level; -import org.apache.log4j.Logger; - -import com.sun.media.jai.codec.MemoryCacheSeekableStream; - -import edu.cornell.mannlib.vitro.webapp.imageprocessor.jai.JaiImageProcessor; -import edu.cornell.mannlib.vitro.webapp.modules.imageProcessor.ImageProcessor.CropRectangle; -import edu.cornell.mannlib.vitro.webapp.modules.imageProcessor.ImageProcessor.Dimensions; - -/** - * This is not a unit test, so it is not named BlahBlahTest. - * - * Instead, it's a test harness that creates thumbnails and writes them to - * files, while also displaying them in a window on the screen. It takes human - * intervention to evaluate. - * - * This is especially true because the images on the screen look color-correct, - * but when viewed in the browser, they might not be. - */ -public class JaiImageProcessorTester extends Frame { - - /** Big enough to hold the JPEG file, certainly. */ - private final static int BUFFER_SIZE = 200 * 200 * 4; - - private final static Dimensions THUMBNAIL_SIZE = new Dimensions( - THUMBNAIL_WIDTH, THUMBNAIL_HEIGHT); - - private final static ImageCropData[] THUMBNAIL_DATA = new ImageCropData[] { - new ImageCropData("/Users/jeb228/Pictures/JimBlake_20010915.jpg", - 50, 50, 115), - new ImageCropData("/Users/jeb228/Pictures/brazil_collab.png", 600, - 250, 400), - new ImageCropData("/Users/jeb228/Pictures/wheel.png", 0, 0, 195), - new ImageCropData("/Users/jeb228/Pictures/DSC04203w-trans.gif", - 400, 1200, 800) }; - - private final JaiImageProcessor thumbnailer = new JaiImageProcessor(); - - @SuppressWarnings("deprecation") - private JaiImageProcessorTester() { - setTitle("Alpha Killer Test"); - addWindowListener(new CloseWindowListener()); - setLayout(createLayout()); - for (ImageCropData icd : THUMBNAIL_DATA) { - try { - InputStream mainStream = new FileInputStream(icd.filename); - File thumbFile = writeToTempFile(thumbnailer.cropAndScale( - mainStream, icd.crop, THUMBNAIL_SIZE)); - System.out.println(thumbFile.getAbsolutePath()); - - MemoryCacheSeekableStream thumbFileStream = new MemoryCacheSeekableStream( - new FileInputStream(thumbFile)); - RenderedOp thumbImage = StreamDescriptor.create( - thumbFileStream, null, null); - add(new javax.media.jai.widget.ImageCanvas(thumbImage)); - } catch (Exception e) { - e.printStackTrace(); - } - } - pack(); - setVisible(true); - } - - /** - * @param thumbStream Thumbnail stream - * @throws IOException - * @throws FileNotFoundException - */ - private File writeToTempFile(InputStream thumbStream) throws IOException, - FileNotFoundException { - File thumbFile = File.createTempFile("ImageUploaderThumbnailerTester", - ""); - OutputStream imageOutputStream = new FileOutputStream(thumbFile); - byte[] buffer = new byte[BUFFER_SIZE]; - int howMany = thumbStream.read(buffer); - imageOutputStream.write(buffer, 0, howMany); - imageOutputStream.close(); - return thumbFile; - } - - private GridLayout createLayout() { - GridLayout layout = new GridLayout(1, THUMBNAIL_DATA.length); - layout.setHgap(10); - return layout; - } - - @SuppressWarnings("unused") - public static void main(String[] args) { - Logger.getLogger(JaiImageProcessor.class).setLevel(Level.DEBUG); - new JaiImageProcessorTester(); - } - - private static class ImageCropData { - final String filename; - final CropRectangle crop; - - ImageCropData(String filename, int x, int y, int size) { - this.filename = filename; - this.crop = new CropRectangle(x, y, size, - size); - } - } - - private class CloseWindowListener extends WindowAdapter { - @Override - public void windowClosing(WindowEvent e) { - setVisible(false); - dispose(); - System.exit(0); - } - } -} diff --git a/api/src/test/java/edu/cornell/mannlib/vitro/webapp/imageprocessor/jai/JaiImageProcessorTester2.java b/api/src/test/java/edu/cornell/mannlib/vitro/webapp/imageprocessor/jai/JaiImageProcessorTester2.java deleted file mode 100644 index 0def41eec..000000000 --- a/api/src/test/java/edu/cornell/mannlib/vitro/webapp/imageprocessor/jai/JaiImageProcessorTester2.java +++ /dev/null @@ -1,271 +0,0 @@ -/* $This file is distributed under the terms of the license in /doc/license.txt$ */ - -package edu.cornell.mannlib.vitro.webapp.imageprocessor.jai; - -import java.awt.BorderLayout; -import java.awt.Color; -import java.awt.Component; -import java.awt.Frame; -import java.awt.GridLayout; -import java.awt.Label; -import java.awt.event.WindowAdapter; -import java.awt.event.WindowEvent; -import java.awt.image.Raster; -import java.io.FileInputStream; -import java.io.InputStream; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -import javax.media.jai.JAI; -import javax.media.jai.RenderedOp; -import javax.media.jai.operator.StreamDescriptor; -import javax.swing.BorderFactory; -import javax.swing.JPanel; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.apache.log4j.Appender; -import org.apache.log4j.Level; -import org.apache.log4j.Logger; -import org.apache.log4j.PatternLayout; - -import com.sun.media.jai.codec.MemoryCacheSeekableStream; - -import edu.cornell.mannlib.vitro.webapp.imageprocessor.jai.JaiImageProcessorTester2.CropDataSet.CropData; -import edu.cornell.mannlib.vitro.webapp.imageprocessor.jai.JaiImageProcessor.NonNoisyImagingListener; -import edu.cornell.mannlib.vitro.webapp.modules.imageProcessor.ImageProcessor.CropRectangle; -import edu.cornell.mannlib.vitro.webapp.modules.imageProcessor.ImageProcessor.Dimensions; - -/** - * This is not a unit test, so it is not named BlahBlahTest. - * - * Instead, it's a test harness that creates thumbnails and displays them in a - * window on the screen. It takes human intervention to evaluate. - * - * The goal here is to see whether differences in crop dimensions might cause - * one or more black edges on the thumbnails. - */ -@SuppressWarnings("deprecation") -public class JaiImageProcessorTester2 extends Frame { - private static final Log log = LogFactory - .getLog(JaiImageProcessorTester2.class); - - private static final int ROWS = 6; - private static final int COLUMNS = 9; - - private static final int EDGE_THRESHOLD = 6000; - - private static final Dimensions THUMBNAIL_SIZE = new Dimensions(200, 200); - - /** Keep things quiet. */ - static { - JAI.getDefaultInstance().setImagingListener( - new NonNoisyImagingListener()); - } - - private final String imagePath; - private final JaiImageProcessor thumbnailer; - - public JaiImageProcessorTester2(String imagePath, - CropDataSet cropDataSet) { - this.imagePath = imagePath; - this.thumbnailer = new JaiImageProcessor(); - - setTitle("Cropping edging test"); - addWindowListener(new CloseWindowListener()); - setLayout(new GridLayout(ROWS, COLUMNS)); - - for (CropData cropData : cropDataSet.crops()) { - add(createImagePanel(cropData)); - } - - pack(); - setVisible(true); - } - - private Component createImagePanel(CropData cropData) { - RenderedOp image = createCroppedImage(cropData); - - - Set blackSides = checkBlackEdges(image); - if (!blackSides.isEmpty()) { - log.warn("edges at " + cropData + ", " + blackSides); - } - - String legend = "left=" + cropData.left + ", top=" + cropData.top - + ", size=" + cropData.size; - Label l = new Label(); - l.setAlignment(Label.CENTER); - if (!blackSides.isEmpty()) { - l.setBackground(new Color(0xFFDDDD)); - legend += " " + blackSides; - } - l.setText(legend); - - JPanel p = new JPanel(); - p.setLayout(new BorderLayout()); - p.add("South", l); - p.add("Center", new javax.media.jai.widget.ImageCanvas(image)); - p.setBackground(new Color(0xFFFFFF)); - p.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10)); - - return p; - } - - private RenderedOp createCroppedImage(CropData cropData) { - try { - InputStream mainStream = new FileInputStream(imagePath); - CropRectangle rectangle = new CropRectangle(cropData.left, - cropData.top, cropData.size, cropData.size); - InputStream thumbnailStream = thumbnailer.cropAndScale(mainStream, - rectangle, THUMBNAIL_SIZE); - - return StreamDescriptor.create(new MemoryCacheSeekableStream( - thumbnailStream), null, null); - } catch (Exception e) { - throw new RuntimeException(e); - } - } - - private Set checkBlackEdges(RenderedOp image) { - Raster imageData = image.getData(); - - int minX = imageData.getMinX(); - int minY = imageData.getMinY(); - int maxX = minX + imageData.getWidth() - 1; - int maxY = minY + imageData.getHeight() - 1; - - Set blackSides = new HashSet(); - if (isBlackEdge(minX, minX, minY, maxY, imageData)) { - blackSides.add("left"); - } - if (isBlackEdge(minX, maxX, minY, minY, imageData)) { - blackSides.add("top"); - } - if (isBlackEdge(maxX, maxX, minY, maxY, imageData)) { - blackSides.add("right"); - } - if (isBlackEdge(minX, maxX, maxY, maxY, imageData)) { - blackSides.add("bottom"); - } - return blackSides; - } - - private boolean isBlackEdge(int fromX, int toX, int fromY, int toY, - Raster imageData) { - int edgeTotal = 0; - try { - for (int col = fromX; col <= toX; col++) { - for (int row = fromY; row <= toY; row++) { - edgeTotal += sumPixel(imageData, col, row); - } - } - } catch (Exception e) { - log.error("can't sum edge: fromX=" + fromX + ", toX=" + toX - + ", fromY=" + fromY + ", toY=" + toY + ", imageWidth=" - + imageData.getWidth() + ", imageHeight=" - + imageData.getHeight() + ": " + e); - } - - log.debug("edge total = " + edgeTotal); - return edgeTotal < EDGE_THRESHOLD; - } - - private int sumPixel(Raster imageData, int col, int row) { - int pixelSum = 0; - int[] pixel = imageData.getPixel(col, row, new int[0]); - for (int value : pixel) { - pixelSum += value; - } - return pixelSum; - } - - /** - *
    -	 * The plan:
    -	 * 
    -	 * Provide the path to an image file.
    -	 * Figure how many images can fit on the screen.
    -	 * Crop in increments, starting at 0,0 and varying the size of the crop.
    -	 * Crop in increments, incrementing from 0,0 downward, and varying the size of the crop.
    -	 * 
    -	 * Start by creating 4 x 4 images in a window, and incrementing from 201 to 216.
    -	 * 
    - */ - - public static void main(String[] args) { - Logger rootLogger = Logger.getRootLogger(); - Appender appender = (Appender) rootLogger.getAllAppenders() - .nextElement(); - appender.setLayout(new PatternLayout("%-5p [%c{1}] %m%n")); - - Logger.getLogger(JaiImageProcessor.class).setLevel(Level.DEBUG); - Logger.getLogger(JaiImageProcessorTester2.class).setLevel( - Level.INFO); - - CropDataSet cropDataSet = new CropDataSet(); - for (int i = 0; i < ROWS * COLUMNS; i++) { -// cropDataSet.add(i, i, 201 + i); - cropDataSet.add(0, 0, 201 + i); - } - - new JaiImageProcessorTester2( - "C:/Users/jeb228/Pictures/wheel.png", cropDataSet); - -// new ImageUploaderThumbnailerTester_2( -// "C:/Users/jeb228/Pictures/DSC04203w-trans.jpg", cropDataSet); - - // new ImageUploaderThumbnailerTester_2( -// "C:/Development/JIRA issues/NIHVIVO-2477 Black borders on thumbnails/" -// + "images from Alex/uploads/file_storage_root/a~n/411/9/" -// + "De^20Bartolome^2c^20Charles^20A^20M_100037581.jpg", -// cropDataSet); - } - - // ---------------------------------------------------------------------- - // helper classes - // ---------------------------------------------------------------------- - - private class CloseWindowListener extends WindowAdapter { - @Override - public void windowClosing(WindowEvent e) { - setVisible(false); - dispose(); - System.exit(0); - } - } - - public static class CropDataSet { - private final List crops = new ArrayList(); - - CropDataSet add(int left, int top, int size) { - crops.add(new CropData(left, top, size)); - return this; - } - - Collection crops() { - return Collections.unmodifiableCollection(crops); - } - - public static class CropData { - final int left; - final int top; - final int size; - - CropData(int left, int top, int size) { - this.left = left; - this.top = top; - this.size = size; - } - - @Override - public String toString() { - return "CropData[" + left + ", " + top + ", " + size + "]"; - } - } - } -} diff --git a/api/src/test/java/edu/cornell/mannlib/vitro/webapp/utils/SparqlQueryRunnerTest.java b/api/src/test/java/edu/cornell/mannlib/vitro/webapp/utils/SparqlQueryRunnerTest.java deleted file mode 100644 index 2ae77111e..000000000 --- a/api/src/test/java/edu/cornell/mannlib/vitro/webapp/utils/SparqlQueryRunnerTest.java +++ /dev/null @@ -1,92 +0,0 @@ -/* $This file is distributed under the terms of the license in /doc/license.txt$ */ - -package edu.cornell.mannlib.vitro.webapp.utils; - -import static org.junit.Assert.*; -import static edu.cornell.mannlib.vitro.webapp.utils.SparqlQueryRunner.*; - -import org.junit.Test; - -import edu.cornell.mannlib.vitro.testing.AbstractTestClass; - -/** - * For now, just test the methods that manipulate the query string. - */ -public class SparqlQueryRunnerTest extends AbstractTestClass { - @Test - public void bindValuesNameNotFound() { - String raw = "No such name here"; - String expected = raw; - assertEquals(expected, bindValues(raw, uriValue("bogus", "BOGUS"))); - } - - @Test - public void bindOneUri() { - String raw = "Replace both ?this and ?this also."; - String expected = "Replace both and also."; - assertEquals(expected, bindValues(raw, uriValue("this", "URI"))); - } - - @Test - public void bindTwoUris() { - String raw = "Replace both ?this and ?that also."; - String expected = "Replace both and also."; - assertEquals( - expected, - bindValues(raw, uriValue("this", "URI"), - uriValue("that", "ANOTHER"))); - } - - @Test - public void honorWordBoundary() { - String raw = "Replace ?this but not ?thistle."; - String expected = "Replace but not ?thistle."; - assertEquals(expected, bindValues(raw, uriValue("this", "URI"))); - } - - @Test - public void honorStringLimit() { - String raw = "?this"; - String expected = ""; - assertEquals(expected, bindValues(raw, uriValue("this", "URI"))); - } - - private static final String REAL_WORLD_RAW = "" // - + "PREFIX : \n" // - + "\n" // - + "SELECT DISTINCT ?context ?config \n" // - + "WHERE { \n" // - + " ?context a :ConfigContext ; \n" // - + " :configContextFor ?baseUri ; \n" // - + " :qualifiedByDomain ?domainUri ; \n" // - + " :qualifiedBy ?rangeUri ; \n" // - + " :hasConfiguration ?config . \n" // - + "} \n"; // - - private static final String REAL_WORLD_EXPECTED = "" // - + "PREFIX : \n" // - + "\n" // - + "SELECT DISTINCT ?context ?config \n" // - + "WHERE { \n" // - + " ?context a :ConfigContext ; \n" // - + " :configContextFor ; \n" // - + " :qualifiedByDomain ; \n" // - + " :qualifiedBy ; \n" // - + " :hasConfiguration ?config . \n" // - + "} \n"; // - - @Test - public void realWorldExample() { - assertEquals( - REAL_WORLD_EXPECTED, - bindValues( - REAL_WORLD_RAW, - uriValue("baseUri", - "http://vivoweb.org/ontology/core#relates"), - uriValue("domainUri", - "http://vivoweb.org/ontology/core#Contract"), - uriValue("rangeUri", - "http://vivoweb.org/ontology/core#ResearcherRole"))); - } - -} diff --git a/api/src/test/java/edu/cornell/mannlib/vitro/webapp/utils/configuration/ConfigurationBeanLoaderTest.java b/api/src/test/java/edu/cornell/mannlib/vitro/webapp/utils/configuration/ConfigurationBeanLoaderTest.java index 32c3c3c71..c98a3a80c 100644 --- a/api/src/test/java/edu/cornell/mannlib/vitro/webapp/utils/configuration/ConfigurationBeanLoaderTest.java +++ b/api/src/test/java/edu/cornell/mannlib/vitro/webapp/utils/configuration/ConfigurationBeanLoaderTest.java @@ -5,7 +5,6 @@ package edu.cornell.mannlib.vitro.webapp.utils.configuration; import static org.apache.jena.datatypes.xsd.XSDDatatype.XSDfloat; import static org.apache.jena.datatypes.xsd.XSDDatatype.XSDstring; import static edu.cornell.mannlib.vitro.testing.ModelUtilitiesTestHelper.dataProperty; -import static edu.cornell.mannlib.vitro.testing.ModelUtilitiesTestHelper.model; import static edu.cornell.mannlib.vitro.testing.ModelUtilitiesTestHelper.objectProperty; import static edu.cornell.mannlib.vitro.testing.ModelUtilitiesTestHelper.typeStatement; import static edu.cornell.mannlib.vitro.webapp.utils.configuration.ConfigurationBeanLoader.toJavaUri; @@ -20,7 +19,6 @@ import java.util.Collections; import java.util.HashSet; import java.util.Set; -import org.junit.Before; import org.junit.Ignore; import org.junit.Test; @@ -32,16 +30,13 @@ import stubs.javax.servlet.http.HttpSessionStub; import org.apache.jena.rdf.model.Model; import org.apache.jena.rdf.model.Statement; -import edu.cornell.mannlib.vitro.testing.AbstractTestClass; import edu.cornell.mannlib.vitro.webapp.modelaccess.ContextModelAccess; -import edu.cornell.mannlib.vitro.webapp.modelaccess.ModelAccess.ModelAccessFactory; import edu.cornell.mannlib.vitro.webapp.modelaccess.RequestModelAccess; import edu.cornell.mannlib.vitro.webapp.utils.configuration.ConfigurationRdfParser.InvalidConfigurationRdfException; import edu.cornell.mannlib.vitro.webapp.utils.configuration.InstanceWrapper.InstanceWrapperException; import edu.cornell.mannlib.vitro.webapp.utils.configuration.PropertyType.PropertyTypeException; import edu.cornell.mannlib.vitro.webapp.utils.configuration.WrappedInstance.NoSuchPropertyMethodException; import edu.cornell.mannlib.vitro.webapp.utils.configuration.WrappedInstance.ResourceUnavailableException; -import edu.cornell.mannlib.vitro.webapp.utils.configuration.WrappedInstance.ValidationFailedException; /** * TODO @@ -50,47 +45,8 @@ import edu.cornell.mannlib.vitro.webapp.utils.configuration.WrappedInstance.Vali * instances by URIs, so if a property refers to a created instance, we just * pass it in. */ -public class ConfigurationBeanLoaderTest extends AbstractTestClass { - private static final String GENERIC_INSTANCE_URI = "http://mytest.edu/some_instance"; - private static final String GENERIC_PROPERTY_URI = "http://mytest.edu/some_property"; - - private static final String SIMPLE_SUCCESS_INSTANCE_URI = "http://mytest.edu/simple_success_instance"; - - private static final String FULL_SUCCESS_INSTANCE_URI = "http://mytest.edu/full_success_instance"; - private static final String FULL_SUCCESS_BOOST_PROPERTY = "http://mydomain.edu/hasBoost"; - private static final String FULL_SUCCESS_TEXT_PROPERTY = "http://mydomain.edu/hasText"; - private static final String FULL_SUCCESS_HELPER_PROPERTY = "http://mydomain.edu/hasHelper"; - private static final String FULL_SUCCESS_HELPER_INSTANCE_URI = "http://mytest.edu/full_success_helper_instance"; - - private ServletContextStub ctx; - private HttpSessionStub session; - private HttpServletRequestStub req; - - private Model model; - - private ConfigurationBeanLoader loader; - private ConfigurationBeanLoader noRequestLoader; - private ConfigurationBeanLoader noContextLoader; - - @Before - public void setup() { - ctx = new ServletContextStub(); - - session = new HttpSessionStub(); - session.setServletContext(ctx); - - req = new HttpServletRequestStub(); - req.setSession(session); - - @SuppressWarnings("unused") - ModelAccessFactory maf = new ModelAccessFactoryStub(); - - model = model(); - - loader = new ConfigurationBeanLoader(model, req); - noRequestLoader = new ConfigurationBeanLoader(model, ctx); - noContextLoader = new ConfigurationBeanLoader(model); - } +public class ConfigurationBeanLoaderTest extends + ConfigurationBeanLoaderTestBase { // ---------------------------------------------------------------------- // Constructor tests @@ -308,276 +264,6 @@ public class ConfigurationBeanLoaderTest extends AbstractTestClass { // -------------------------------------------- - @Test - public void propertyMethodHasNoParameter_throwsException() - throws ConfigurationBeanLoaderException { - model.add(typeStatement(GENERIC_INSTANCE_URI, - toJavaUri(NoParameterOnPropertyMethod.class))); - - expectSimpleFailure( - NoParameterOnPropertyMethod.class, - throwable(ConfigurationBeanLoaderException.class, - "Failed to load"), - throwable(InstanceWrapperException.class, - "must accept exactly one parameter")); - } - - public static class NoParameterOnPropertyMethod { - @Property(uri = GENERIC_PROPERTY_URI) - public void methodTakesNoParameters() { - // Not suitable as a property method. - } - } - - // -------------------------------------------- - - @Test - public void propertyMethodHasMultipleParameters_throwsException() - throws ConfigurationBeanLoaderException { - model.add(typeStatement(GENERIC_INSTANCE_URI, - toJavaUri(MultipleParametersOnPropertyMethod.class))); - - expectSimpleFailure( - MultipleParametersOnPropertyMethod.class, - throwable(ConfigurationBeanLoaderException.class, - "Failed to load"), - throwable(InstanceWrapperException.class, - "must accept exactly one parameter")); - } - - public static class MultipleParametersOnPropertyMethod { - @SuppressWarnings("unused") - @Property(uri = GENERIC_PROPERTY_URI) - public void methodTakesMultipleParameters(String s, Float f) { - // Not suitable as a property method. - } - } - - // -------------------------------------------- - - @Test - public void propertyMethodHasInvalidParameter_throwsException() - throws ConfigurationBeanLoaderException { - model.add(typeStatement(GENERIC_INSTANCE_URI, - toJavaUri(InvalidParameterOnPropertyMethod.class))); - - expectSimpleFailure( - InvalidParameterOnPropertyMethod.class, - throwable(ConfigurationBeanLoaderException.class, - "Failed to load"), - throwable(InstanceWrapperException.class, - "Failed to create the PropertyMethod")); - } - - public static class InvalidParameterOnPropertyMethod { - @SuppressWarnings("unused") - @Property(uri = GENERIC_PROPERTY_URI) - public void methodTakesInvalidParameters(byte b) { - // Not suitable as a property method. - } - } - - // -------------------------------------------- - - @Test - public void propertyMethodDoesNotReturnVoid_throwsException() - throws ConfigurationBeanLoaderException { - model.add(typeStatement(GENERIC_INSTANCE_URI, - toJavaUri(PropertyMethodMustReturnVoid.class))); - - expectSimpleFailure( - PropertyMethodMustReturnVoid.class, - throwable(ConfigurationBeanLoaderException.class, - "Failed to load"), - throwable(InstanceWrapperException.class, "should return void")); - } - - public static class PropertyMethodMustReturnVoid { - @Property(uri = GENERIC_PROPERTY_URI) - public String methodReturnIsNotVoid(String s) { - // Not suitable as a property method. - return s; - } - } - - // -------------------------------------------- - - @Test - public void propertyMethodNotAccessible_throwsException() - throws ConfigurationBeanLoaderException { - model.add(typeStatement(GENERIC_INSTANCE_URI, - toJavaUri(PropertyMethodIsPrivate.class))); - model.add(dataProperty(GENERIC_INSTANCE_URI, GENERIC_PROPERTY_URI, - "can't store in a private method.")); - - expectSimpleFailure( - PropertyMethodIsPrivate.class, - throwable(ConfigurationBeanLoaderException.class, - "Failed to load"), - throwable(PropertyTypeException.class, - "Property method failed.")); - } - - public static class PropertyMethodIsPrivate { - @SuppressWarnings("unused") - @Property(uri = GENERIC_PROPERTY_URI) - private void methodReturnIsNotVoid(String s) { - // Not suitable as a property method. - } - } - - // -------------------------------------------- - - @Test - public void propertyMethodThrowsException_throwsException() - throws ConfigurationBeanLoaderException { - model.add(typeStatement(GENERIC_INSTANCE_URI, - toJavaUri(PropertyMethodFails.class))); - model.add(dataProperty(GENERIC_INSTANCE_URI, GENERIC_PROPERTY_URI, - "exception while loading.")); - - expectSimpleFailure( - PropertyMethodFails.class, - throwable(ConfigurationBeanLoaderException.class, - "Failed to load"), - throwable(PropertyTypeException.class, - "Property method failed.")); - } - - public static class PropertyMethodFails { - @SuppressWarnings("unused") - @Property(uri = GENERIC_PROPERTY_URI) - public void methodThrowsException(String s) { - if (true) { - throw new RuntimeException("property method fails."); - } - } - } - - // -------------------------------------------- - - @Test - public void propertyMethodDuplicateUri_throwsException() - throws ConfigurationBeanLoaderException { - model.add(typeStatement(GENERIC_INSTANCE_URI, - toJavaUri(TwoMethodsWithSameUri.class))); - - expectSimpleFailure( - TwoMethodsWithSameUri.class, - throwable(ConfigurationBeanLoaderException.class, - "Failed to load"), - throwable(InstanceWrapperException.class, - "methods have the same URI")); - } - - public static class TwoMethodsWithSameUri { - @SuppressWarnings("unused") - @Property(uri = GENERIC_PROPERTY_URI) - public void firstProperty(String s) { - // Nothing to do - } - - @SuppressWarnings("unused") - @Property(uri = GENERIC_PROPERTY_URI) - public void secondProperty(String s) { - // Nothing to do - } - } - - // -------------------------------------------- - - @Test - public void validationMethodHasParameters_throwsException() - throws ConfigurationBeanLoaderException { - model.add(typeStatement(GENERIC_INSTANCE_URI, - toJavaUri(ValidationMethodWithParameter.class))); - - expectSimpleFailure( - ValidationMethodWithParameter.class, - throwable(ConfigurationBeanLoaderException.class, - "Failed to load"), - throwable(InstanceWrapperException.class, - "should not have parameters")); - } - - public static class ValidationMethodWithParameter { - @SuppressWarnings("unused") - @Validation - public void validateWithParameter(String s) { - // Nothing to do - } - } - - // -------------------------------------------- - - @Test - public void validationMethodDoesNotReturnVoid_throwsException() - throws ConfigurationBeanLoaderException { - model.add(typeStatement(GENERIC_INSTANCE_URI, - toJavaUri(ValidationMethodShouldReturnVoid.class))); - - expectSimpleFailure( - ValidationMethodShouldReturnVoid.class, - throwable(ConfigurationBeanLoaderException.class, - "Failed to load"), - throwable(InstanceWrapperException.class, "should return void")); - } - - public static class ValidationMethodShouldReturnVoid { - @Validation - public String validateWithReturnType() { - return "Hi there!"; - } - } - - // -------------------------------------------- - - @Test - public void validationMethodNotAccessible_throwsException() - throws ConfigurationBeanLoaderException { - model.add(typeStatement(GENERIC_INSTANCE_URI, - toJavaUri(ValidationMethodIsPrivate.class))); - - expectSimpleFailure( - ValidationMethodIsPrivate.class, - throwable(ConfigurationBeanLoaderException.class, - "Failed to load"), - throwable(ValidationFailedException.class, - "Error executing validation method")); - } - - public static class ValidationMethodIsPrivate { - @Validation - private void validateIsPrivate() { - // private method - } - } - - // -------------------------------------------- - - @Test - public void validationMethodThrowsException_throwsException() - throws ConfigurationBeanLoaderException { - model.add(typeStatement(GENERIC_INSTANCE_URI, - toJavaUri(ValidationThrowsException.class))); - - expectSimpleFailure( - ValidationThrowsException.class, - throwable(ConfigurationBeanLoaderException.class, - "Failed to load"), - throwable(ValidationFailedException.class, - "Error executing validation method")); - } - - public static class ValidationThrowsException { - @Validation - public void validateFails() { - throw new RuntimeException("from validation method"); - } - } - - // -------------------------------------------- - @Test public void loaderCantSatisfyContextModelsUser_throwsException() throws ConfigurationBeanLoaderException { @@ -1041,6 +727,7 @@ public class ConfigurationBeanLoaderTest extends AbstractTestClass { // Additional tests // ---------------------------------------------------------------------- + @SuppressWarnings("unused") @Test @Ignore // TODO @@ -1049,6 +736,7 @@ public class ConfigurationBeanLoaderTest extends AbstractTestClass { fail("circularReferencesAreNotFatal not implemented"); } + @SuppressWarnings("unused") @Test @Ignore // TODO deals with circularity. @@ -1057,6 +745,7 @@ public class ConfigurationBeanLoaderTest extends AbstractTestClass { fail("subordinateObjectCantBeLoaded_leavesNoAccessibleInstanceOfParent not implemented"); } + @SuppressWarnings("unused") @Test @Ignore // TODO deals with circularity. @@ -1065,42 +754,4 @@ public class ConfigurationBeanLoaderTest extends AbstractTestClass { fail("parentObjectCantBeLoaded_leavesNoAccessibleInstanceOfSubordinate not implemented"); } - // ---------------------------------------------------------------------- - // Helper methods for simple failure - // ---------------------------------------------------------------------- - - private void expectSimpleFailure(Class failureClass, - ExpectedThrowable expected, ExpectedThrowable cause) - throws ConfigurationBeanLoaderException { - expectException(expected.getClazz(), expected.getMessageSubstring(), - cause.getClazz(), cause.getMessageSubstring()); - - @SuppressWarnings("unused") - Object unused = loader.loadInstance(GENERIC_INSTANCE_URI, failureClass); - } - - private ExpectedThrowable throwable(Class clazz, - String messageSubstring) { - return new ExpectedThrowable(clazz, messageSubstring); - } - - private static class ExpectedThrowable { - private final Class clazz; - private final String messageSubstring; - - public ExpectedThrowable(Class clazz, - String messageSubstring) { - this.clazz = clazz; - this.messageSubstring = messageSubstring; - } - - public Class getClazz() { - return clazz; - } - - public String getMessageSubstring() { - return messageSubstring; - } - } - } diff --git a/api/src/test/java/edu/cornell/mannlib/vitro/webapp/utils/configuration/ConfigurationBeanLoaderTestBase.java b/api/src/test/java/edu/cornell/mannlib/vitro/webapp/utils/configuration/ConfigurationBeanLoaderTestBase.java new file mode 100644 index 000000000..b102738cf --- /dev/null +++ b/api/src/test/java/edu/cornell/mannlib/vitro/webapp/utils/configuration/ConfigurationBeanLoaderTestBase.java @@ -0,0 +1,100 @@ +/* $This file is distributed under the terms of the license in /doc/license.txt$ */ + +package edu.cornell.mannlib.vitro.webapp.utils.configuration; + +import static edu.cornell.mannlib.vitro.testing.ModelUtilitiesTestHelper.model; + +import org.apache.jena.rdf.model.Model; +import org.junit.Before; + +import edu.cornell.mannlib.vitro.testing.AbstractTestClass; +import edu.cornell.mannlib.vitro.webapp.modelaccess.ModelAccess.ModelAccessFactory; +import stubs.edu.cornell.mannlib.vitro.webapp.modelaccess.ModelAccessFactoryStub; +import stubs.javax.servlet.ServletContextStub; +import stubs.javax.servlet.http.HttpServletRequestStub; +import stubs.javax.servlet.http.HttpSessionStub; + +/** + * TODO + */ +public class ConfigurationBeanLoaderTestBase extends AbstractTestClass { + protected static final String GENERIC_INSTANCE_URI = "http://mytest.edu/some_instance"; + protected static final String GENERIC_PROPERTY_URI = "http://mytest.edu/some_property"; + + protected static final String SIMPLE_SUCCESS_INSTANCE_URI = "http://mytest.edu/simple_success_instance"; + + protected static final String FULL_SUCCESS_INSTANCE_URI = "http://mytest.edu/full_success_instance"; + protected static final String FULL_SUCCESS_BOOST_PROPERTY = "http://mydomain.edu/hasBoost"; + protected static final String FULL_SUCCESS_TEXT_PROPERTY = "http://mydomain.edu/hasText"; + protected static final String FULL_SUCCESS_HELPER_PROPERTY = "http://mydomain.edu/hasHelper"; + protected static final String FULL_SUCCESS_HELPER_INSTANCE_URI = "http://mytest.edu/full_success_helper_instance"; + + private ServletContextStub ctx; + private HttpSessionStub session; + private HttpServletRequestStub req; + + protected Model model; + + protected ConfigurationBeanLoader loader; + protected ConfigurationBeanLoader noRequestLoader; + protected ConfigurationBeanLoader noContextLoader; + + @Before + public void setup() { + ctx = new ServletContextStub(); + + session = new HttpSessionStub(); + session.setServletContext(ctx); + + req = new HttpServletRequestStub(); + req.setSession(session); + + @SuppressWarnings("unused") + ModelAccessFactory maf = new ModelAccessFactoryStub(); + + model = model(); + + loader = new ConfigurationBeanLoader(model, req); + noRequestLoader = new ConfigurationBeanLoader(model, ctx); + noContextLoader = new ConfigurationBeanLoader(model); + } + + // ---------------------------------------------------------------------- + // Helper methods for simple failure + // ---------------------------------------------------------------------- + + protected void expectSimpleFailure(Class failureClass, + ExpectedThrowable expected, ExpectedThrowable cause) + throws ConfigurationBeanLoaderException { + expectException(expected.getClazz(), expected.getMessageSubstring(), + cause.getClazz(), cause.getMessageSubstring()); + + @SuppressWarnings("unused") + Object unused = loader.loadInstance(GENERIC_INSTANCE_URI, failureClass); + } + + protected ExpectedThrowable throwable(Class clazz, + String messageSubstring) { + return new ExpectedThrowable(clazz, messageSubstring); + } + + private static class ExpectedThrowable { + private final Class clazz; + private final String messageSubstring; + + public ExpectedThrowable(Class clazz, + String messageSubstring) { + this.clazz = clazz; + this.messageSubstring = messageSubstring; + } + + public Class getClazz() { + return clazz; + } + + public String getMessageSubstring() { + return messageSubstring; + } + } + +} diff --git a/api/src/test/java/edu/cornell/mannlib/vitro/webapp/utils/configuration/ConfigurationBeanLoader_Cardinality_Test.java b/api/src/test/java/edu/cornell/mannlib/vitro/webapp/utils/configuration/ConfigurationBeanLoader_Cardinality_Test.java new file mode 100644 index 000000000..2e9740a90 --- /dev/null +++ b/api/src/test/java/edu/cornell/mannlib/vitro/webapp/utils/configuration/ConfigurationBeanLoader_Cardinality_Test.java @@ -0,0 +1,251 @@ +/* $This file is distributed under the terms of the license in /doc/license.txt$ */ + +package edu.cornell.mannlib.vitro.webapp.utils.configuration; + +import static edu.cornell.mannlib.vitro.testing.ModelUtilitiesTestHelper.dataProperty; +import static edu.cornell.mannlib.vitro.testing.ModelUtilitiesTestHelper.objectProperty; +import static edu.cornell.mannlib.vitro.testing.ModelUtilitiesTestHelper.typeStatement; +import static edu.cornell.mannlib.vitro.webapp.utils.configuration.ConfigurationBeanLoader.toJavaUri; +import static org.junit.Assert.assertEquals; + +import java.util.Set; + +import org.apache.jena.rdf.model.Statement; +import org.junit.Test; + +import edu.cornell.mannlib.vitro.webapp.utils.configuration.InstanceWrapper.InstanceWrapperException; +import edu.cornell.mannlib.vitro.webapp.utils.configuration.WrappedInstance.CardinalityException; + +/** + * Tests for minOccurs, maxOccurs on the @Property annotation. + */ +public class ConfigurationBeanLoader_Cardinality_Test extends + ConfigurationBeanLoaderTestBase { + + private static final Statement[] FOUND_NONE = new Statement[0]; + + private static final Statement[] FOUND_ONE = new Statement[] { dataProperty( + GENERIC_INSTANCE_URI, GENERIC_PROPERTY_URI, "value One") }; + + private static final Statement[] FOUND_TWO = new Statement[] { + dataProperty(GENERIC_INSTANCE_URI, GENERIC_PROPERTY_URI, + "value One"), + dataProperty(GENERIC_INSTANCE_URI, GENERIC_PROPERTY_URI, + "value Two") }; + + private static final Statement[] FOUND_THREE = new Statement[] { + dataProperty(GENERIC_INSTANCE_URI, GENERIC_PROPERTY_URI, + "value One"), + dataProperty(GENERIC_INSTANCE_URI, GENERIC_PROPERTY_URI, + "value Two"), + dataProperty(GENERIC_INSTANCE_URI, GENERIC_PROPERTY_URI, + "value Three") }; + + private static final Statement[] FOUND_FOUR = new Statement[] { + dataProperty(GENERIC_INSTANCE_URI, GENERIC_PROPERTY_URI, + "value One"), + dataProperty(GENERIC_INSTANCE_URI, GENERIC_PROPERTY_URI, + "value Two"), + dataProperty(GENERIC_INSTANCE_URI, GENERIC_PROPERTY_URI, + "value Three"), + dataProperty(GENERIC_INSTANCE_URI, GENERIC_PROPERTY_URI, + "value Four") }; + + // -------------------------------------------- + + @Test + public void minOccursIsNegative_throwsException() + throws ConfigurationBeanLoaderException { + model.add(typeStatement(GENERIC_INSTANCE_URI, + toJavaUri(NegativeMinOccurs.class))); + model.add(objectProperty(GENERIC_INSTANCE_URI, GENERIC_PROPERTY_URI, + "http://some.other/uri")); + + expectSimpleFailure( + NegativeMinOccurs.class, + throwable(ConfigurationBeanLoaderException.class, + "Failed to load"), + throwable(InstanceWrapperException.class, + "must not be negative")); + } + + public static class NegativeMinOccurs { + @SuppressWarnings("unused") + @Property(uri = GENERIC_PROPERTY_URI, minOccurs = -1) + public void setValue(String value) { + // Nothing to do + } + } + + // -------------------------------------------- + + @Test + public void maxOccursLessThanMinOccurs_throwsException() + throws ConfigurationBeanLoaderException { + model.add(typeStatement(GENERIC_INSTANCE_URI, + toJavaUri(MaxOccursLessThanMinOccurs.class))); + model.add(objectProperty(GENERIC_INSTANCE_URI, GENERIC_PROPERTY_URI, + "http://some.other/uri")); + + expectSimpleFailure( + MaxOccursLessThanMinOccurs.class, + throwable(ConfigurationBeanLoaderException.class, + "Failed to load"), + throwable(InstanceWrapperException.class, + "not be less than minOccurs")); + } + + public static class MaxOccursLessThanMinOccurs { + @SuppressWarnings("unused") + @Property(uri = GENERIC_PROPERTY_URI, minOccurs = 2, maxOccurs = 1) + public void setValue(String value) { + // Nothing to do + } + } + + // -------------------------------------------- + + @Test + public void expectingSomeFoundNone_throwsException() + throws ConfigurationBeanLoaderException { + model.add(typeStatement(GENERIC_INSTANCE_URI, + toJavaUri(ExpectingSome.class))); + model.add(FOUND_NONE); + + expectSimpleFailure( + ExpectingSome.class, + throwable(ConfigurationBeanLoaderException.class, + "Failed to load"), + throwable(CardinalityException.class, "Expecting at least 2")); + } + + @Test + public void expectingSomeFoundFewer_throwsException() + throws ConfigurationBeanLoaderException { + model.add(typeStatement(GENERIC_INSTANCE_URI, + toJavaUri(ExpectingSome.class))); + model.add(FOUND_ONE); + + expectSimpleFailure( + ExpectingSome.class, + throwable(ConfigurationBeanLoaderException.class, + "Failed to load"), + throwable(CardinalityException.class, "Expecting at least 2")); + } + + @Test + public void expectingSomeFoundMore_throwsException() + throws ConfigurationBeanLoaderException { + model.add(typeStatement(GENERIC_INSTANCE_URI, + toJavaUri(ExpectingSome.class))); + model.add(FOUND_FOUR); + + expectSimpleFailure( + ExpectingSome.class, + throwable(ConfigurationBeanLoaderException.class, + "Failed to load"), + throwable(CardinalityException.class, + "Expecting no more than 3")); + } + + @Test + public void expectingSomeFoundSome_success() + throws ConfigurationBeanLoaderException { + model.add(typeStatement(GENERIC_INSTANCE_URI, + toJavaUri(ExpectingSome.class))); + model.add(FOUND_THREE); + Set instances = loader.loadAll(ExpectingSome.class); + assertEquals(1, instances.size()); + model.removeAll(); + + model.add(typeStatement(GENERIC_INSTANCE_URI, + toJavaUri(ExpectingSome.class))); + model.add(FOUND_TWO); + instances = loader.loadAll(ExpectingSome.class); + assertEquals(1, instances.size()); + } + + public static class ExpectingSome { + @SuppressWarnings("unused") + @Property(uri = GENERIC_PROPERTY_URI, minOccurs = 2, maxOccurs = 3) + public void setValue(String value) { + // Nothing to do + } + } + + // -------------------------------------------- + + @Test + public void notSpecifiedFoundNone_success() + throws ConfigurationBeanLoaderException { + model.add(typeStatement(GENERIC_INSTANCE_URI, + toJavaUri(CardinalityNotSpecified.class))); + model.add(FOUND_NONE); + + Set instances = loader + .loadAll(CardinalityNotSpecified.class); + assertEquals(1, instances.size()); + } + + @Test + public void notSpecifiedFoundSome_success() + throws ConfigurationBeanLoaderException { + model.add(typeStatement(GENERIC_INSTANCE_URI, + toJavaUri(CardinalityNotSpecified.class))); + model.add(FOUND_FOUR); + + Set instances = loader + .loadAll(CardinalityNotSpecified.class); + assertEquals(1, instances.size()); + } + + public static class CardinalityNotSpecified { + @SuppressWarnings("unused") + @Property(uri = GENERIC_PROPERTY_URI) + public void setValue(String value) { + // Nothing to do + } + } + + // -------------------------------------------- + + @Test + public void expectNoneFoundNone_success() + throws ConfigurationBeanLoaderException { + model.add(typeStatement(GENERIC_INSTANCE_URI, + toJavaUri(ExpectNone.class))); + model.add(FOUND_NONE); + + Set instances = loader.loadAll(ExpectNone.class); + assertEquals(1, instances.size()); + } + + public static class ExpectNone { + @SuppressWarnings("unused") + @Property(uri = GENERIC_PROPERTY_URI, maxOccurs = 0) + public void setValue(String value) { + // Nothing to do + } + } + + // -------------------------------------------- + + @Test + public void expectExactlyFoundExactly_success() + throws ConfigurationBeanLoaderException { + model.add(typeStatement(GENERIC_INSTANCE_URI, + toJavaUri(ExpectTwo.class))); + model.add(FOUND_TWO); + + Set instances = loader.loadAll(ExpectTwo.class); + assertEquals(1, instances.size()); + } + + public static class ExpectTwo { + @SuppressWarnings("unused") + @Property(uri = GENERIC_PROPERTY_URI, minOccurs = 2, maxOccurs = 2) + public void setValue(String value) { + // Nothing to do + } + } +} diff --git a/api/src/test/java/edu/cornell/mannlib/vitro/webapp/utils/configuration/ConfigurationBeanLoader_PropertyTest.java b/api/src/test/java/edu/cornell/mannlib/vitro/webapp/utils/configuration/ConfigurationBeanLoader_PropertyTest.java new file mode 100644 index 000000000..3b7e28fdc --- /dev/null +++ b/api/src/test/java/edu/cornell/mannlib/vitro/webapp/utils/configuration/ConfigurationBeanLoader_PropertyTest.java @@ -0,0 +1,338 @@ +/* $This file is distributed under the terms of the license in /doc/license.txt$ */ + +package edu.cornell.mannlib.vitro.webapp.utils.configuration; + +import static edu.cornell.mannlib.vitro.testing.ModelUtilitiesTestHelper.dataProperty; +import static edu.cornell.mannlib.vitro.testing.ModelUtilitiesTestHelper.typeStatement; +import static edu.cornell.mannlib.vitro.webapp.utils.configuration.ConfigurationBeanLoader.toJavaUri; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +import org.apache.jena.rdf.model.Statement; +import org.junit.Test; + +import edu.cornell.mannlib.vitro.webapp.utils.configuration.InstanceWrapper.InstanceWrapperException; +import edu.cornell.mannlib.vitro.webapp.utils.configuration.PropertyType.PropertyTypeException; + +/** + * Tests of the @Property annotation. + */ +public class ConfigurationBeanLoader_PropertyTest extends + ConfigurationBeanLoaderTestBase { + protected static final String OTHER_PROPERTY_URI = "http://mytest.edu/different_property"; + + // -------------------------------------------- + + @Test + public void propertyMethodHasNoParameter_throwsException() + throws ConfigurationBeanLoaderException { + model.add(typeStatement(GENERIC_INSTANCE_URI, + toJavaUri(NoParameterOnPropertyMethod.class))); + + expectSimpleFailure( + NoParameterOnPropertyMethod.class, + throwable(ConfigurationBeanLoaderException.class, + "Failed to load"), + throwable(InstanceWrapperException.class, + "must accept exactly one parameter")); + } + + public static class NoParameterOnPropertyMethod { + @Property(uri = GENERIC_PROPERTY_URI) + public void methodTakesNoParameters() { + // Not suitable as a property method. + } + } + + // -------------------------------------------- + + @Test + public void propertyMethodHasMultipleParameters_throwsException() + throws ConfigurationBeanLoaderException { + model.add(typeStatement(GENERIC_INSTANCE_URI, + toJavaUri(MultipleParametersOnPropertyMethod.class))); + + expectSimpleFailure( + MultipleParametersOnPropertyMethod.class, + throwable(ConfigurationBeanLoaderException.class, + "Failed to load"), + throwable(InstanceWrapperException.class, + "must accept exactly one parameter")); + } + + public static class MultipleParametersOnPropertyMethod { + @SuppressWarnings("unused") + @Property(uri = GENERIC_PROPERTY_URI) + public void methodTakesMultipleParameters(String s, Float f) { + // Not suitable as a property method. + } + } + + // -------------------------------------------- + + @Test + public void propertyMethodHasInvalidParameter_throwsException() + throws ConfigurationBeanLoaderException { + model.add(typeStatement(GENERIC_INSTANCE_URI, + toJavaUri(InvalidParameterOnPropertyMethod.class))); + + expectSimpleFailure( + InvalidParameterOnPropertyMethod.class, + throwable(ConfigurationBeanLoaderException.class, + "Failed to load"), + throwable(InstanceWrapperException.class, + "Failed to create the PropertyMethod")); + } + + public static class InvalidParameterOnPropertyMethod { + @SuppressWarnings("unused") + @Property(uri = GENERIC_PROPERTY_URI) + public void methodTakesInvalidParameters(byte b) { + // Not suitable as a property method. + } + } + + // -------------------------------------------- + + @Test + public void propertyMethodDoesNotReturnVoid_throwsException() + throws ConfigurationBeanLoaderException { + model.add(typeStatement(GENERIC_INSTANCE_URI, + toJavaUri(PropertyMethodMustReturnVoid.class))); + + expectSimpleFailure( + PropertyMethodMustReturnVoid.class, + throwable(ConfigurationBeanLoaderException.class, + "Failed to load"), + throwable(InstanceWrapperException.class, "should return void")); + } + + public static class PropertyMethodMustReturnVoid { + @Property(uri = GENERIC_PROPERTY_URI) + public String methodReturnIsNotVoid(String s) { + // Not suitable as a property method. + return s; + } + } + + // -------------------------------------------- + + @Test + public void propertyMethodNotAccessible_throwsException() + throws ConfigurationBeanLoaderException { + model.add(typeStatement(GENERIC_INSTANCE_URI, + toJavaUri(PropertyMethodIsPrivate.class))); + model.add(dataProperty(GENERIC_INSTANCE_URI, GENERIC_PROPERTY_URI, + "can't store in a private method.")); + + expectSimpleFailure( + PropertyMethodIsPrivate.class, + throwable(ConfigurationBeanLoaderException.class, + "Failed to load"), + throwable(PropertyTypeException.class, + "Property method failed.")); + } + + public static class PropertyMethodIsPrivate { + @SuppressWarnings("unused") + @Property(uri = GENERIC_PROPERTY_URI) + private void methodReturnIsNotVoid(String s) { + // Not suitable as a property method. + } + } + + // -------------------------------------------- + + @Test + public void propertyMethodThrowsException_throwsException() + throws ConfigurationBeanLoaderException { + model.add(typeStatement(GENERIC_INSTANCE_URI, + toJavaUri(PropertyMethodFails.class))); + model.add(dataProperty(GENERIC_INSTANCE_URI, GENERIC_PROPERTY_URI, + "exception while loading.")); + + expectSimpleFailure( + PropertyMethodFails.class, + throwable(ConfigurationBeanLoaderException.class, + "Failed to load"), + throwable(PropertyTypeException.class, + "Property method failed.")); + } + + public static class PropertyMethodFails { + @SuppressWarnings("unused") + @Property(uri = GENERIC_PROPERTY_URI) + public void methodThrowsException(String s) { + if (true) { + throw new RuntimeException("property method fails."); + } + } + } + + // -------------------------------------------- + + @Test + public void propertyMethodDuplicateUri_throwsException() + throws ConfigurationBeanLoaderException { + model.add(typeStatement(GENERIC_INSTANCE_URI, + toJavaUri(TwoMethodsWithSameUri.class))); + + expectSimpleFailure( + TwoMethodsWithSameUri.class, + throwable(ConfigurationBeanLoaderException.class, + "Failed to load"), + throwable(InstanceWrapperException.class, + "methods have the same URI")); + } + + public static class TwoMethodsWithSameUri { + @SuppressWarnings("unused") + @Property(uri = GENERIC_PROPERTY_URI) + public void firstProperty(String s) { + // Nothing to do + } + + @SuppressWarnings("unused") + @Property(uri = GENERIC_PROPERTY_URI) + public void secondProperty(String s) { + // Nothing to do + } + } + + // -------------------------------------------- + + @Test + public void superclassContainsPropertyAnnotation_success() + throws ConfigurationBeanLoaderException { + model.add(new Statement[] { + typeStatement(GENERIC_INSTANCE_URI, + toJavaUri(EmptyPropertyMethodSubclass.class)), + dataProperty(GENERIC_INSTANCE_URI, GENERIC_PROPERTY_URI, + "Value") }); + + EmptyPropertyMethodSubclass instance = loader.loadInstance( + GENERIC_INSTANCE_URI, EmptyPropertyMethodSubclass.class); + + assertNotNull(instance); + assertEquals("Value", instance.value); + } + + @Test + public void propertyMethodOverridesPropertyMethod_throwsException() + throws ConfigurationBeanLoaderException { + model.add(typeStatement(GENERIC_INSTANCE_URI, + toJavaUri(PropertyMethodOverPropertyMethodSubclass.class))); + + expectSimpleFailure( + PropertyMethodOverPropertyMethodSubclass.class, + throwable(ConfigurationBeanLoaderException.class, + "Failed to load"), + throwable(InstanceWrapperException.class, + "conflicts with a property method")); + } + + @Test + public void plainMethodOverridesPropertyMethod_throwsException() + throws ConfigurationBeanLoaderException { + model.add(typeStatement(GENERIC_INSTANCE_URI, + toJavaUri(PlainOverPropertyMethodSubclass.class))); + + expectSimpleFailure( + PlainOverPropertyMethodSubclass.class, + throwable(ConfigurationBeanLoaderException.class, + "Failed to load"), + throwable(InstanceWrapperException.class, + "conflicts with a property method")); + } + + @Test + public void uriConflictsBetweenSubclassAndSuperclassPropertyMethods_throwsException() + throws ConfigurationBeanLoaderException { + model.add(typeStatement(GENERIC_INSTANCE_URI, + toJavaUri(ConflictingUriPropertyMethodSubclass.class))); + + expectSimpleFailure( + ConflictingUriPropertyMethodSubclass.class, + throwable(ConfigurationBeanLoaderException.class, + "Failed to load"), + throwable(InstanceWrapperException.class, + "Two property methods have the same URI")); + } + + @Test + public void propertyMethodSameNameButDoesNotOverride_throwsException() + throws ConfigurationBeanLoaderException { + model.add(new Statement[] { + typeStatement(GENERIC_INSTANCE_URI, + toJavaUri(DistinctPropertyMethodSubclass.class)), + dataProperty(GENERIC_INSTANCE_URI, GENERIC_PROPERTY_URI, + "Value"), + dataProperty(GENERIC_INSTANCE_URI, OTHER_PROPERTY_URI, + 100.0F) }); + + expectSimpleFailure( + DistinctPropertyMethodSubclass.class, + throwable(ConfigurationBeanLoaderException.class, + "Failed to load"), + throwable(InstanceWrapperException.class, + "conflicts with a property method")); + } + + public static class PropertyMethodSuperclass { + public String value = null; + + @Property(uri = GENERIC_PROPERTY_URI) + public void propertySuper(String v) { + if (value != null) { + throw new RuntimeException("propertySuper has already run."); + } + value = v; + } + } + + public static class EmptyPropertyMethodSubclass extends + PropertyMethodSuperclass { + // Just want to see that the superclass method is run. + } + + public static class DistinctPropertyMethodSubclass extends + PropertyMethodSuperclass { + public float fvalue; + + @Property(uri = OTHER_PROPERTY_URI) + public void propertySuper(Float f) { + if (fvalue != 0.0) { + throw new RuntimeException("propertySub has already run."); + } + fvalue = f; + } + } + + public static class ConflictingUriPropertyMethodSubclass extends + PropertyMethodSuperclass { + + @Property(uri = GENERIC_PROPERTY_URI) + public void propertyConflict(String v) { + // nothing to do. + } + } + + public static class PropertyMethodOverPropertyMethodSubclass extends + EmptyPropertyMethodSubclass { + @Override + @SuppressWarnings("unused") + @Property(uri = GENERIC_PROPERTY_URI) + public void propertySuper(String v) { + // Should fail (two levels down) + } + } + + public static class PlainOverPropertyMethodSubclass extends + PropertyMethodSuperclass { + @SuppressWarnings("unused") + public void propertySuper(Float f) { + // nothing to do + } + } + +} diff --git a/api/src/test/java/edu/cornell/mannlib/vitro/webapp/utils/configuration/ConfigurationBeanLoader_ValidationTest.java b/api/src/test/java/edu/cornell/mannlib/vitro/webapp/utils/configuration/ConfigurationBeanLoader_ValidationTest.java new file mode 100644 index 000000000..8e3498a56 --- /dev/null +++ b/api/src/test/java/edu/cornell/mannlib/vitro/webapp/utils/configuration/ConfigurationBeanLoader_ValidationTest.java @@ -0,0 +1,215 @@ +/* $This file is distributed under the terms of the license in /doc/license.txt$ */ + +package edu.cornell.mannlib.vitro.webapp.utils.configuration; + +import static edu.cornell.mannlib.vitro.testing.ModelUtilitiesTestHelper.typeStatement; +import static edu.cornell.mannlib.vitro.webapp.utils.configuration.ConfigurationBeanLoader.toJavaUri; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import edu.cornell.mannlib.vitro.webapp.utils.configuration.InstanceWrapper.InstanceWrapperException; +import edu.cornell.mannlib.vitro.webapp.utils.configuration.WrappedInstance.ValidationFailedException; + +/** + * Test the @Validation annotation. + */ +public class ConfigurationBeanLoader_ValidationTest extends + ConfigurationBeanLoaderTestBase { + // -------------------------------------------- + + @Test + public void validationMethodHasParameters_throwsException() + throws ConfigurationBeanLoaderException { + model.add(typeStatement(GENERIC_INSTANCE_URI, + toJavaUri(ValidationMethodWithParameter.class))); + + expectSimpleFailure( + ValidationMethodWithParameter.class, + throwable(ConfigurationBeanLoaderException.class, + "Failed to load"), + throwable(InstanceWrapperException.class, + "should not have parameters")); + } + + public static class ValidationMethodWithParameter { + @SuppressWarnings("unused") + @Validation + public void validateWithParameter(String s) { + // Nothing to do + } + } + + // -------------------------------------------- + + @Test + public void validationMethodDoesNotReturnVoid_throwsException() + throws ConfigurationBeanLoaderException { + model.add(typeStatement(GENERIC_INSTANCE_URI, + toJavaUri(ValidationMethodShouldReturnVoid.class))); + + expectSimpleFailure( + ValidationMethodShouldReturnVoid.class, + throwable(ConfigurationBeanLoaderException.class, + "Failed to load"), + throwable(InstanceWrapperException.class, "should return void")); + } + + public static class ValidationMethodShouldReturnVoid { + @Validation + public String validateWithReturnType() { + return "Hi there!"; + } + } + + // -------------------------------------------- + + @Test + public void validationMethodNotAccessible_throwsException() + throws ConfigurationBeanLoaderException { + model.add(typeStatement(GENERIC_INSTANCE_URI, + toJavaUri(ValidationMethodIsPrivate.class))); + + expectSimpleFailure( + ValidationMethodIsPrivate.class, + throwable(ConfigurationBeanLoaderException.class, + "Failed to load"), + throwable(ValidationFailedException.class, + "Error executing validation method")); + } + + public static class ValidationMethodIsPrivate { + @Validation + private void validateIsPrivate() { + // private method + } + } + + // -------------------------------------------- + + @Test + public void validationMethodThrowsException_throwsException() + throws ConfigurationBeanLoaderException { + model.add(typeStatement(GENERIC_INSTANCE_URI, + toJavaUri(ValidationThrowsException.class))); + + expectSimpleFailure( + ValidationThrowsException.class, + throwable(ConfigurationBeanLoaderException.class, + "Failed to load"), + throwable(ValidationFailedException.class, + "Error executing validation method")); + } + + public static class ValidationThrowsException { + @Validation + public void validateFails() { + throw new RuntimeException("from validation method"); + } + } + + // -------------------------------------------- + + @Test + public void superclassContainsValidationMethod_success() + throws ConfigurationBeanLoaderException { + model.add(typeStatement(GENERIC_INSTANCE_URI, + toJavaUri(EmptyValidationSubclass.class))); + + EmptyValidationSubclass instance = loader.loadInstance( + GENERIC_INSTANCE_URI, EmptyValidationSubclass.class); + + assertNotNull(instance); + assertTrue(instance.validatorSuperHasRun); + } + + @Test + public void superclassAndSubclassContainValidationMethods_success() + throws ConfigurationBeanLoaderException { + model.add(typeStatement(GENERIC_INSTANCE_URI, + toJavaUri(AdditionalValidationSubclass.class))); + + AdditionalValidationSubclass instance = loader.loadInstance( + GENERIC_INSTANCE_URI, AdditionalValidationSubclass.class); + + assertNotNull(instance); + assertTrue(instance.validatorSuperHasRun); + assertTrue(instance.validatorSubHasRun); + } + + @Test + public void validationMethodOverridesValidationMethod_throwsException() + throws ConfigurationBeanLoaderException { + model.add(typeStatement(GENERIC_INSTANCE_URI, + toJavaUri(ValidationOverValidationSubclass.class))); + + expectSimpleFailure( + ValidationOverValidationSubclass.class, + throwable(ConfigurationBeanLoaderException.class, + "Failed to load"), + throwable(InstanceWrapperException.class, + "overrides a validation method")); + } + + @Test + public void plainMethodOverridesValidationMethod_throwsException() + throws ConfigurationBeanLoaderException { + model.add(typeStatement(GENERIC_INSTANCE_URI, + toJavaUri(PlainOverValidationSubclass.class))); + + expectSimpleFailure( + PlainOverValidationSubclass.class, + throwable(ConfigurationBeanLoaderException.class, + "Failed to load"), + throwable(InstanceWrapperException.class, + "overrides a validation method")); + } + + public static class ValidationSuperclass { + public boolean validatorSuperHasRun = false; + + @Validation + public void validatorSuper() { + if (validatorSuperHasRun) { + throw new RuntimeException("validatorSuper has already run."); + } + validatorSuperHasRun = true; + } + } + + public static class EmptyValidationSubclass extends ValidationSuperclass { + // Just want to see that the superclass validation is run. + } + + public static class AdditionalValidationSubclass extends + ValidationSuperclass { + public boolean validatorSubHasRun = false; + + @Validation + public void validatorSub() { + if (validatorSubHasRun) { + throw new RuntimeException("validatorSub has already run."); + } + validatorSubHasRun = true; + } + } + + public static class ValidationOverValidationSubclass extends + EmptyValidationSubclass { + @Override + @Validation + public void validatorSuper() { + // Should fail (two levels down) + } + } + + public static class PlainOverValidationSubclass extends + ValidationSuperclass { + @Override + public void validatorSuper() { + // Should fail + } + } + +} diff --git a/api/src/test/java/edu/cornell/mannlib/vitro/webapp/utils/dataGetter/DataGetterUtilsTest.java b/api/src/test/java/edu/cornell/mannlib/vitro/webapp/utils/dataGetter/DataGetterUtilsTest.java index 2f556b6a0..2fbf225cd 100644 --- a/api/src/test/java/edu/cornell/mannlib/vitro/webapp/utils/dataGetter/DataGetterUtilsTest.java +++ b/api/src/test/java/edu/cornell/mannlib/vitro/webapp/utils/dataGetter/DataGetterUtilsTest.java @@ -5,7 +5,7 @@ import java.io.InputStream; import java.lang.reflect.InvocationTargetException; import java.util.List; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.apache.log4j.Level; import org.junit.Assert; import org.junit.Before; diff --git a/api/src/test/java/edu/cornell/mannlib/vitro/webapp/utils/sparqlrunner/QueryHolderTest.java b/api/src/test/java/edu/cornell/mannlib/vitro/webapp/utils/sparqlrunner/QueryHolderTest.java new file mode 100644 index 000000000..18143d244 --- /dev/null +++ b/api/src/test/java/edu/cornell/mannlib/vitro/webapp/utils/sparqlrunner/QueryHolderTest.java @@ -0,0 +1,155 @@ +/* $This file is distributed under the terms of the license in /doc/license.txt$ */ + +package edu.cornell.mannlib.vitro.webapp.utils.sparqlrunner; + +import static org.junit.Assert.*; + +import org.junit.Test; + +import edu.cornell.mannlib.vitro.testing.AbstractTestClass; + +/** + * Check the functionality of QueryHolder. + */ +public class QueryHolderTest extends AbstractTestClass { + private static final String SOME_URI = "http://some.uri/"; + private static final String SOME_LITERAL = "abracadabra"; + + private static final String FIND_ONCE = "SELECT ?s ?p WHERE { ?s ?p ?bindMe }"; + private static final String URI_BOUND_ONCE = "SELECT ?s ?p WHERE { ?s ?p <" + + SOME_URI + "> }"; + private static final String LITERAL_BOUND_ONCE = "SELECT ?s ?p WHERE { ?s ?p \"" + + SOME_LITERAL + "\" }"; + + private static final String FIND_TWICE = "CONSTRUCT { ?s ?p ?find_twice } WHERE { ?s ?p ?find_twice }"; + private static final String URI_BOUND_TWICE = "CONSTRUCT { ?s ?p <" + + SOME_URI + "> } WHERE { ?s ?p <" + SOME_URI + "> }"; + private static final String LITERAL_BOUND_TWICE = "CONSTRUCT { ?s ?p \"" + + SOME_LITERAL + "\" } WHERE { ?s ?p \"" + SOME_LITERAL + "\" }"; + + private static final String SQUEEZED = "CONSTRUCT {?s ?p ?squeeze} WHERE {?s ?p ?squeeze}"; + private static final String URI_BOUND_SQUEEZED = "CONSTRUCT {?s ?p <" + + SOME_URI + ">} WHERE {?s ?p <" + SOME_URI + ">}"; + private static final String LITERAL_BOUND_SQUEEZED = "CONSTRUCT {?s ?p \"" + + SOME_LITERAL + "\"} WHERE {?s ?p \"" + SOME_LITERAL + "\"}"; + + private static final String FIND_IN_SELECT_CLAUSE = "SELECT ?s ?p ?bindMe WHERE { ?s ?p ?bindMe }"; + private static final String URI_BOUND_IN_SELECT_CLAUSE = "SELECT ?s ?p ?bindMe WHERE { ?s ?p <" + + SOME_URI + "> }"; + + // ---------------------------------------------------------------------- + // The tests + // ---------------------------------------------------------------------- + + @Test + public void hasVariable_findsOne() { + assertTrue(new QueryHolder(FIND_ONCE).hasVariable("bindMe")); + } + + @Test + public void hasVariable_findsTwo() { + assertTrue(new QueryHolder(FIND_TWICE).hasVariable("find_twice")); + } + + @Test + public void hasVariable_doesntFindOne() { + assertFalse(new QueryHolder(FIND_TWICE).hasVariable("notThere")); + } + + @Test + public void hasVariable_notFooledByExtendedName() { + assertFalse(new QueryHolder(FIND_ONCE).hasVariable("bind")); + } + + @Test + public void hasVariable_notFooledByPunctuation() { + assertTrue(new QueryHolder(SQUEEZED).hasVariable("squeeze")); + } + + @Test + public void bindToUri_notFound_noComplaint() { + bindToUri(FIND_ONCE, "notThere", SOME_URI).yields(FIND_ONCE); + } + + @Test + public void bindToUri_findsOne_bindsIt() { + bindToUri(FIND_ONCE, "bindMe", SOME_URI).yields(URI_BOUND_ONCE); + } + + @Test + public void bindToUri_findsTwo_bindsBoth() { + bindToUri(FIND_TWICE, "find_twice", SOME_URI).yields(URI_BOUND_TWICE); + } + + @Test + public void bindToUri_notFooledByExtendedName() { + bindToUri(FIND_ONCE, "bind", SOME_URI).yields(FIND_ONCE); + } + + @Test + public void bindToUri_notFooledByPunctuation() { + bindToUri(SQUEEZED, "squeeze", SOME_URI).yields(URI_BOUND_SQUEEZED); + } + + @Test + public void bindToPlainLiteral_notFound_noComplaint() { + bindToLiteral(FIND_ONCE, "notThere", SOME_LITERAL).yields(FIND_ONCE); + } + + @Test + public void bindToPlainLiteral_findsOne_bindsIt() { + bindToLiteral(FIND_ONCE, "bindMe", SOME_LITERAL) + .yields(LITERAL_BOUND_ONCE); + } + + @Test + public void bindToPlainLiteral_findsTwo_bindsBoth() { + bindToLiteral(FIND_TWICE, "find_twice", SOME_LITERAL) + .yields(LITERAL_BOUND_TWICE); + } + + @Test + public void bindToPlainLiteral_notFooledByExtendedName() { + bindToLiteral(FIND_ONCE, "bind", SOME_LITERAL).yields(FIND_ONCE); + } + + @Test + public void bindToPlainLiteral_notFooledByPunctuation() { + bindToLiteral(SQUEEZED, "squeeze", SOME_LITERAL) + .yields(LITERAL_BOUND_SQUEEZED); + } + + @Test + public void variableInSelectClauseIsLeftAlone() { + bindToUri(FIND_IN_SELECT_CLAUSE, "bindMe", SOME_URI) + .yields(URI_BOUND_IN_SELECT_CLAUSE); + } + + // ---------------------------------------------------------------------- + // Helper methods and classes + // ---------------------------------------------------------------------- + + private Yielder bindToUri(String query, String name, String uri) { + QueryHolder holder = new QueryHolder(query); + QueryHolder bound = holder.bindToUri(name, uri); + return new Yielder(bound.getQueryString()); + } + + private Yielder bindToLiteral(String query, String name, String value) { + QueryHolder holder = new QueryHolder(query); + QueryHolder bound = holder.bindToPlainLiteral(name, value); + return new Yielder(bound.getQueryString()); + } + + private class Yielder { + private final String actual; + + public Yielder(String actual) { + this.actual = actual; + } + + void yields(String expected) { + assertEquals(expected, actual); + } + } +} diff --git a/api/src/test/java/edu/cornell/mannlib/vitro/webapp/utils/sparqlrunner/ResultSetParserTest.java b/api/src/test/java/edu/cornell/mannlib/vitro/webapp/utils/sparqlrunner/ResultSetParserTest.java new file mode 100644 index 000000000..326737682 --- /dev/null +++ b/api/src/test/java/edu/cornell/mannlib/vitro/webapp/utils/sparqlrunner/ResultSetParserTest.java @@ -0,0 +1,331 @@ +/* $This file is distributed under the terms of the license in /doc/license.txt$ */ + +package edu.cornell.mannlib.vitro.webapp.utils.sparqlrunner; + +import static edu.cornell.mannlib.vitro.testing.ModelUtilitiesTestHelper.dataProperty; +import static edu.cornell.mannlib.vitro.testing.ModelUtilitiesTestHelper.model; +import static edu.cornell.mannlib.vitro.testing.ModelUtilitiesTestHelper.objectProperty; +import static org.apache.jena.datatypes.xsd.XSDDatatype.XSDinteger; +import static org.apache.jena.datatypes.xsd.XSDDatatype.XSDlong; +import static org.junit.Assert.assertEquals; + +import org.apache.jena.query.QuerySolution; +import org.apache.jena.query.ResultSet; +import org.apache.jena.rdf.model.Model; +import org.apache.log4j.Level; +import org.junit.Before; +import org.junit.Ignore; +import org.junit.Test; + +import edu.cornell.mannlib.vitro.testing.AbstractTestClass; + +public class ResultSetParserTest extends AbstractTestClass { + + private static final String NAMESPACE = "http://namespace#"; + + private static final String SUBJECT = NAMESPACE + "subject"; + + private static final String PREDICATE_URI = NAMESPACE + "uri"; + private static final String OBJECT_URI = NAMESPACE + "objectUri"; + private static final String OBJECT_URI_DEFAULT = NAMESPACE + + "objectUriDefault"; + + private static final String PREDICATE_ANON = NAMESPACE + "anonymous"; + private static final String OBJECT_ANON_DEFAULT = NAMESPACE + "anonDefault"; + + private static final String PREDICATE_STRING = NAMESPACE + "string"; + private static final String OBJECT_STRING = "objectString"; + private static final String OBJECT_STRING_DEFAULT = "objectStringDefault"; + + private static final String PREDICATE_INT = NAMESPACE + "int"; + private static final Integer OBJECT_INT = 4; + private static final Integer OBJECT_INT_DEFAULT = -1; + + private static final String PREDICATE_LONG = NAMESPACE + "long"; + private static final Long OBJECT_LONG = 888L; + private static final Long OBJECT_LONG_DEFAULT = -333L; + + private static final Object PARSING_FAILURE = "PARSING_FAILURE"; + private static final Object NO_RECORDS_FOUND = "NO_RECORDS_FOUND"; + + private static final String SELECT_QUERY = "" // + + "SELECT ?uri ?anonymous ?string ?int ?long \n" // + + "WHERE { \n" // + + " ?s <" + PREDICATE_URI + "> ?uri . \n" // + + " ?s <" + PREDICATE_ANON + "> ?anon . \n" // + + " ?s <" + PREDICATE_STRING + "> ?string . \n" // + + " ?s <" + PREDICATE_INT + "> ?int . \n" // + + " ?s <" + PREDICATE_LONG + "> ?long . \n" // + + "} \n"; + + private Model model; + + @Before + public void setup() { + setLoggerLevel(ModelSelectQueryContext.class, Level.OFF); + + model = model(objectProperty(SUBJECT, PREDICATE_URI, OBJECT_URI), + objectProperty(SUBJECT, PREDICATE_ANON), + dataProperty(SUBJECT, PREDICATE_STRING, OBJECT_STRING), + dataProperty(SUBJECT, PREDICATE_INT, OBJECT_INT, XSDinteger), + dataProperty(SUBJECT, PREDICATE_LONG, OBJECT_LONG, XSDlong)); + } + + // ---------------------------------------------------------------------- + // The tests + // ---------------------------------------------------------------------- + + @Test + public void errorInParse_yieldsDefaultValue() { + assertParsingResult(PARSING_FAILURE, new BaseParser() { + @Override + Object parseOneLine(QuerySolution solution) { + throw new RuntimeException("I refuse to parse this!"); + } + }); + } + + // ---------------------------------------------------------------------- + + @Test + public void expectedUriFoundUri() { + assertParsingResult(OBJECT_URI, new BaseParser() { + @Override + Object parseOneLine(QuerySolution solution) { + return ifResourcePresent(solution, "uri", OBJECT_URI_DEFAULT); + } + }); + } + + @Test + public void expectedUriFoundNothing_returnsDefault() { + assertParsingResult(OBJECT_URI_DEFAULT, new BaseParser() { + @Override + Object parseOneLine(QuerySolution solution) { + return ifResourcePresent(solution, "nothing", + OBJECT_URI_DEFAULT); + } + }); + } + + /** + * TODO Ignoring because the anonymous node isn't showing up in the + * ResultSet. Why not? Anyway, this behaves like "foundNothing". + */ + @Test + @Ignore + public void expectedUriFoundAnonymous_returnsDefault() { + assertParsingResult(OBJECT_URI_DEFAULT, new BaseParser() { + @Override + Object parseOneLine(QuerySolution solution) { + return ifResourcePresent(solution, "anon", OBJECT_URI_DEFAULT); + } + }); + } + + @Test + public void expectedUriFoundString_returnsDefault() { + assertParsingResult(OBJECT_URI_DEFAULT, new BaseParser() { + @Override + Object parseOneLine(QuerySolution solution) { + return ifResourcePresent(solution, "string", OBJECT_URI_DEFAULT); + } + }); + } + + // ---------------------------------------------------------------------- + + @Test + public void expectedStringFoundString() { + assertParsingResult(OBJECT_STRING, new BaseParser() { + @Override + Object parseOneLine(QuerySolution solution) { + return ifLiteralPresent(solution, "string", + OBJECT_STRING_DEFAULT); + } + }); + } + + @Test + public void expectedStringFoundNothing_returnsDefault() { + assertParsingResult(OBJECT_STRING_DEFAULT, new BaseParser() { + @Override + Object parseOneLine(QuerySolution solution) { + return ifLiteralPresent(solution, "nothing", + OBJECT_STRING_DEFAULT); + } + }); + } + + @Test + public void expectedStringFoundResource_fails() { + assertParsingResult(PARSING_FAILURE, new BaseParser() { + @Override + Object parseOneLine(QuerySolution solution) { + return ifLiteralPresent(solution, "uri", OBJECT_STRING_DEFAULT); + } + }); + } + + @Test + public void expectedStringFoundInt_returnsStringValueOfInt() { + assertParsingResult(OBJECT_INT.toString(), new BaseParser() { + @Override + Object parseOneLine(QuerySolution solution) { + return ifLiteralPresent(solution, "int", OBJECT_STRING_DEFAULT); + } + }); + } + + // ---------------------------------------------------------------------- + + @Test + public void expectedIntFoundInt() { + assertParsingResult(OBJECT_INT, new BaseParser() { + @Override + Object parseOneLine(QuerySolution solution) { + return ifIntPresent(solution, "int", OBJECT_INT_DEFAULT); + } + }); + } + + @Test + public void expectedIntFoundNothing() { + assertParsingResult(OBJECT_INT_DEFAULT, new BaseParser() { + @Override + Object parseOneLine(QuerySolution solution) { + return ifIntPresent(solution, "nothing", OBJECT_INT_DEFAULT); + } + }); + } + + @Test + public void expectedIntFoundResource_fails() { + assertParsingResult(PARSING_FAILURE, new BaseParser() { + @Override + Object parseOneLine(QuerySolution solution) { + return ifIntPresent(solution, "uri", OBJECT_INT_DEFAULT); + } + }); + } + + @Test + public void expectedIntFoundString_fails() { + assertParsingResult(PARSING_FAILURE, new BaseParser() { + @Override + Object parseOneLine(QuerySolution solution) { + return ifIntPresent(solution, "string", OBJECT_INT_DEFAULT); + } + }); + } + + @Test + public void expectedIntFoundLong() { + assertParsingResult(new Integer(OBJECT_LONG.intValue()), + new BaseParser() { + @Override + Object parseOneLine(QuerySolution solution) { + return ifIntPresent(solution, "long", + OBJECT_INT_DEFAULT); + } + }); + } + + // ---------------------------------------------------------------------- + + @Test + public void expectedLongFoundLong() { + assertParsingResult(OBJECT_LONG, new BaseParser() { + @Override + Object parseOneLine(QuerySolution solution) { + return ifLongPresent(solution, "long", OBJECT_LONG_DEFAULT); + } + }); + } + + @Test + public void expectedLongFoundNothing() { + assertParsingResult(OBJECT_LONG_DEFAULT, new BaseParser() { + @Override + Object parseOneLine(QuerySolution solution) { + return ifLongPresent(solution, "nothing", OBJECT_LONG_DEFAULT); + } + }); + } + + @Test + public void expectedLongFoundResource_fails() { + assertParsingResult(PARSING_FAILURE, new BaseParser() { + @Override + Object parseOneLine(QuerySolution solution) { + return ifLongPresent(solution, "uri", OBJECT_LONG_DEFAULT); + } + }); + } + + @Test + public void expectedLongFoundString_fails() { + assertParsingResult(PARSING_FAILURE, new BaseParser() { + @Override + Object parseOneLine(QuerySolution solution) { + return ifLongPresent(solution, "string", OBJECT_LONG_DEFAULT); + } + }); + } + + @Test + public void expectedLongFoundInt() { + assertParsingResult(new Long(OBJECT_INT), new BaseParser() { + @Override + Object parseOneLine(QuerySolution solution) { + return ifLongPresent(solution, "int", OBJECT_LONG_DEFAULT); + } + }); + } + + /** + *
    +	 * ifLongPresent, return value or default.
    +	 *   present, absent
    +	 * 
    + */ + // ---------------------------------------------------------------------- + // Helper methods + // ---------------------------------------------------------------------- + + private void assertParsingResult(Object expected, BaseParser parser) { + Object actual = SparqlQueryRunner + .createSelectQueryContext(model, SELECT_QUERY).execute() + .parse(parser); + assertEquals(expected, actual); + } + + private abstract static class BaseParser extends ResultSetParser { + @Override + protected Object defaultValue() { + return PARSING_FAILURE; + } + + @Override + protected Object parseResults(String queryStr, ResultSet results) { + if (results.hasNext()) { + return parseOneLine(results.next()); + } else { + return NO_RECORDS_FOUND; + } + } + + abstract Object parseOneLine(QuerySolution solution); + } + + // private String dumpSolution(QuerySolution solution) { + // Map fields = new HashMap<>(); + // Iterator names = solution.varNames(); + // while (names.hasNext()) { + // String name = names.next(); + // fields.put(name, solution.get(name)); + // } + // return fields.toString(); + // } + // +} diff --git a/api/src/test/java/edu/cornell/mannlib/vitro/webapp/utils/sparqlrunner/SparqlQueryRunnerTest.java b/api/src/test/java/edu/cornell/mannlib/vitro/webapp/utils/sparqlrunner/SparqlQueryRunnerTest.java new file mode 100644 index 000000000..c77dcfa24 --- /dev/null +++ b/api/src/test/java/edu/cornell/mannlib/vitro/webapp/utils/sparqlrunner/SparqlQueryRunnerTest.java @@ -0,0 +1,170 @@ +/* $This file is distributed under the terms of the license in /doc/license.txt$ */ + +package edu.cornell.mannlib.vitro.webapp.utils.sparqlrunner; + +import static edu.cornell.mannlib.vitro.testing.ModelUtilitiesTestHelper.dataProperty; +import static edu.cornell.mannlib.vitro.testing.ModelUtilitiesTestHelper.model; +import static edu.cornell.mannlib.vitro.webapp.utils.sparqlrunner.SparqlQueryRunner.createConstructQueryContext; +import static edu.cornell.mannlib.vitro.webapp.utils.sparqlrunner.SparqlQueryRunner.createSelectQueryContext; +import static edu.cornell.mannlib.vitro.webapp.utils.sparqlrunner.SparqlQueryRunner.queryHolder; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + +import java.io.ByteArrayOutputStream; +import java.io.UnsupportedEncodingException; +import java.util.Arrays; +import java.util.List; + +import org.apache.jena.atlas.json.JSON; +import org.apache.jena.atlas.json.JsonObject; +import org.apache.jena.rdf.model.Model; +import org.junit.Before; +import org.junit.Test; + +import edu.cornell.mannlib.vitro.testing.AbstractTestClass; +import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFService; +import edu.cornell.mannlib.vitro.webapp.rdfservice.impl.jena.model.RDFServiceModel; + +/** + * Top-level smoke tests for the SparqlQueryRunnerTest. Exercise all of the + * methods that create contexts, and show that we can do the simplest of + * operations against them. + */ +public class SparqlQueryRunnerTest extends AbstractTestClass { + private static final String SUBJECT_URI = "http://namespace/subject_uri"; + private static final String PREDICATE_URI = "http://namespace/predicate_uri"; + private static final String OBJECT_VALUE = "object_value"; + + private static final String SELECT_QUERY = "SELECT ?s ?p ?o WHERE { ?s ?p ?o . }"; + private static final JsonObject EXPECTED_SELECT_RESULTS = JSON.parse("" + + "{ " // + + " 'head' : { " // + + " 'vars' : [ 's', 'p', 'o' ] " // + + " } , " // + + " 'results' : { " // + + " 'bindings' : [ " // + + " { " // + + " 'p' : { " // + + " 'type' : 'uri' , " // + + " 'value' : 'http://namespace/predicate_uri' " // + + " } , " // + + " 'o' : { " // + + " 'type' : 'literal' , " // + + " 'value' : 'object_value' " // + + " } , " // + + " 's' : { " // + + " 'type' : 'uri' , " // + + " 'value' : 'http://namespace/subject_uri' " // + + " } " // + + " } " // + + " ] " // + + " } " // + + "} "); + + private static final String CONSTRUCT_QUERY = "CONSTRUCT { ?s ?p ?o } WHERE { ?s ?p ?o }"; + + private Model model; + private RDFService rdfService; + private ByteArrayOutputStream buffer; + private Model constructed; + + @Before + public void setup() { + model = model(dataProperty(SUBJECT_URI, PREDICATE_URI, OBJECT_VALUE)); + rdfService = new RDFServiceModel(model); + buffer = new ByteArrayOutputStream(); + } + + // ---------------------------------------------------------------------- + // SELECT tests + // ---------------------------------------------------------------------- + + @Test + public void selectQueryAgainstModel() { + createSelectQueryContext(model, SELECT_QUERY).execute().writeToOutput( + buffer); + assertExpectedSelectResults(); + } + + @Test + public void selectQueryHolderAgainstModel() { + createSelectQueryContext(model, queryHolder(SELECT_QUERY)).execute() + .writeToOutput(buffer); + assertExpectedSelectResults(); + } + + @Test + public void selectQueryAgainstRDFService() { + createSelectQueryContext(rdfService, SELECT_QUERY).execute() + .writeToOutput(buffer); + assertExpectedSelectResults(); + } + + @Test + public void selectQueryHolderAgainstRDFService() { + createSelectQueryContext(rdfService, queryHolder(SELECT_QUERY)) + .execute().writeToOutput(buffer); + assertExpectedSelectResults(); + } + + /** + * We've shown that all select contexts work. It should suffice that one of + * them can convert to string fields. + */ + @Test + public void selectToStringFields() { + List objectValues = createSelectQueryContext(model, + SELECT_QUERY).execute().toStringFields("o").flatten(); + assertEquals(Arrays.asList(OBJECT_VALUE), objectValues); + } + + // ---------------------------------------------------------------------- + // CONSTRUCT tests + // ---------------------------------------------------------------------- + + @Test + public void constructQueryAgainstModel() { + constructed = createConstructQueryContext(model, CONSTRUCT_QUERY) + .execute().toModel(); + assertExpectedConstructResults(); + } + + @Test + public void constructQueryHolderAgainstModel() { + constructed = createConstructQueryContext(model, + queryHolder(CONSTRUCT_QUERY)).execute().toModel(); + assertExpectedConstructResults(); + } + + @Test + public void constructQueryAgainstRDFService() { + constructed = createConstructQueryContext(rdfService, CONSTRUCT_QUERY) + .execute().toModel(); + assertExpectedConstructResults(); + } + + @Test + public void constructQueryHolderAgainstRDFService() { + constructed = createConstructQueryContext(rdfService, + queryHolder(CONSTRUCT_QUERY)).execute().toModel(); + assertExpectedConstructResults(); + } + + // ---------------------------------------------------------------------- + // Helper methods + // ---------------------------------------------------------------------- + + private void assertExpectedSelectResults() { + try { + JsonObject actual = JSON.parse(buffer.toString("UTF-8")); + assertEquals(EXPECTED_SELECT_RESULTS, actual); + } catch (UnsupportedEncodingException e) { + fail(e.toString()); + } + } + + private void assertExpectedConstructResults() { + assertEquals(model.listStatements().toSet(), constructed + .listStatements().toSet()); + } +} diff --git a/api/src/test/java/edu/cornell/mannlib/vitro/webapp/utils/sparqlrunner/StringResultsMappingTest.java b/api/src/test/java/edu/cornell/mannlib/vitro/webapp/utils/sparqlrunner/StringResultsMappingTest.java new file mode 100644 index 000000000..0521cbff8 --- /dev/null +++ b/api/src/test/java/edu/cornell/mannlib/vitro/webapp/utils/sparqlrunner/StringResultsMappingTest.java @@ -0,0 +1,140 @@ +/* $This file is distributed under the terms of the license in /doc/license.txt$ */ + +package edu.cornell.mannlib.vitro.webapp.utils.sparqlrunner; + +import static edu.cornell.mannlib.vitro.testing.ModelUtilitiesTestHelper.dataProperty; +import static edu.cornell.mannlib.vitro.testing.ModelUtilitiesTestHelper.model; +import static edu.cornell.mannlib.vitro.webapp.utils.sparqlrunner.SparqlQueryRunner.createSelectQueryContext; +import static org.junit.Assert.assertEquals; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; + +import org.apache.jena.rdf.model.Model; +import org.junit.Before; +import org.junit.Test; + +import edu.cornell.mannlib.vitro.testing.AbstractTestClass; + +public class StringResultsMappingTest extends AbstractTestClass { + private static final String SUBJECT_URI_1 = "http://namespace/subject_uri_1"; + private static final String PREDICATE_URI_1 = "http://namespace/predicate_uri_1"; + private static final String OBJECT_VALUE_1 = "object_value_1"; + private static final String SUBJECT_URI_2 = "http://namespace/subject_uri_2"; + private static final String PREDICATE_URI_2 = "http://namespace/predicate_uri_2"; + private static final String OBJECT_VALUE_2 = "object_value_2"; + + private static final String SELECT_QUERY = "SELECT ?s ?p ?o WHERE { ?s ?p ?o . }"; + + private static final List> LIST_ALL_FIELDS = list( + map(entry("s", SUBJECT_URI_1), entry("p", PREDICATE_URI_1), + entry("o", OBJECT_VALUE_1)), + map(entry("s", SUBJECT_URI_2), entry("p", PREDICATE_URI_2), + entry("o", OBJECT_VALUE_2))); + private static final List> LIST_ONE_FIELD = list( + map(entry("o", OBJECT_VALUE_1)), map(entry("o", OBJECT_VALUE_2))); + + private static final List FLATTEN_ALL_FIELDS = list(SUBJECT_URI_1, + PREDICATE_URI_1, OBJECT_VALUE_1, SUBJECT_URI_2, PREDICATE_URI_2, + OBJECT_VALUE_2); + private static final List FLATTEN_ONE_FIELD = list(OBJECT_VALUE_1, + OBJECT_VALUE_2); + + private Model model; + + @Before + public void setup() { + model = model( + dataProperty(SUBJECT_URI_1, PREDICATE_URI_1, OBJECT_VALUE_1), + dataProperty(SUBJECT_URI_2, PREDICATE_URI_2, OBJECT_VALUE_2)); + } + + @Test + public void checkMapsOfAllFields() { + assertEquivalentUnorderedLists(LIST_ALL_FIELDS, + createSelectQueryContext(model, SELECT_QUERY).execute() + .toStringFields().getListOfMaps()); + } + + @Test + public void checkMapsOfOneField() { + assertEquivalentUnorderedLists(LIST_ONE_FIELD, + createSelectQueryContext(model, SELECT_QUERY).execute() + .toStringFields("o").getListOfMaps()); + } + + @Test + public void checkFlattenAllFields() { + assertEquivalentUnorderedLists(FLATTEN_ALL_FIELDS, + createSelectQueryContext(model, SELECT_QUERY).execute() + .toStringFields().flatten()); + } + + @Test + public void checkFlattenOneField() { + assertEquivalentUnorderedLists(FLATTEN_ONE_FIELD, + createSelectQueryContext(model, SELECT_QUERY).execute() + .toStringFields("o").flatten()); + } + + @Test + public void checkFlattenToSet() { + assertEquals(new HashSet<>(FLATTEN_ONE_FIELD), + createSelectQueryContext(model, SELECT_QUERY).execute() + .toStringFields("o").flattenToSet()); + } + + // ---------------------------------------------------------------------- + // Helper methods + // ---------------------------------------------------------------------- + + private void assertEquivalentUnorderedLists(List list1, List list2) { + Collections.sort(list1, new ArbitraryOrder<>()); + Collections.sort(list2, new ArbitraryOrder<>()); + assertEquals(list1, list2); + } + + private static class ArbitraryOrder implements Comparator { + @Override + public int compare(T t1, T t2) { + return t1.hashCode() - t2.hashCode(); + } + } + + @SafeVarargs + private static List list(T... items) { + List l = new ArrayList<>(); + for (T item : items) { + l.add(item); + } + return l; + } + + private static Map map(Entry... entries) { + Map m = new HashMap<>(); + for (Entry entry : entries) { + m.put(entry.key, entry.value); + } + return m; + } + + private static Entry entry(String key, String value) { + return new Entry(key, value); + } + + private static class Entry { + final String key; + final String value; + + Entry(String key, String value) { + this.key = key; + this.value = value; + } + } + +} diff --git a/api/src/test/java/freemarker/ext/dump/DumpDirectiveTest.java b/api/src/test/java/freemarker/ext/dump/DumpDirectiveTest.java index 54916c659..0c8dcb21f 100644 --- a/api/src/test/java/freemarker/ext/dump/DumpDirectiveTest.java +++ b/api/src/test/java/freemarker/ext/dump/DumpDirectiveTest.java @@ -22,7 +22,7 @@ import java.util.Set; import java.util.SortedMap; import java.util.TreeMap; -import org.apache.commons.lang.time.DateUtils; +import org.apache.commons.lang3.time.DateUtils; import org.apache.log4j.Level; import org.apache.log4j.Logger; import org.junit.Before; diff --git a/api/src/test/java/stubs/com/hp/hpl/jena/rdf/model/LiteralStub.java b/api/src/test/java/stubs/org/apache/jena/rdf/model/LiteralStub.java similarity index 100% rename from api/src/test/java/stubs/com/hp/hpl/jena/rdf/model/LiteralStub.java rename to api/src/test/java/stubs/org/apache/jena/rdf/model/LiteralStub.java diff --git a/api/src/test/java/stubs/com/hp/hpl/jena/rdf/model/ModelMaker/ModelMakerStub.java b/api/src/test/java/stubs/org/apache/jena/rdf/model/ModelMaker/ModelMakerStub.java similarity index 100% rename from api/src/test/java/stubs/com/hp/hpl/jena/rdf/model/ModelMaker/ModelMakerStub.java rename to api/src/test/java/stubs/org/apache/jena/rdf/model/ModelMaker/ModelMakerStub.java diff --git a/dependencies/pom.xml b/dependencies/pom.xml index cb40a949f..3a00a9b8c 100644 --- a/dependencies/pom.xml +++ b/dependencies/pom.xml @@ -7,13 +7,13 @@ org.vivoweb vitro-dependencies - 1.9.0-SNAPSHOT + 1.10.0-SNAPSHOT pom org.vivoweb vitro-project - 1.9.0-SNAPSHOT + 1.10.0-SNAPSHOT .. @@ -38,30 +38,30 @@ cglib 2.2 - + + + + + + @@ -73,22 +73,22 @@ com.mchange c3p0 - 0.9.2-pre4 + 0.9.5.2 com.twelvemonkeys.imageio imageio-jpeg - 3.2.1 + 3.3.1 com.twelvemonkeys.imageio imageio-tiff - 3.2.1 + 3.3.1 com.twelvemonkeys.servlet servlet - 3.2.1 + 3.3.1 commons-dbcp @@ -98,17 +98,28 @@ commons-fileupload commons-fileupload - 1.2.1 + 1.3.2 commons-io commons-io - 2.4 + 2.5 - commons-lang - commons-lang - 2.6 + + org.apache.commons + commons-lang3 + 3.4 + + + commons-collections + commons-collections + 3.2.1 + + + org.apache.commons + commons-csv + 1.4 commons-logging @@ -150,48 +161,89 @@ net.sourceforge.owlapi jfact - 4.0.0 + 5.0.1 + + + + net.sourceforge.owlapi + owlapi-distribution + + + + + + net.sourceforge.owlapi + owlapi-api + 5.0.4 net.sourceforge.owlapi - owlapi-distribution - 4.0.1 + owlapi-apibinding + 5.0.4 + - * - * + net.sourceforge.owlapi + owlapi-rio + + org.semarglproject + semargl-core + 0.6.1 + + + org.semarglproject + semargl-rdfa + 0.6.1 + + + com.github.jsonld-java + jsonld-java + 0.8.3 + + + + com.fasterxml.jackson.core + jackson-core + 2.7.4 + + org.apache.httpcomponents - httpcore - 4.2.4 + fluent-hc + 4.5.2 + + + org.apache.httpcomponents + httpclient + 4.5.2 org.apache.httpcomponents httpmime - 4.2.5 + 4.5.2 org.apache.jena jena-arq - 3.1.0 + 3.1.1 org.apache.jena jena-core - 3.1.0 + 3.1.1 org.apache.jena jena-sdb - 3.1.0 + 3.1.1 org.apache.jena jena-tdb - 3.1.0 + 3.1.1 org.apache.solr @@ -201,13 +253,18 @@ org.directwebremoting dwr - 2.0.M2.8 + 3.0.2-RELEASE org.freemarker freemarker 2.3.23 + + org.glassfish + javax.json + 1.0.4 + org.hibernate jtidy @@ -216,12 +273,17 @@ org.jsoup jsoup - 1.6.1 + 1.10.1 org.owasp.antisamy antisamy - 1.4.4 + 1.5.3 + + + com.google.code.gson + gson + 2.5 @@ -241,41 +303,15 @@ 1.2.1 - - org.vivoweb.dependencies - csv - 1.0 - - - org.vivoweb.dependencies - fedora-client - 1.0 - - - org.vivoweb.dependencies - jai_codec - 1.1.3 - - - org.vivoweb.dependencies - jai_core - 1.1.3 - - - + javax.mail mail 1.4 - - javax.xml - jaxrpc - 1.1 - diff --git a/home/pom.xml b/home/pom.xml index 596eb44f1..0121397b2 100644 --- a/home/pom.xml +++ b/home/pom.xml @@ -7,13 +7,13 @@ org.vivoweb vitro-home - 1.9.0-SNAPSHOT + 1.10.0-SNAPSHOT pom org.vivoweb vitro-project - 1.9.0-SNAPSHOT + 1.10.0-SNAPSHOT .. diff --git a/installer/home/pom.xml b/installer/home/pom.xml index ce88261b2..345563376 100644 --- a/installer/home/pom.xml +++ b/installer/home/pom.xml @@ -7,13 +7,13 @@ org.vivoweb vitro-installer-home - 1.9.0-SNAPSHOT + 1.10.0-SNAPSHOT pom org.vivoweb vitro-installer - 1.9.0-SNAPSHOT + 1.10.0-SNAPSHOT .. diff --git a/installer/pom.xml b/installer/pom.xml index 18ba447a2..b21dd3f9f 100644 --- a/installer/pom.xml +++ b/installer/pom.xml @@ -7,7 +7,7 @@ org.vivoweb vitro-installer - 1.9.0-SNAPSHOT + 1.10.0-SNAPSHOT pom Vitro Installer @@ -21,8 +21,8 @@ maven-compiler-plugin - 1.7 - 1.7 + 1.8 + 1.8 UTF-8 diff --git a/installer/solr/pom.xml b/installer/solr/pom.xml index f70cc91c3..e374a9b0c 100644 --- a/installer/solr/pom.xml +++ b/installer/solr/pom.xml @@ -7,13 +7,13 @@ org.vivoweb vitro-installer-solr - 1.9.0-SNAPSHOT + 1.10.0-SNAPSHOT war org.vivoweb vitro-installer - 1.9.0-SNAPSHOT + 1.10.0-SNAPSHOT .. diff --git a/installer/webapp/pom.xml b/installer/webapp/pom.xml index bd8cf44e9..48a72d194 100644 --- a/installer/webapp/pom.xml +++ b/installer/webapp/pom.xml @@ -7,13 +7,13 @@ org.vivoweb vitro-installer-webapp - 1.9.0-SNAPSHOT + 1.10.0-SNAPSHOT war org.vivoweb vitro-installer - 1.9.0-SNAPSHOT + 1.10.0-SNAPSHOT .. diff --git a/legacy/utilities/buildutils/src/edu/cornell/mannlib/vitro/utilities/containerneutral/CheckContainerNeutrality.java b/legacy/utilities/buildutils/src/edu/cornell/mannlib/vitro/utilities/containerneutral/CheckContainerNeutrality.java index 4b2c4bfa1..cdc517d33 100644 --- a/legacy/utilities/buildutils/src/edu/cornell/mannlib/vitro/utilities/containerneutral/CheckContainerNeutrality.java +++ b/legacy/utilities/buildutils/src/edu/cornell/mannlib/vitro/utilities/containerneutral/CheckContainerNeutrality.java @@ -26,7 +26,7 @@ import javax.xml.xpath.XPathExpression; import javax.xml.xpath.XPathExpressionException; import javax.xml.xpath.XPathFactory; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; diff --git a/legacy/utilities/rdbmigration/src/edu/cornell/mannlib/vivo/utilities/rdbmigration/RdbMigrator.java b/legacy/utilities/rdbmigration/src/edu/cornell/mannlib/vivo/utilities/rdbmigration/RdbMigrator.java index 2381f16dd..b06b5d601 100644 --- a/legacy/utilities/rdbmigration/src/edu/cornell/mannlib/vivo/utilities/rdbmigration/RdbMigrator.java +++ b/legacy/utilities/rdbmigration/src/edu/cornell/mannlib/vivo/utilities/rdbmigration/RdbMigrator.java @@ -19,7 +19,7 @@ import java.util.Arrays; import java.util.List; import java.util.Properties; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.apache.jena.db.DBConnection; import org.apache.jena.db.GraphRDB; diff --git a/legacy/webapp/config/licenser/known_exceptions.txt b/legacy/webapp/config/licenser/known_exceptions.txt index 2bf0a056d..e7cbc989d 100644 --- a/legacy/webapp/config/licenser/known_exceptions.txt +++ b/legacy/webapp/config/licenser/known_exceptions.txt @@ -35,7 +35,7 @@ webapp/web/js/tiny_mce/* webapp/web/js/tiny_mce/**/* # See /doc/3rd-party-licenses.txt for LICENSE file -webapp/web/js/jquery.js +webapp/web/js/jquery-1.12.4.min.js webapp/web/js/jquery-ui/* webapp/web/js/jquery_plugins/* webapp/web/js/jquery.fix.clone.js diff --git a/legacy/webapp/languages/es_GO/i18n/all_es_GO.properties b/legacy/webapp/languages/es_GO/i18n/all_es_GO.properties index a62a3a29d..3741d22d6 100644 --- a/legacy/webapp/languages/es_GO/i18n/all_es_GO.properties +++ b/legacy/webapp/languages/es_GO/i18n/all_es_GO.properties @@ -234,6 +234,7 @@ verbose_turn_off = Apagar resource_uri = URI de recursos individual_not_found = Individual no encontrado +individual_not_found_msg = El individuo no se encontró en el sistema. entity_to_query_for = Este id es el identificador de la entidad para consultar. netid también funciona. menu_ordering = Menú pedidos @@ -423,6 +424,13 @@ run_sdb_setup = Ejecutar la instalación SDB unrecognized_user = Usuario no reconocido no_individual_associated_with_id = Por alguna razón, no hay ninguna persona en VIVO que se asocia con su ID de red. Tal vez usted debería ponerse en contacto con el administrador de VIVO. +page_not_created = página no pudo ser creado +page_not_created_msg = Se ha producido un error al crear la página, por favor, compruebe los registros. +page_not_found = Página no encontrada +page_not_found_msg = La página no se ha encontrado en el sistema. +page_uri_missing = No se especifica la página URI +page_uri_missing_msg = No se pudo generar la página pd no estaba claro en qué página se está solicitando. Una asignación de dirección URL es posible que falte. + # # site admin templates ( /templates/freemarker/body/siteAdmin ) # @@ -518,6 +526,8 @@ class_link = Enlace clase previous = Anterior page_link = enlace de la página next_capitalized = Próximo +download_results = resultados de la transferencia directa +to = a # # shortview templates ( /templates/freemarker/body/partials/shortview ) diff --git a/pom.xml b/pom.xml index d23cfc925..d8de4fed8 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ org.vivoweb vitro-project - 1.9.0-SNAPSHOT + 1.10.0-SNAPSHOT pom Vitro @@ -166,8 +166,8 @@ org.apache.maven.plugins maven-compiler-plugin - 1.7 - 1.7 + 1.8 + 1.8 UTF-8 @@ -248,14 +248,6 @@ - - - vivo-dependencies - VIVO Dependencies - https://raw.github.com/vivo-project/dependencies/master/ - - - ossrh diff --git a/solr/pom.xml b/solr/pom.xml index fd46f3bde..572d48fcd 100644 --- a/solr/pom.xml +++ b/solr/pom.xml @@ -7,13 +7,13 @@ org.vivoweb vitro-solr - 1.9.0-SNAPSHOT + 1.10.0-SNAPSHOT war org.vivoweb vitro-project - 1.9.0-SNAPSHOT + 1.10.0-SNAPSHOT .. diff --git a/webapp/pom.xml b/webapp/pom.xml index 1f54ddedf..b407d9cbf 100644 --- a/webapp/pom.xml +++ b/webapp/pom.xml @@ -7,13 +7,13 @@ org.vivoweb vitro-webapp - 1.9.0-SNAPSHOT + 1.10.0-SNAPSHOT war org.vivoweb vitro-project - 1.9.0-SNAPSHOT + 1.10.0-SNAPSHOT .. @@ -41,7 +41,7 @@ org.vivoweb vitro-api - 1.9.0-SNAPSHOT + 1.10.0-SNAPSHOT diff --git a/webapp/src/main/webapp/WEB-INF/dwr.xml b/webapp/src/main/webapp/WEB-INF/dwr.xml index 21fd9445c..eb238891d 100755 --- a/webapp/src/main/webapp/WEB-INF/dwr.xml +++ b/webapp/src/main/webapp/WEB-INF/dwr.xml @@ -1,5 +1,4 @@ - + @@ -48,6 +47,8 @@ + + diff --git a/webapp/src/main/webapp/WEB-INF/web.xml b/webapp/src/main/webapp/WEB-INF/web.xml index 04ee7bc23..98d7b6180 100644 --- a/webapp/src/main/webapp/WEB-INF/web.xml +++ b/webapp/src/main/webapp/WEB-INF/web.xml @@ -939,7 +939,7 @@ dwr-invoker - uk.ltd.getahead.dwr.DWRServlet + org.directwebremoting.servlet.DwrServlet debug true @@ -1243,6 +1243,16 @@ /selectLocale + + TpfServlet + TpfServlet + org.vivoweb.linkeddatafragments.servlet.VitroLinkedDataFragmentServlet + + + TpfServlet + /tpf/* + + diff --git a/webapp/src/main/webapp/admin/conceptRepair.jsp b/webapp/src/main/webapp/admin/conceptRepair.jsp index 548daa5c1..09c67e7c3 100644 --- a/webapp/src/main/webapp/admin/conceptRepair.jsp +++ b/webapp/src/main/webapp/admin/conceptRepair.jsp @@ -17,7 +17,7 @@ if (conceptIdStr != null) { String describeQueryStr = - "PREFIX afn: \n\n" + + "PREFIX afn: \n\n" + "DESCRIBE ?bnode \n" + "WHERE { \n" + " FILTER(afn:bnode(?bnode) = \"" + conceptIdStr + "\")\n" + diff --git a/webapp/src/main/webapp/admin/removeBadRestrictions.jsp b/webapp/src/main/webapp/admin/removeBadRestrictions.jsp index e0a9a8fe8..355dfe1c7 100644 --- a/webapp/src/main/webapp/admin/removeBadRestrictions.jsp +++ b/webapp/src/main/webapp/admin/removeBadRestrictions.jsp @@ -63,7 +63,7 @@ private Model describeBnode(String bnodeId) { String describeQueryStr = - "PREFIX afn: \n\n" + + "PREFIX afn: \n\n" + "DESCRIBE ?bnode \n" + "WHERE { \n" + " FILTER(afn:bnode(?bnode) = \"" + bnodeId + "\")\n" + diff --git a/webapp/src/main/webapp/config/listViewConfig-default.xml b/webapp/src/main/webapp/config/listViewConfig-default.xml index 7c7327152..3460f6f66 100644 --- a/webapp/src/main/webapp/config/listViewConfig-default.xml +++ b/webapp/src/main/webapp/config/listViewConfig-default.xml @@ -7,7 +7,7 @@ - PREFIX afn: <http://jena.hpl.hp.com/ARQ/function#> + PREFIX afn: <http://jena.apache.org/ARQ/function#> PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#> PREFIX vitro: <http://vitro.mannlib.cornell.edu/ns/vitro/0.7#> diff --git a/webapp/src/main/webapp/config/listViewConfig-hasElement.xml b/webapp/src/main/webapp/config/listViewConfig-hasElement.xml index 04b4ee24e..6ab0c5ab4 100644 --- a/webapp/src/main/webapp/config/listViewConfig-hasElement.xml +++ b/webapp/src/main/webapp/config/listViewConfig-hasElement.xml @@ -8,7 +8,7 @@ PREFIX display: <http://vitro.mannlib.cornell.edu/ontologies/display/1.1#> - PREFIX afn: <http://jena.hpl.hp.com/ARQ/function#> + PREFIX afn: <http://jena.apache.org/ARQ/function#> SELECT ?menuItem (afn:localname(?menuItem) AS ?menuItemName) diff --git a/webapp/src/main/webapp/css/jquery_plugins/qtip/jquery.qtip.min.css b/webapp/src/main/webapp/css/jquery_plugins/qtip/jquery.qtip.min.css new file mode 100644 index 000000000..5adc4b755 --- /dev/null +++ b/webapp/src/main/webapp/css/jquery_plugins/qtip/jquery.qtip.min.css @@ -0,0 +1 @@ +#qtip-overlay.blurs,.qtip-close{cursor:pointer}.qtip{position:absolute;left:-28000px;top:-28000px;display:none;max-width:280px;min-width:50px;font-size:10.5px;line-height:12px;direction:ltr;box-shadow:none;padding:0}.qtip-content,.qtip-titlebar{position:relative;overflow:hidden}.qtip-content{padding:5px 9px;text-align:left;word-wrap:break-word}.qtip-titlebar{padding:5px 35px 5px 10px;border-width:0 0 1px;font-weight:700}.qtip-titlebar+.qtip-content{border-top-width:0!important}.qtip-close{position:absolute;right:-9px;top:-9px;z-index:11;outline:0;border:1px solid transparent}.qtip-titlebar .qtip-close{right:4px;top:50%;margin-top:-9px}* html .qtip-titlebar .qtip-close{top:16px}.qtip-icon .ui-icon,.qtip-titlebar .ui-icon{display:block;text-indent:-1000em;direction:ltr}.qtip-icon,.qtip-icon .ui-icon{-moz-border-radius:3px;-webkit-border-radius:3px;border-radius:3px;text-decoration:none}.qtip-icon .ui-icon{width:18px;height:14px;line-height:14px;text-align:center;text-indent:0;font:normal 700 10px/13px Tahoma,sans-serif;color:inherit;background:-100em -100em no-repeat}.qtip-default{border:1px solid #F1D031;background-color:#FFFFA3;color:#555}.qtip-default .qtip-titlebar{background-color:#FFEF93}.qtip-default .qtip-icon{border-color:#CCC;background:#F1F1F1;color:#777}.qtip-default .qtip-titlebar .qtip-close{border-color:#AAA;color:#111}.qtip-light{background-color:#fff;border-color:#E2E2E2;color:#454545}.qtip-light .qtip-titlebar{background-color:#f1f1f1}.qtip-dark{background-color:#505050;border-color:#303030;color:#f3f3f3}.qtip-dark .qtip-titlebar{background-color:#404040}.qtip-dark .qtip-icon{border-color:#444}.qtip-dark .qtip-titlebar .ui-state-hover{border-color:#303030}.qtip-cream{background-color:#FBF7AA;border-color:#F9E98E;color:#A27D35}.qtip-red,.qtip-red .qtip-icon,.qtip-red .qtip-titlebar .ui-state-hover{border-color:#D95252}.qtip-cream .qtip-titlebar{background-color:#F0DE7D}.qtip-cream .qtip-close .qtip-icon{background-position:-82px 0}.qtip-red{background-color:#F78B83;color:#912323}.qtip-red .qtip-titlebar{background-color:#F06D65}.qtip-red .qtip-close .qtip-icon{background-position:-102px 0}.qtip-green{background-color:#CAED9E;border-color:#90D93F;color:#3F6219}.qtip-green .qtip-titlebar{background-color:#B0DE78}.qtip-green .qtip-close .qtip-icon{background-position:-42px 0}.qtip-blue{background-color:#E5F6FE;border-color:#ADD9ED;color:#5E99BD}.qtip-blue .qtip-titlebar{background-color:#D0E9F5}.qtip-blue .qtip-close .qtip-icon{background-position:-2px 0}.qtip-shadow{-webkit-box-shadow:1px 1px 3px 1px rgba(0,0,0,.15);-moz-box-shadow:1px 1px 3px 1px rgba(0,0,0,.15);box-shadow:1px 1px 3px 1px rgba(0,0,0,.15)}.qtip-bootstrap,.qtip-rounded,.qtip-tipsy{-moz-border-radius:5px;-webkit-border-radius:5px;border-radius:5px}.qtip-rounded .qtip-titlebar{-moz-border-radius:4px 4px 0 0;-webkit-border-radius:4px 4px 0 0;border-radius:4px 4px 0 0}.qtip-youtube{-moz-border-radius:2px;-webkit-border-radius:2px;border-radius:2px;-webkit-box-shadow:0 0 3px #333;-moz-box-shadow:0 0 3px #333;box-shadow:0 0 3px #333;color:#fff;border:0 solid transparent;background:#4A4A4A;background-image:-webkit-gradient(linear,left top,left bottom,color-stop(0,#4A4A4A),color-stop(100%,#000));background-image:-webkit-linear-gradient(top,#4A4A4A 0,#000 100%);background-image:-moz-linear-gradient(top,#4A4A4A 0,#000 100%);background-image:-ms-linear-gradient(top,#4A4A4A 0,#000 100%);background-image:-o-linear-gradient(top,#4A4A4A 0,#000 100%)}.qtip-youtube .qtip-titlebar{background-color:#4A4A4A;background-color:rgba(0,0,0,0)}.qtip-youtube .qtip-content{padding:.75em;font:12px arial,sans-serif;filter:progid:DXImageTransform.Microsoft.Gradient(GradientType=0, StartColorStr=#4a4a4a, EndColorStr=#000000);-ms-filter:"progid:DXImageTransform.Microsoft.Gradient(GradientType=0,StartColorStr=#4a4a4a,EndColorStr=#000000);"}.qtip-youtube .qtip-icon{border-color:#222}.qtip-youtube .qtip-titlebar .ui-state-hover{border-color:#303030}.qtip-jtools{background:#232323;background:rgba(0,0,0,.7);background-image:-webkit-gradient(linear,left top,left bottom,from(#717171),to(#232323));background-image:-moz-linear-gradient(top,#717171,#232323);background-image:-webkit-linear-gradient(top,#717171,#232323);background-image:-ms-linear-gradient(top,#717171,#232323);background-image:-o-linear-gradient(top,#717171,#232323);border:2px solid #ddd;border:2px solid rgba(241,241,241,1);-moz-border-radius:2px;-webkit-border-radius:2px;border-radius:2px;-webkit-box-shadow:0 0 12px #333;-moz-box-shadow:0 0 12px #333;box-shadow:0 0 12px #333}.qtip-jtools .qtip-titlebar{background-color:transparent;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr=#717171, endColorstr=#4A4A4A);-ms-filter:"progid:DXImageTransform.Microsoft.gradient(startColorstr=#717171,endColorstr=#4A4A4A)"}.qtip-jtools .qtip-content{filter:progid:DXImageTransform.Microsoft.gradient(startColorstr=#4A4A4A, endColorstr=#232323);-ms-filter:"progid:DXImageTransform.Microsoft.gradient(startColorstr=#4A4A4A,endColorstr=#232323)"}.qtip-jtools .qtip-content,.qtip-jtools .qtip-titlebar{background:0 0;color:#fff;border:0 dashed transparent}.qtip-jtools .qtip-icon{border-color:#555}.qtip-jtools .qtip-titlebar .ui-state-hover{border-color:#333}.qtip-cluetip{-webkit-box-shadow:4px 4px 5px rgba(0,0,0,.4);-moz-box-shadow:4px 4px 5px rgba(0,0,0,.4);box-shadow:4px 4px 5px rgba(0,0,0,.4);background-color:#D9D9C2;color:#111;border:0 dashed transparent}.qtip-cluetip .qtip-titlebar{background-color:#87876A;color:#fff;border:0 dashed transparent}.qtip-cluetip .qtip-icon{border-color:#808064}.qtip-cluetip .qtip-titlebar .ui-state-hover{border-color:#696952;color:#696952}.qtip-tipsy{background:#000;background:rgba(0,0,0,.87);color:#fff;border:0 solid transparent;font-size:11px;font-family:'Lucida Grande',sans-serif;font-weight:700;line-height:16px;text-shadow:0 1px #000}.qtip-tipsy .qtip-titlebar{padding:6px 35px 0 10px;background-color:transparent}.qtip-tipsy .qtip-content{padding:6px 10px}.qtip-tipsy .qtip-icon{border-color:#222;text-shadow:none}.qtip-tipsy .qtip-titlebar .ui-state-hover{border-color:#303030}.qtip-tipped{border:3px solid #959FA9;-moz-border-radius:3px;-webkit-border-radius:3px;border-radius:3px;background-color:#F9F9F9;color:#454545;font-weight:400;font-family:serif}.qtip-tipped .qtip-titlebar{border-bottom-width:0;color:#fff;background:#3A79B8;background-image:-webkit-gradient(linear,left top,left bottom,from(#3A79B8),to(#2E629D));background-image:-webkit-linear-gradient(top,#3A79B8,#2E629D);background-image:-moz-linear-gradient(top,#3A79B8,#2E629D);background-image:-ms-linear-gradient(top,#3A79B8,#2E629D);background-image:-o-linear-gradient(top,#3A79B8,#2E629D);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr=#3A79B8, endColorstr=#2E629D);-ms-filter:"progid:DXImageTransform.Microsoft.gradient(startColorstr=#3A79B8,endColorstr=#2E629D)"}.qtip-tipped .qtip-icon{border:2px solid #285589;background:#285589}.qtip-tipped .qtip-icon .ui-icon{background-color:#FBFBFB;color:#555}.qtip-bootstrap{font-size:14px;line-height:20px;color:#333;padding:1px;background-color:#fff;border:1px solid #ccc;border:1px solid rgba(0,0,0,.2);-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;-webkit-box-shadow:0 5px 10px rgba(0,0,0,.2);-moz-box-shadow:0 5px 10px rgba(0,0,0,.2);box-shadow:0 5px 10px rgba(0,0,0,.2);-webkit-background-clip:padding-box;-moz-background-clip:padding;background-clip:padding-box}.qtip-bootstrap .qtip-titlebar{padding:8px 14px;margin:0;font-size:14px;font-weight:400;line-height:18px;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;-webkit-border-radius:5px 5px 0 0;-moz-border-radius:5px 5px 0 0;border-radius:5px 5px 0 0}.qtip-bootstrap .qtip-titlebar .qtip-close{right:11px;top:45%;border-style:none}.qtip-bootstrap .qtip-content{padding:9px 14px}.qtip-bootstrap .qtip-icon{background:0 0}.qtip-bootstrap .qtip-icon .ui-icon{width:auto;height:auto;float:right;font-size:20px;font-weight:700;line-height:18px;color:#000;text-shadow:0 1px 0 #fff;opacity:.2;filter:alpha(opacity=20)}#qtip-overlay,#qtip-overlay div{left:0;top:0;width:100%;height:100%}.qtip-bootstrap .qtip-icon .ui-icon:hover{color:#000;text-decoration:none;cursor:pointer;opacity:.4;filter:alpha(opacity=40)}.qtip:not(.ie9haxors) div.qtip-content,.qtip:not(.ie9haxors) div.qtip-titlebar{filter:none;-ms-filter:none}.qtip .qtip-tip{margin:0 auto;overflow:hidden;z-index:10}.qtip .qtip-tip,x:-o-prefocus{visibility:hidden}.qtip .qtip-tip,.qtip .qtip-tip .qtip-vml,.qtip .qtip-tip canvas{position:absolute;color:#123456;background:0 0;border:0 dashed transparent}.qtip .qtip-tip canvas{top:0;left:0}.qtip .qtip-tip .qtip-vml{behavior:url(#default#VML);display:inline-block;visibility:visible}#qtip-overlay{position:fixed}#qtip-overlay div{position:absolute;background-color:#000;opacity:.7;filter:alpha(opacity=70);-ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=70)"}.qtipmodal-ie6fix{position:absolute!important} \ No newline at end of file diff --git a/webapp/src/main/webapp/error.jsp b/webapp/src/main/webapp/error.jsp index 566e53673..fb3368677 100755 --- a/webapp/src/main/webapp/error.jsp +++ b/webapp/src/main/webapp/error.jsp @@ -2,7 +2,7 @@ <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> <%@ page isErrorPage="true" %> -<%@ page import="com.oreilly.servlet.ServletUtils,edu.cornell.mannlib.vitro.webapp.web.*" %> +<%@ page import="edu.cornell.mannlib.vitro.webapp.web.*" %> <%@page import="edu.cornell.mannlib.vitro.webapp.controller.VitroRequest"%> <%@page import="edu.cornell.mannlib.vitro.webapp.beans.ApplicationBean"%> <%@page import="org.apache.commons.logging.Log"%> @@ -46,7 +46,10 @@
    <% try{ %> -

    Trace:

    <%= ServletUtils.getStackTraceAsString(exception) %>
    +

    Trace:

    +
    
    +                        exception.printStackTrace(new java.io.PrintWriter(out));
    +                    
    <% }catch (Exception e){ %> No trace is available. <% } %> diff --git a/webapp/src/main/webapp/fileupload/datastreamModification.jsp b/webapp/src/main/webapp/fileupload/datastreamModification.jsp deleted file mode 100644 index 0a134f8f8..000000000 --- a/webapp/src/main/webapp/fileupload/datastreamModification.jsp +++ /dev/null @@ -1,24 +0,0 @@ -<%-- $This file is distributed under the terms of the license in /doc/license.txt$ --%> - -<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> - -<%/* this is used by the FedoraDatastreamController and not by the N3 editing system.*/%> - -

    Upload a replacement for ${fileName}

    -
    " - enctype="multipart/form-data" method="POST"> - -

    File

    - - <%/*

    - use existing file name

    -

    - rename file to name of file being uploaded

    */%> - - - - - - - -
    diff --git a/webapp/src/main/webapp/fileupload/datastreamModificationSuccess.jsp b/webapp/src/main/webapp/fileupload/datastreamModificationSuccess.jsp deleted file mode 100644 index 663991cbe..000000000 --- a/webapp/src/main/webapp/fileupload/datastreamModificationSuccess.jsp +++ /dev/null @@ -1,16 +0,0 @@ -<%-- $This file is distributed under the terms of the license in /doc/license.txt$ --%> - -<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> - -
    The file ${orginalFileName} was updated. The file received from you had the MD5 checksum ${checksum}.
    - - -
    The name of the file was also changed to ${newFileName}.
    -
    - - - - - - - \ No newline at end of file diff --git a/webapp/src/main/webapp/fileupload/md5.jsp b/webapp/src/main/webapp/fileupload/md5.jsp deleted file mode 100644 index 9ace23a43..000000000 --- a/webapp/src/main/webapp/fileupload/md5.jsp +++ /dev/null @@ -1,11 +0,0 @@ -<%-- $This file is distributed under the terms of the license in /doc/license.txt$ --%> - -<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> - -
    ${checksum} "${fileName}"
    - - - - -
    return to ${name}
    - \ No newline at end of file diff --git a/webapp/src/main/webapp/i18n/all.properties b/webapp/src/main/webapp/i18n/all.properties index 4c0ead6af..a980cc36d 100644 --- a/webapp/src/main/webapp/i18n/all.properties +++ b/webapp/src/main/webapp/i18n/all.properties @@ -246,6 +246,7 @@ verbose_turn_off = Turn off resource_uri = Resource URI individual_not_found = Individual not found +individual_not_found_msg = The individual was not found in the system. entity_to_query_for = This id is the id of the entity to query for. netid also works. menu_ordering = Menu Ordering @@ -435,6 +436,13 @@ run_sdb_setup = Run SDB Setup unrecognized_user = Unrecognized user no_individual_associated_with_id = For some reason, there is no individual in VIVO that is associated with your Net ID. Perhaps you should contact your VIVO administrator. +page_not_created = Page could not be created +page_not_created_msg = There was an error while creating the page, please check the logs. +page_not_found = Page Not Found +page_not_found_msg = The page was not found in the system. +page_uri_missing = No page URI specified +page_uri_missing_msg = Could not generate page beacause it was unclear what page was being requested. A URL mapping may be missing. + # # site admin templates ( /templates/freemarker/body/siteAdmin ) # @@ -531,6 +539,8 @@ class_link = class link previous = Previous page_link = page link next_capitalized = Next +download_results = Download Results +to = to # # shortview templates ( /templates/freemarker/body/partials/shortview ) diff --git a/webapp/src/main/webapp/js/account/accountUtils.js b/webapp/src/main/webapp/js/account/accountUtils.js index b3429d401..c5e79dfab 100644 --- a/webapp/src/main/webapp/js/account/accountUtils.js +++ b/webapp/src/main/webapp/js/account/accountUtils.js @@ -41,16 +41,16 @@ $(document).ready(function(){ $('input:checkbox[name=delete-all]').click(function(){ if ( this.checked ) { // if checked, select all the checkboxes - $('input:checkbox[name=deleteAccount]').attr('checked','checked'); + $('input:checkbox[name=deleteAccount]').prop('checked','checked'); } else { // if not checked, deselect all the checkboxes - $('input:checkbox[name=deleteAccount]').removeAttr('checked'); + $('input:checkbox[name=deleteAccount]').prop('checked', null); } }); $('input:checkbox[name=deleteAccount]').click(function(){ - $('input:checkbox[name=delete-all]').removeAttr('checked'); + $('input:checkbox[name=delete-all]').prop('checked', null); }); // Confirmation alert for account deletion in userAccounts-list.ftl template diff --git a/webapp/src/main/webapp/js/edit/entityRetry.js b/webapp/src/main/webapp/js/edit/entityRetry.js index 3087d9090..a2be916b8 100644 --- a/webapp/src/main/webapp/js/edit/entityRetry.js +++ b/webapp/src/main/webapp/js/edit/entityRetry.js @@ -42,7 +42,7 @@ function monikerInit(){ function update(){ //updates moniker list when type is changed - DWRUtil.useLoadingMessage(); + dwr.util.useLoadingMessage(); EntityDWR.monikers(createList, document.getElementById("VClassURI").value ); @@ -62,7 +62,7 @@ function createList(data) { //puts options in moniker select list var opt = new Option("[new moniker]",""); ele.options[ele.options.length] = opt; - DWRUtil.setValue("Moniker",getCurrentMoniker()); // getCurrentMoniker() is defined on jsp + dwr.util.setValue("Moniker",getCurrentMoniker()); // getCurrentMoniker() is defined on jsp checkMonikers(); } @@ -111,7 +111,7 @@ function fillList(id, data, selectedtext) { for (var i = 0; i < data.length; i++) { - var text = DWRUtil.toDescriptiveString(data[i]); + var text = dwr.util.toDescriptiveString(data[i]); var value = text; diff --git a/webapp/src/main/webapp/js/ents_edit.js b/webapp/src/main/webapp/js/ents_edit.js index 04ef980de..7bed21719 100644 --- a/webapp/src/main/webapp/js/ents_edit.js +++ b/webapp/src/main/webapp/js/ents_edit.js @@ -1,15 +1,15 @@ /* $This file is distributed under the terms of the license in /doc/license.txt$ */ /* this code uses: - dwrutil in util.js + dwr.util in util.js vitro.js detect.js */ dojo.require("dojo.io.*"); dojo.require("dojo.event.*"); -//DWREngine.setErrorHandler(function(data){alert("DWREngine error: " + data);}); -//DWREngine.setWarningHandler(function(data){alert("DWREngine warning: " + data);}); +//dwr.engine.setErrorHandler(function(data){alert("dwr.engine error: " + data);}); +//dwr.engine.setWarningHandler(function(data){alert("dwr.engine warning: " + data);}); /* ents_edit.js has several tasks: @@ -68,7 +68,7 @@ function update( ) { clearProp(); updateTable(); updateEntityAndPropHash(); - var but = $("newPropButton"); + var but = dwr.util.byId("newPropButton"); if( but ) { but.disabled = false; } } @@ -78,11 +78,11 @@ function clearProp() { var clearMap={sunrise:"",sunset: ""}; editingNewProp = false; gVClassUri = null; - DWRUtil.setValues( clearMap ); - clear($("propertyList")); - clear($("vClassList")); + dwr.util.setValues( clearMap ); + clear(dwr.util.byId("propertyList")); + clear(dwr.util.byId("vClassList")); - var ele = $("entitiesList"); + var ele = dwr.util.byId("entitiesList"); while( ele != null && ele.length > 0){ ele.remove(0); } @@ -102,7 +102,7 @@ function updateEntityAndPropHash(){ //once we set the gEntity we can get the ents2ents properties. //PropertyDWR.getAllPropInstByVClass(setPropertyHash, entityObj.VClassURI ); if( entityObj != null && 'URI' in entityObj ) - PropertyDWR.getAllPossiblePropInstForIndividual(setPropertyHash, entityObj.URI); + PropertyDWR.getAllPossiblePropInstForIndividual(entityObj.URI, { callback:setPropertyHash }); //else //alert("could not find an individual, usually this means that there is no type for the URI"); }; @@ -126,7 +126,7 @@ function updateEntityAndPropHash(){ }; //get the gEntity and then build the gPropertyHash - EntityDWR.entityByURI(setGEntity, gEntityURI); + EntityDWR.entityByURI(gEntityURI, { callback:setGEntity }); } /***************************************************************** @@ -139,7 +139,7 @@ function doNoPropsWarning(){ "The button you have clicked adds a new object property statement relating this individual " + "to another individual. If you are looking to add new object properties to the " + "system, look under the 'About' menu."); - var but = $("newPropButton"); + var but = dwr.util.byId("newPropButton"); if( but ) { but.disabled = true; } } @@ -149,18 +149,18 @@ because dwr calls are async we have a callback function updateTable(callback) { var cb = callback; function fillTable(props) { - DWRUtil.removeAllRows("propbody"); + dwr.util.removeAllRows("propbody"); /* This makes the row that gets added to the ents_edit form for each ents2ents object. */ - addRows($("propbody"), props, + addRows(dwr.util.byId("propbody"), props, [getDomain, getProperty, getRange, getEdit, getDelete], makeTr); - var newPropTr = $("justwritenTr"); + var newPropTr = dwr.util.byId("justwritenTr"); if( newPropTr != null ){ Fat.fade_element( "justwritenTr" ); } if(cb !== undefined && cb !== null ) { cb(); } } - PropertyDWR.getExistingProperties(fillTable,gEntityURI); + PropertyDWR.getExistingProperties(gEntityURI, { callback: fillTable }); } /**************************************************************************/ @@ -252,7 +252,7 @@ var makeTr = (function(){ /* outer func */ /* called when Edit button is clicked */ function editTable(inputElement, subjectURI, predicateURI, objectURI){ var rowIndex = inputElement.parentNode.parentNode.rowIndex-1 ; - var table = $("propbody"); + var table = dwr.util.byId("propbody"); table.deleteRow( rowIndex ); table.insertRow(rowIndex); table.rows[rowIndex].insertCell(0); @@ -261,7 +261,7 @@ function editTable(inputElement, subjectURI, predicateURI, objectURI){ table.rows[rowIndex].cells[0].colSpan = tableMaxColspan( table ); vform.style.display="block"; - PropertyDWR.getProperty(fillForm, subjectURI, predicateURI, objectURI); + PropertyDWR.getProperty(subjectURI, predicateURI, objectURI, { callback: fillForm }); inputElement.parentNode.parentNode.style.display="none"; } @@ -270,7 +270,7 @@ function deleteProp( subjectURI, predicateURI, objectURI, objectName, predicateN if( PropertyDWR.deleteProp ){ if (confirm("Are you sure you want to delete the property\n" + objectName + " " + predicateName + "?")) { - PropertyDWR.deleteProp(update, subjectURI, predicateURI, objectURI); + PropertyDWR.deleteProp(subjectURI, predicateURI, objectURI, { callback:update }); } } else { alert("Deletion of object property statements is disabled."); @@ -312,7 +312,7 @@ function newProp() { fillForm( newP ); editingNewProp = true; fillRangeVClassList(); - var table = $("propbody"); + var table = dwr.util.byId("propbody"); var tr = table.insertRow(0); tr.id = "newrow"; appendPropForm( tr, tableMaxColspan( table ) ); @@ -331,7 +331,7 @@ function fillForm(aprop) { gProperty = aprop; var vclass = gProperty.domainClass; - DWRUtil.setValues(gProperty); + dwr.util.setValues(gProperty); toggleDisabled("newPropButton"); @@ -346,7 +346,7 @@ function fillPropList(classId) { /* This function fills up the form's select list with options Notice that the option id is the propertyid + 'D' or 'R' so that domain and range properties can be distinguished */ - var propList = $("propertyList"); + var propList = dwr.util.byId("propertyList"); clear(propList); //add properties as options @@ -383,10 +383,10 @@ function fillPropList(classId) { *****************************************************************/ function fillRangeVClassList( propId ){ //If propId is null then the one on the property select list will be used - if( propId == null ) { propId = DWRUtil.getValue("propertyList");} + if( propId == null ) { propId = dwr.util.getValue("propertyList");} //clear the list and put the loading message up - var vclassListEle = $("vClassList"); + var vclassListEle = dwr.util.byId("vClassList"); clear(vclassListEle); vclassListEle.options[vclassListEle.options.length] = new Option("Loading...",-10); //vclassListEle.options[0].selected = true; @@ -394,11 +394,11 @@ function fillRangeVClassList( propId ){ var prop = gPropertyHash[propId]; - VClassDWR.getVClasses( - function(vclasses){ - addVClassOptions( vclasses ); - }, - prop.domainClassURI, prop.propertyURI, prop.subjectSide); + VClassDWR.getVClasses(prop.domainClassURI, prop.propertyURI, prop.subjectSide, + function(vclasses){ + addVClassOptions( vclasses ); + } + ); } /**************************************************************** @@ -406,14 +406,14 @@ function fillRangeVClassList( propId ){ the entitiesList. ****************************************************************/ function addVClassOptions( vclassArray ){ - // DWRUtil.addOptions("entitiesList",null); - var vclassEle = $("vClassList"); + // dwr.util.addOptions("entitiesList",null); + var vclassEle = dwr.util.byId("vClassList"); clear( vclassEle ); vclassEle.disabled = false; if( vclassArray == null || vclassArray.length < 1){ vclassEle.disabled = true; - entsEle = $("entitiesList"); + entsEle = dwr.util.byId("entitiesList"); clear(entsEle); var msg="There are no entities defined yet that could fill this role"; var opt = new Option(msg,-1); @@ -435,15 +435,15 @@ function addVClassOptions( vclassArray ){ //attempt to set the selected option to the current vclass var vclassURI = null; - var prop = gPropertyHash[ DWRUtil.getValue("propertyList") ]; + var prop = gPropertyHash[ dwr.util.getValue("propertyList") ]; if( gProperty.propertyURI == prop.propertyURI ){ vclassURI = gProperty.rangeClassURI; - DWRUtil.setValue(vclassEle, vclassURI ); + dwr.util.setValue(vclassEle, vclassURI ); //here we were unable to set the vclass select option to the vclassid //of the entity. this means that the vclass of the entity is not one that //is permited by the PropertyInheritance and other restrictions. - if( DWRUtil.getValue(vclassEle) != vclassURI){ + if( dwr.util.getValue(vclassEle) != vclassURI){ alert("This entity's class does not match the class of this property. This is usually the "+ "result of the class of the entity having been changed. Properties that were added when " + "this entity had the old class still reflect that value.\n" + @@ -461,15 +461,15 @@ function addVClassOptions( vclassArray ){ *****************************************************************/ function fillEntsList( vclassEle ){ if( vclassEle == null ) - vclassEle = $("vClassList"); - var vclassUri = DWRUtil.getValue( vclassEle ); + vclassEle = dwr.util.byId("vClassList"); + var vclassUri = dwr.util.getValue( vclassEle ); if( vclassUri == gVClassUri ) return; else gVClassUri = vclassUri; - var entsListEle = $("entitiesList"); + var entsListEle = dwr.util.byId("entitiesList"); clear(entsListEle); entityOptToSelect = null; @@ -494,7 +494,7 @@ function fillEntsList( vclassEle ){ }, load: function(type, data, evt){ //clear here since we will be using addEntOptions for multiple adds -// var entsListEle = $("entitiesList"); +// var entsListEle = dwr.util.byId("entitiesList"); // clear(entsListEle); addEntOptions(data, -1); }, @@ -509,7 +509,7 @@ function fillEntsList( vclassEle ){ /** add entities in entArray as options elements to select element "Individual" */ function addEntOptions( entArray ){ - var entsListEle = $("entitiesList"); + var entsListEle = dwr.util.byId("entitiesList"); if( entArray == null || entArray.length == 0){ clear(entsListEle); @@ -623,12 +623,12 @@ function writeProp() { return; } if( !validateForm() ) { return; } - var prop = gPropertyHash[DWRUtil.getValue("propertyList")]; + var prop = gPropertyHash[dwr.util.getValue("propertyList")]; var newP = {}; var oldP = gProperty; newP.propertyURI = prop.propertyURI; - var selected = DWRUtil.getValue("entitiesList"); + var selected = dwr.util.getValue("entitiesList"); newP.subjectEntURI= gEntity.URI; newP.objectEntURI = selected ; @@ -639,16 +639,17 @@ function writeProp() { if( editingNewProp ){ newP.ents2entsId = -1; - PropertyDWR.insertProp(callback, newP ); + PropertyDWR.insertProp(newP, { callback:callback } ); } else { var afterDelete= function(result){ - PropertyDWR.insertProp(callback, newP); + PropertyDWR.insertProp(newP, { callback:callback }); }; - PropertyDWR.deleteProp(afterDelete, + PropertyDWR.deleteProp( gProperty.subjectEntURI, gProperty.propertyURI, - gProperty.objectEntURI); + gProperty.objectEntURI, + { callback:afterDelete }); } } @@ -657,10 +658,10 @@ addEvent(window, 'load', update); /********************* some utilities ***********************************/ /* this clones the property edit from a div on the ents_edit.jsp */ -function getForm(){ return $("propeditdiv").cloneNode(true); } +function getForm(){ return dwr.util.byId("propeditdiv").cloneNode(true); } /* a function to display a lot of info about an object */ -function disy( obj, note ){ alert( (note!==null?note:"") + DWRUtil.toDescriptiveString(obj, 3));} +function disy( obj, note ){ alert( (note!==null?note:"") + dwr.util.toDescriptiveString(obj, 3));} /* attempts to get the URI of the entity being edited */ function getEntityUriFromPage(){ diff --git a/webapp/src/main/webapp/js/ents_retry.js b/webapp/src/main/webapp/js/ents_retry.js index d0aa8def9..d4cecd0ac 100644 --- a/webapp/src/main/webapp/js/ents_retry.js +++ b/webapp/src/main/webapp/js/ents_retry.js @@ -30,7 +30,7 @@ function init(){ function update(){ //updates moniker list when type is changed - DWRUtil.useLoadingMessage(); + dwr.util.useLoadingMessage(); EntityDWR.monikers(createList, document.getElementById("field2Value").value ); @@ -50,7 +50,7 @@ function createList(data) { //puts options in moniker select list var opt = new Option("[new moniker]",""); ele.options[ele.options.length] = opt; - DWRUtil.setValue("monikerSelect",getCurrentMoniker()); // getCurrentMoniker() is defined on jsp + dwr.util.setValue("monikerSelect",getCurrentMoniker()); // getCurrentMoniker() is defined on jsp checkMonikers(); } @@ -96,7 +96,7 @@ function fillList(id, data, selectedtext) { for (var i = 0; i < data.length; i++) { - var text = DWRUtil.toDescriptiveString(data[i]); + var text = dwr.util.toDescriptiveString(data[i]); var value = text; diff --git a/webapp/src/main/webapp/js/imageUpload/cropImage.js b/webapp/src/main/webapp/js/imageUpload/cropImage.js index 243d71a96..f30a929cf 100644 --- a/webapp/src/main/webapp/js/imageUpload/cropImage.js +++ b/webapp/src/main/webapp/js/imageUpload/cropImage.js @@ -2,7 +2,7 @@ (function($) { - $(window).load(function(){ + $(window).on("load", function(){ var jcrop_api = $.Jcrop('#cropbox',{ /*onChange: showPreview,*/ @@ -35,10 +35,10 @@ marginTop: '-' + Math.round(ry * coords.y) + 'px' }); - $('input[name=x]').val(coords.x); - $('input[name=y]').val(coords.y); - $('input[name=w]').val(coords.w); - $('input[name=h]').val(coords.h); + $('input[name=x]').val(Math.round(coords.x)); + $('input[name=y]').val(Math.round(coords.y)); + $('input[name=w]').val(Math.round(coords.w)); + $('input[name=h]').val(Math.round(coords.h)); } }; diff --git a/webapp/src/main/webapp/js/individual/individualUriRdf.js b/webapp/src/main/webapp/js/individual/individualUriRdf.js index 422d1db88..634e8c3df 100644 --- a/webapp/src/main/webapp/js/individual/individualUriRdf.js +++ b/webapp/src/main/webapp/js/individual/individualUriRdf.js @@ -3,36 +3,31 @@ $(document).ready(function(){ $.extend(this, i18nStringsUriRdf); - + + $('head').append(''); + // This function creates and styles the "qTip" tooltip that displays the resource uri and the rdf link when the user clicks the uri/rdf icon. $('span#iconControlsLeftSide').children('img#uriIcon').each(function() { $(this).qtip( { + prerender: true, // We need this for the .click() event listener on 'a.close' content: { - prerender: true, // We need this for the .click() event listener on 'a.close' text: '
    ' + i18nStringsUriRdf.shareProfileUri + '
    ' + i18nStringsUriRdf.viewRDFProfile + '
    ' + i18nStringsUriRdf.closeString + '' }, position: { - corner: { - target: 'bottomLeft', - tooltip: 'topLeft' - } + my: 'top left', + at: 'bottom left' }, show: { - when: {event: 'click'} + event: 'click' }, hide: { - fixed: true, // Make it fixed so it can be hovered over and interacted with - when: { - target: $('a.close'), - event: 'click' - } + event: 'click' }, style: { - padding: '1em', - width: 400, - backgroundColor: '#f1f2ee' + classes: 'uriIconTip', + width: 400 } }); }); @@ -41,30 +36,23 @@ $(document).ready(function(){ { $(this).qtip( { + prerender: true, // We need this for the .click() event listener on 'a.close' content: { - prerender: true, // We need this for the .click() event listener on 'a.close' text: '
    ' + i18nStringsUriRdf.shareProfileUri + '
    ' + i18nStringsUriRdf.viewRDFProfile + '
    ' + i18nStringsUriRdf.closeString + '' }, position: { - corner: { - target: 'bottomLeft', - tooltip: 'topLeft' - } + my: 'top left', + at: 'bottom left' }, show: { - when: {event: 'click'} + event: 'click' }, hide: { - fixed: true, // Make it fixed so it can be hovered over and interacted with - when: { - target: $('a.close'), - event: 'click' - } + event: 'click' }, style: { - padding: '1em', - width: 400, - backgroundColor: '#f1f2ee' + classes: 'uriIconTip', + width: 400 } }); }); @@ -73,30 +61,23 @@ $(document).ready(function(){ { $(this).qtip( { + prerender: true, // We need this for the .click() event listener on 'a.close' content: { - prerender: true, // We need this for the .click() event listener on 'a.close' text: '
    ' + i18nStringsUriRdf.shareProfileUri + '
    ' + i18nStringsUriRdf.viewRDFProfile + '
    ' + i18nStringsUriRdf.closeString + '' }, position: { - corner: { - target: 'bottomRight', - tooltip: 'topRight' - } + my: 'top right', + at: 'bottom right' }, show: { - when: {event: 'click'} + event: 'click' }, hide: { - fixed: true, // Make it fixed so it can be hovered over and interacted with - when: { - target: $('a.close'), - event: 'click' - } + event: 'click' }, style: { - padding: '1em', - width: 400, - backgroundColor: '#f1f2ee' + classes: 'uriIconTip', + width: 400 } }); }); diff --git a/webapp/src/main/webapp/js/jquery-1.12.4.min.js b/webapp/src/main/webapp/js/jquery-1.12.4.min.js new file mode 100644 index 000000000..e83647587 --- /dev/null +++ b/webapp/src/main/webapp/js/jquery-1.12.4.min.js @@ -0,0 +1,5 @@ +/*! jQuery v1.12.4 | (c) jQuery Foundation | jquery.org/license */ +!function(a,b){"object"==typeof module&&"object"==typeof module.exports?module.exports=a.document?b(a,!0):function(a){if(!a.document)throw new Error("jQuery requires a window with a document");return b(a)}:b(a)}("undefined"!=typeof window?window:this,function(a,b){var c=[],d=a.document,e=c.slice,f=c.concat,g=c.push,h=c.indexOf,i={},j=i.toString,k=i.hasOwnProperty,l={},m="1.12.4",n=function(a,b){return new n.fn.init(a,b)},o=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,p=/^-ms-/,q=/-([\da-z])/gi,r=function(a,b){return b.toUpperCase()};n.fn=n.prototype={jquery:m,constructor:n,selector:"",length:0,toArray:function(){return e.call(this)},get:function(a){return null!=a?0>a?this[a+this.length]:this[a]:e.call(this)},pushStack:function(a){var b=n.merge(this.constructor(),a);return b.prevObject=this,b.context=this.context,b},each:function(a){return n.each(this,a)},map:function(a){return this.pushStack(n.map(this,function(b,c){return a.call(b,c,b)}))},slice:function(){return this.pushStack(e.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(a){var b=this.length,c=+a+(0>a?b:0);return this.pushStack(c>=0&&b>c?[this[c]]:[])},end:function(){return this.prevObject||this.constructor()},push:g,sort:c.sort,splice:c.splice},n.extend=n.fn.extend=function(){var a,b,c,d,e,f,g=arguments[0]||{},h=1,i=arguments.length,j=!1;for("boolean"==typeof g&&(j=g,g=arguments[h]||{},h++),"object"==typeof g||n.isFunction(g)||(g={}),h===i&&(g=this,h--);i>h;h++)if(null!=(e=arguments[h]))for(d in e)a=g[d],c=e[d],g!==c&&(j&&c&&(n.isPlainObject(c)||(b=n.isArray(c)))?(b?(b=!1,f=a&&n.isArray(a)?a:[]):f=a&&n.isPlainObject(a)?a:{},g[d]=n.extend(j,f,c)):void 0!==c&&(g[d]=c));return g},n.extend({expando:"jQuery"+(m+Math.random()).replace(/\D/g,""),isReady:!0,error:function(a){throw new Error(a)},noop:function(){},isFunction:function(a){return"function"===n.type(a)},isArray:Array.isArray||function(a){return"array"===n.type(a)},isWindow:function(a){return null!=a&&a==a.window},isNumeric:function(a){var b=a&&a.toString();return!n.isArray(a)&&b-parseFloat(b)+1>=0},isEmptyObject:function(a){var b;for(b in a)return!1;return!0},isPlainObject:function(a){var b;if(!a||"object"!==n.type(a)||a.nodeType||n.isWindow(a))return!1;try{if(a.constructor&&!k.call(a,"constructor")&&!k.call(a.constructor.prototype,"isPrototypeOf"))return!1}catch(c){return!1}if(!l.ownFirst)for(b in a)return k.call(a,b);for(b in a);return void 0===b||k.call(a,b)},type:function(a){return null==a?a+"":"object"==typeof a||"function"==typeof a?i[j.call(a)]||"object":typeof a},globalEval:function(b){b&&n.trim(b)&&(a.execScript||function(b){a.eval.call(a,b)})(b)},camelCase:function(a){return a.replace(p,"ms-").replace(q,r)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toLowerCase()===b.toLowerCase()},each:function(a,b){var c,d=0;if(s(a)){for(c=a.length;c>d;d++)if(b.call(a[d],d,a[d])===!1)break}else for(d in a)if(b.call(a[d],d,a[d])===!1)break;return a},trim:function(a){return null==a?"":(a+"").replace(o,"")},makeArray:function(a,b){var c=b||[];return null!=a&&(s(Object(a))?n.merge(c,"string"==typeof a?[a]:a):g.call(c,a)),c},inArray:function(a,b,c){var d;if(b){if(h)return h.call(b,a,c);for(d=b.length,c=c?0>c?Math.max(0,d+c):c:0;d>c;c++)if(c in b&&b[c]===a)return c}return-1},merge:function(a,b){var c=+b.length,d=0,e=a.length;while(c>d)a[e++]=b[d++];if(c!==c)while(void 0!==b[d])a[e++]=b[d++];return a.length=e,a},grep:function(a,b,c){for(var d,e=[],f=0,g=a.length,h=!c;g>f;f++)d=!b(a[f],f),d!==h&&e.push(a[f]);return e},map:function(a,b,c){var d,e,g=0,h=[];if(s(a))for(d=a.length;d>g;g++)e=b(a[g],g,c),null!=e&&h.push(e);else for(g in a)e=b(a[g],g,c),null!=e&&h.push(e);return f.apply([],h)},guid:1,proxy:function(a,b){var c,d,f;return"string"==typeof b&&(f=a[b],b=a,a=f),n.isFunction(a)?(c=e.call(arguments,2),d=function(){return a.apply(b||this,c.concat(e.call(arguments)))},d.guid=a.guid=a.guid||n.guid++,d):void 0},now:function(){return+new Date},support:l}),"function"==typeof Symbol&&(n.fn[Symbol.iterator]=c[Symbol.iterator]),n.each("Boolean Number String Function Array Date RegExp Object Error Symbol".split(" "),function(a,b){i["[object "+b+"]"]=b.toLowerCase()});function s(a){var b=!!a&&"length"in a&&a.length,c=n.type(a);return"function"===c||n.isWindow(a)?!1:"array"===c||0===b||"number"==typeof b&&b>0&&b-1 in a}var t=function(a){var b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u="sizzle"+1*new Date,v=a.document,w=0,x=0,y=ga(),z=ga(),A=ga(),B=function(a,b){return a===b&&(l=!0),0},C=1<<31,D={}.hasOwnProperty,E=[],F=E.pop,G=E.push,H=E.push,I=E.slice,J=function(a,b){for(var c=0,d=a.length;d>c;c++)if(a[c]===b)return c;return-1},K="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",L="[\\x20\\t\\r\\n\\f]",M="(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",N="\\["+L+"*("+M+")(?:"+L+"*([*^$|!~]?=)"+L+"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|("+M+"))|)"+L+"*\\]",O=":("+M+")(?:\\((('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|((?:\\\\.|[^\\\\()[\\]]|"+N+")*)|.*)\\)|)",P=new RegExp(L+"+","g"),Q=new RegExp("^"+L+"+|((?:^|[^\\\\])(?:\\\\.)*)"+L+"+$","g"),R=new RegExp("^"+L+"*,"+L+"*"),S=new RegExp("^"+L+"*([>+~]|"+L+")"+L+"*"),T=new RegExp("="+L+"*([^\\]'\"]*?)"+L+"*\\]","g"),U=new RegExp(O),V=new RegExp("^"+M+"$"),W={ID:new RegExp("^#("+M+")"),CLASS:new RegExp("^\\.("+M+")"),TAG:new RegExp("^("+M+"|[*])"),ATTR:new RegExp("^"+N),PSEUDO:new RegExp("^"+O),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+L+"*(even|odd|(([+-]|)(\\d*)n|)"+L+"*(?:([+-]|)"+L+"*(\\d+)|))"+L+"*\\)|)","i"),bool:new RegExp("^(?:"+K+")$","i"),needsContext:new RegExp("^"+L+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+L+"*((?:-\\d)?\\d*)"+L+"*\\)|)(?=[^-]|$)","i")},X=/^(?:input|select|textarea|button)$/i,Y=/^h\d$/i,Z=/^[^{]+\{\s*\[native \w/,$=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,_=/[+~]/,aa=/'|\\/g,ba=new RegExp("\\\\([\\da-f]{1,6}"+L+"?|("+L+")|.)","ig"),ca=function(a,b,c){var d="0x"+b-65536;return d!==d||c?b:0>d?String.fromCharCode(d+65536):String.fromCharCode(d>>10|55296,1023&d|56320)},da=function(){m()};try{H.apply(E=I.call(v.childNodes),v.childNodes),E[v.childNodes.length].nodeType}catch(ea){H={apply:E.length?function(a,b){G.apply(a,I.call(b))}:function(a,b){var c=a.length,d=0;while(a[c++]=b[d++]);a.length=c-1}}}function fa(a,b,d,e){var f,h,j,k,l,o,r,s,w=b&&b.ownerDocument,x=b?b.nodeType:9;if(d=d||[],"string"!=typeof a||!a||1!==x&&9!==x&&11!==x)return d;if(!e&&((b?b.ownerDocument||b:v)!==n&&m(b),b=b||n,p)){if(11!==x&&(o=$.exec(a)))if(f=o[1]){if(9===x){if(!(j=b.getElementById(f)))return d;if(j.id===f)return d.push(j),d}else if(w&&(j=w.getElementById(f))&&t(b,j)&&j.id===f)return d.push(j),d}else{if(o[2])return H.apply(d,b.getElementsByTagName(a)),d;if((f=o[3])&&c.getElementsByClassName&&b.getElementsByClassName)return H.apply(d,b.getElementsByClassName(f)),d}if(c.qsa&&!A[a+" "]&&(!q||!q.test(a))){if(1!==x)w=b,s=a;else if("object"!==b.nodeName.toLowerCase()){(k=b.getAttribute("id"))?k=k.replace(aa,"\\$&"):b.setAttribute("id",k=u),r=g(a),h=r.length,l=V.test(k)?"#"+k:"[id='"+k+"']";while(h--)r[h]=l+" "+qa(r[h]);s=r.join(","),w=_.test(a)&&oa(b.parentNode)||b}if(s)try{return H.apply(d,w.querySelectorAll(s)),d}catch(y){}finally{k===u&&b.removeAttribute("id")}}}return i(a.replace(Q,"$1"),b,d,e)}function ga(){var a=[];function b(c,e){return a.push(c+" ")>d.cacheLength&&delete b[a.shift()],b[c+" "]=e}return b}function ha(a){return a[u]=!0,a}function ia(a){var b=n.createElement("div");try{return!!a(b)}catch(c){return!1}finally{b.parentNode&&b.parentNode.removeChild(b),b=null}}function ja(a,b){var c=a.split("|"),e=c.length;while(e--)d.attrHandle[c[e]]=b}function ka(a,b){var c=b&&a,d=c&&1===a.nodeType&&1===b.nodeType&&(~b.sourceIndex||C)-(~a.sourceIndex||C);if(d)return d;if(c)while(c=c.nextSibling)if(c===b)return-1;return a?1:-1}function la(a){return function(b){var c=b.nodeName.toLowerCase();return"input"===c&&b.type===a}}function ma(a){return function(b){var c=b.nodeName.toLowerCase();return("input"===c||"button"===c)&&b.type===a}}function na(a){return ha(function(b){return b=+b,ha(function(c,d){var e,f=a([],c.length,b),g=f.length;while(g--)c[e=f[g]]&&(c[e]=!(d[e]=c[e]))})})}function oa(a){return a&&"undefined"!=typeof a.getElementsByTagName&&a}c=fa.support={},f=fa.isXML=function(a){var b=a&&(a.ownerDocument||a).documentElement;return b?"HTML"!==b.nodeName:!1},m=fa.setDocument=function(a){var b,e,g=a?a.ownerDocument||a:v;return g!==n&&9===g.nodeType&&g.documentElement?(n=g,o=n.documentElement,p=!f(n),(e=n.defaultView)&&e.top!==e&&(e.addEventListener?e.addEventListener("unload",da,!1):e.attachEvent&&e.attachEvent("onunload",da)),c.attributes=ia(function(a){return a.className="i",!a.getAttribute("className")}),c.getElementsByTagName=ia(function(a){return a.appendChild(n.createComment("")),!a.getElementsByTagName("*").length}),c.getElementsByClassName=Z.test(n.getElementsByClassName),c.getById=ia(function(a){return o.appendChild(a).id=u,!n.getElementsByName||!n.getElementsByName(u).length}),c.getById?(d.find.ID=function(a,b){if("undefined"!=typeof b.getElementById&&p){var c=b.getElementById(a);return c?[c]:[]}},d.filter.ID=function(a){var b=a.replace(ba,ca);return function(a){return a.getAttribute("id")===b}}):(delete d.find.ID,d.filter.ID=function(a){var b=a.replace(ba,ca);return function(a){var c="undefined"!=typeof a.getAttributeNode&&a.getAttributeNode("id");return c&&c.value===b}}),d.find.TAG=c.getElementsByTagName?function(a,b){return"undefined"!=typeof b.getElementsByTagName?b.getElementsByTagName(a):c.qsa?b.querySelectorAll(a):void 0}:function(a,b){var c,d=[],e=0,f=b.getElementsByTagName(a);if("*"===a){while(c=f[e++])1===c.nodeType&&d.push(c);return d}return f},d.find.CLASS=c.getElementsByClassName&&function(a,b){return"undefined"!=typeof b.getElementsByClassName&&p?b.getElementsByClassName(a):void 0},r=[],q=[],(c.qsa=Z.test(n.querySelectorAll))&&(ia(function(a){o.appendChild(a).innerHTML="",a.querySelectorAll("[msallowcapture^='']").length&&q.push("[*^$]="+L+"*(?:''|\"\")"),a.querySelectorAll("[selected]").length||q.push("\\["+L+"*(?:value|"+K+")"),a.querySelectorAll("[id~="+u+"-]").length||q.push("~="),a.querySelectorAll(":checked").length||q.push(":checked"),a.querySelectorAll("a#"+u+"+*").length||q.push(".#.+[+~]")}),ia(function(a){var b=n.createElement("input");b.setAttribute("type","hidden"),a.appendChild(b).setAttribute("name","D"),a.querySelectorAll("[name=d]").length&&q.push("name"+L+"*[*^$|!~]?="),a.querySelectorAll(":enabled").length||q.push(":enabled",":disabled"),a.querySelectorAll("*,:x"),q.push(",.*:")})),(c.matchesSelector=Z.test(s=o.matches||o.webkitMatchesSelector||o.mozMatchesSelector||o.oMatchesSelector||o.msMatchesSelector))&&ia(function(a){c.disconnectedMatch=s.call(a,"div"),s.call(a,"[s!='']:x"),r.push("!=",O)}),q=q.length&&new RegExp(q.join("|")),r=r.length&&new RegExp(r.join("|")),b=Z.test(o.compareDocumentPosition),t=b||Z.test(o.contains)?function(a,b){var c=9===a.nodeType?a.documentElement:a,d=b&&b.parentNode;return a===d||!(!d||1!==d.nodeType||!(c.contains?c.contains(d):a.compareDocumentPosition&&16&a.compareDocumentPosition(d)))}:function(a,b){if(b)while(b=b.parentNode)if(b===a)return!0;return!1},B=b?function(a,b){if(a===b)return l=!0,0;var d=!a.compareDocumentPosition-!b.compareDocumentPosition;return d?d:(d=(a.ownerDocument||a)===(b.ownerDocument||b)?a.compareDocumentPosition(b):1,1&d||!c.sortDetached&&b.compareDocumentPosition(a)===d?a===n||a.ownerDocument===v&&t(v,a)?-1:b===n||b.ownerDocument===v&&t(v,b)?1:k?J(k,a)-J(k,b):0:4&d?-1:1)}:function(a,b){if(a===b)return l=!0,0;var c,d=0,e=a.parentNode,f=b.parentNode,g=[a],h=[b];if(!e||!f)return a===n?-1:b===n?1:e?-1:f?1:k?J(k,a)-J(k,b):0;if(e===f)return ka(a,b);c=a;while(c=c.parentNode)g.unshift(c);c=b;while(c=c.parentNode)h.unshift(c);while(g[d]===h[d])d++;return d?ka(g[d],h[d]):g[d]===v?-1:h[d]===v?1:0},n):n},fa.matches=function(a,b){return fa(a,null,null,b)},fa.matchesSelector=function(a,b){if((a.ownerDocument||a)!==n&&m(a),b=b.replace(T,"='$1']"),c.matchesSelector&&p&&!A[b+" "]&&(!r||!r.test(b))&&(!q||!q.test(b)))try{var d=s.call(a,b);if(d||c.disconnectedMatch||a.document&&11!==a.document.nodeType)return d}catch(e){}return fa(b,n,null,[a]).length>0},fa.contains=function(a,b){return(a.ownerDocument||a)!==n&&m(a),t(a,b)},fa.attr=function(a,b){(a.ownerDocument||a)!==n&&m(a);var e=d.attrHandle[b.toLowerCase()],f=e&&D.call(d.attrHandle,b.toLowerCase())?e(a,b,!p):void 0;return void 0!==f?f:c.attributes||!p?a.getAttribute(b):(f=a.getAttributeNode(b))&&f.specified?f.value:null},fa.error=function(a){throw new Error("Syntax error, unrecognized expression: "+a)},fa.uniqueSort=function(a){var b,d=[],e=0,f=0;if(l=!c.detectDuplicates,k=!c.sortStable&&a.slice(0),a.sort(B),l){while(b=a[f++])b===a[f]&&(e=d.push(f));while(e--)a.splice(d[e],1)}return k=null,a},e=fa.getText=function(a){var b,c="",d=0,f=a.nodeType;if(f){if(1===f||9===f||11===f){if("string"==typeof a.textContent)return a.textContent;for(a=a.firstChild;a;a=a.nextSibling)c+=e(a)}else if(3===f||4===f)return a.nodeValue}else while(b=a[d++])c+=e(b);return c},d=fa.selectors={cacheLength:50,createPseudo:ha,match:W,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(a){return a[1]=a[1].replace(ba,ca),a[3]=(a[3]||a[4]||a[5]||"").replace(ba,ca),"~="===a[2]&&(a[3]=" "+a[3]+" "),a.slice(0,4)},CHILD:function(a){return a[1]=a[1].toLowerCase(),"nth"===a[1].slice(0,3)?(a[3]||fa.error(a[0]),a[4]=+(a[4]?a[5]+(a[6]||1):2*("even"===a[3]||"odd"===a[3])),a[5]=+(a[7]+a[8]||"odd"===a[3])):a[3]&&fa.error(a[0]),a},PSEUDO:function(a){var b,c=!a[6]&&a[2];return W.CHILD.test(a[0])?null:(a[3]?a[2]=a[4]||a[5]||"":c&&U.test(c)&&(b=g(c,!0))&&(b=c.indexOf(")",c.length-b)-c.length)&&(a[0]=a[0].slice(0,b),a[2]=c.slice(0,b)),a.slice(0,3))}},filter:{TAG:function(a){var b=a.replace(ba,ca).toLowerCase();return"*"===a?function(){return!0}:function(a){return a.nodeName&&a.nodeName.toLowerCase()===b}},CLASS:function(a){var b=y[a+" "];return b||(b=new RegExp("(^|"+L+")"+a+"("+L+"|$)"))&&y(a,function(a){return b.test("string"==typeof a.className&&a.className||"undefined"!=typeof a.getAttribute&&a.getAttribute("class")||"")})},ATTR:function(a,b,c){return function(d){var e=fa.attr(d,a);return null==e?"!="===b:b?(e+="","="===b?e===c:"!="===b?e!==c:"^="===b?c&&0===e.indexOf(c):"*="===b?c&&e.indexOf(c)>-1:"$="===b?c&&e.slice(-c.length)===c:"~="===b?(" "+e.replace(P," ")+" ").indexOf(c)>-1:"|="===b?e===c||e.slice(0,c.length+1)===c+"-":!1):!0}},CHILD:function(a,b,c,d,e){var f="nth"!==a.slice(0,3),g="last"!==a.slice(-4),h="of-type"===b;return 1===d&&0===e?function(a){return!!a.parentNode}:function(b,c,i){var j,k,l,m,n,o,p=f!==g?"nextSibling":"previousSibling",q=b.parentNode,r=h&&b.nodeName.toLowerCase(),s=!i&&!h,t=!1;if(q){if(f){while(p){m=b;while(m=m[p])if(h?m.nodeName.toLowerCase()===r:1===m.nodeType)return!1;o=p="only"===a&&!o&&"nextSibling"}return!0}if(o=[g?q.firstChild:q.lastChild],g&&s){m=q,l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),j=k[a]||[],n=j[0]===w&&j[1],t=n&&j[2],m=n&&q.childNodes[n];while(m=++n&&m&&m[p]||(t=n=0)||o.pop())if(1===m.nodeType&&++t&&m===b){k[a]=[w,n,t];break}}else if(s&&(m=b,l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),j=k[a]||[],n=j[0]===w&&j[1],t=n),t===!1)while(m=++n&&m&&m[p]||(t=n=0)||o.pop())if((h?m.nodeName.toLowerCase()===r:1===m.nodeType)&&++t&&(s&&(l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),k[a]=[w,t]),m===b))break;return t-=e,t===d||t%d===0&&t/d>=0}}},PSEUDO:function(a,b){var c,e=d.pseudos[a]||d.setFilters[a.toLowerCase()]||fa.error("unsupported pseudo: "+a);return e[u]?e(b):e.length>1?(c=[a,a,"",b],d.setFilters.hasOwnProperty(a.toLowerCase())?ha(function(a,c){var d,f=e(a,b),g=f.length;while(g--)d=J(a,f[g]),a[d]=!(c[d]=f[g])}):function(a){return e(a,0,c)}):e}},pseudos:{not:ha(function(a){var b=[],c=[],d=h(a.replace(Q,"$1"));return d[u]?ha(function(a,b,c,e){var f,g=d(a,null,e,[]),h=a.length;while(h--)(f=g[h])&&(a[h]=!(b[h]=f))}):function(a,e,f){return b[0]=a,d(b,null,f,c),b[0]=null,!c.pop()}}),has:ha(function(a){return function(b){return fa(a,b).length>0}}),contains:ha(function(a){return a=a.replace(ba,ca),function(b){return(b.textContent||b.innerText||e(b)).indexOf(a)>-1}}),lang:ha(function(a){return V.test(a||"")||fa.error("unsupported lang: "+a),a=a.replace(ba,ca).toLowerCase(),function(b){var c;do if(c=p?b.lang:b.getAttribute("xml:lang")||b.getAttribute("lang"))return c=c.toLowerCase(),c===a||0===c.indexOf(a+"-");while((b=b.parentNode)&&1===b.nodeType);return!1}}),target:function(b){var c=a.location&&a.location.hash;return c&&c.slice(1)===b.id},root:function(a){return a===o},focus:function(a){return a===n.activeElement&&(!n.hasFocus||n.hasFocus())&&!!(a.type||a.href||~a.tabIndex)},enabled:function(a){return a.disabled===!1},disabled:function(a){return a.disabled===!0},checked:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&!!a.checked||"option"===b&&!!a.selected},selected:function(a){return a.parentNode&&a.parentNode.selectedIndex,a.selected===!0},empty:function(a){for(a=a.firstChild;a;a=a.nextSibling)if(a.nodeType<6)return!1;return!0},parent:function(a){return!d.pseudos.empty(a)},header:function(a){return Y.test(a.nodeName)},input:function(a){return X.test(a.nodeName)},button:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&"button"===a.type||"button"===b},text:function(a){var b;return"input"===a.nodeName.toLowerCase()&&"text"===a.type&&(null==(b=a.getAttribute("type"))||"text"===b.toLowerCase())},first:na(function(){return[0]}),last:na(function(a,b){return[b-1]}),eq:na(function(a,b,c){return[0>c?c+b:c]}),even:na(function(a,b){for(var c=0;b>c;c+=2)a.push(c);return a}),odd:na(function(a,b){for(var c=1;b>c;c+=2)a.push(c);return a}),lt:na(function(a,b,c){for(var d=0>c?c+b:c;--d>=0;)a.push(d);return a}),gt:na(function(a,b,c){for(var d=0>c?c+b:c;++db;b++)d+=a[b].value;return d}function ra(a,b,c){var d=b.dir,e=c&&"parentNode"===d,f=x++;return b.first?function(b,c,f){while(b=b[d])if(1===b.nodeType||e)return a(b,c,f)}:function(b,c,g){var h,i,j,k=[w,f];if(g){while(b=b[d])if((1===b.nodeType||e)&&a(b,c,g))return!0}else while(b=b[d])if(1===b.nodeType||e){if(j=b[u]||(b[u]={}),i=j[b.uniqueID]||(j[b.uniqueID]={}),(h=i[d])&&h[0]===w&&h[1]===f)return k[2]=h[2];if(i[d]=k,k[2]=a(b,c,g))return!0}}}function sa(a){return a.length>1?function(b,c,d){var e=a.length;while(e--)if(!a[e](b,c,d))return!1;return!0}:a[0]}function ta(a,b,c){for(var d=0,e=b.length;e>d;d++)fa(a,b[d],c);return c}function ua(a,b,c,d,e){for(var f,g=[],h=0,i=a.length,j=null!=b;i>h;h++)(f=a[h])&&(c&&!c(f,d,e)||(g.push(f),j&&b.push(h)));return g}function va(a,b,c,d,e,f){return d&&!d[u]&&(d=va(d)),e&&!e[u]&&(e=va(e,f)),ha(function(f,g,h,i){var j,k,l,m=[],n=[],o=g.length,p=f||ta(b||"*",h.nodeType?[h]:h,[]),q=!a||!f&&b?p:ua(p,m,a,h,i),r=c?e||(f?a:o||d)?[]:g:q;if(c&&c(q,r,h,i),d){j=ua(r,n),d(j,[],h,i),k=j.length;while(k--)(l=j[k])&&(r[n[k]]=!(q[n[k]]=l))}if(f){if(e||a){if(e){j=[],k=r.length;while(k--)(l=r[k])&&j.push(q[k]=l);e(null,r=[],j,i)}k=r.length;while(k--)(l=r[k])&&(j=e?J(f,l):m[k])>-1&&(f[j]=!(g[j]=l))}}else r=ua(r===g?r.splice(o,r.length):r),e?e(null,g,r,i):H.apply(g,r)})}function wa(a){for(var b,c,e,f=a.length,g=d.relative[a[0].type],h=g||d.relative[" "],i=g?1:0,k=ra(function(a){return a===b},h,!0),l=ra(function(a){return J(b,a)>-1},h,!0),m=[function(a,c,d){var e=!g&&(d||c!==j)||((b=c).nodeType?k(a,c,d):l(a,c,d));return b=null,e}];f>i;i++)if(c=d.relative[a[i].type])m=[ra(sa(m),c)];else{if(c=d.filter[a[i].type].apply(null,a[i].matches),c[u]){for(e=++i;f>e;e++)if(d.relative[a[e].type])break;return va(i>1&&sa(m),i>1&&qa(a.slice(0,i-1).concat({value:" "===a[i-2].type?"*":""})).replace(Q,"$1"),c,e>i&&wa(a.slice(i,e)),f>e&&wa(a=a.slice(e)),f>e&&qa(a))}m.push(c)}return sa(m)}function xa(a,b){var c=b.length>0,e=a.length>0,f=function(f,g,h,i,k){var l,o,q,r=0,s="0",t=f&&[],u=[],v=j,x=f||e&&d.find.TAG("*",k),y=w+=null==v?1:Math.random()||.1,z=x.length;for(k&&(j=g===n||g||k);s!==z&&null!=(l=x[s]);s++){if(e&&l){o=0,g||l.ownerDocument===n||(m(l),h=!p);while(q=a[o++])if(q(l,g||n,h)){i.push(l);break}k&&(w=y)}c&&((l=!q&&l)&&r--,f&&t.push(l))}if(r+=s,c&&s!==r){o=0;while(q=b[o++])q(t,u,g,h);if(f){if(r>0)while(s--)t[s]||u[s]||(u[s]=F.call(i));u=ua(u)}H.apply(i,u),k&&!f&&u.length>0&&r+b.length>1&&fa.uniqueSort(i)}return k&&(w=y,j=v),t};return c?ha(f):f}return h=fa.compile=function(a,b){var c,d=[],e=[],f=A[a+" "];if(!f){b||(b=g(a)),c=b.length;while(c--)f=wa(b[c]),f[u]?d.push(f):e.push(f);f=A(a,xa(e,d)),f.selector=a}return f},i=fa.select=function(a,b,e,f){var i,j,k,l,m,n="function"==typeof a&&a,o=!f&&g(a=n.selector||a);if(e=e||[],1===o.length){if(j=o[0]=o[0].slice(0),j.length>2&&"ID"===(k=j[0]).type&&c.getById&&9===b.nodeType&&p&&d.relative[j[1].type]){if(b=(d.find.ID(k.matches[0].replace(ba,ca),b)||[])[0],!b)return e;n&&(b=b.parentNode),a=a.slice(j.shift().value.length)}i=W.needsContext.test(a)?0:j.length;while(i--){if(k=j[i],d.relative[l=k.type])break;if((m=d.find[l])&&(f=m(k.matches[0].replace(ba,ca),_.test(j[0].type)&&oa(b.parentNode)||b))){if(j.splice(i,1),a=f.length&&qa(j),!a)return H.apply(e,f),e;break}}}return(n||h(a,o))(f,b,!p,e,!b||_.test(a)&&oa(b.parentNode)||b),e},c.sortStable=u.split("").sort(B).join("")===u,c.detectDuplicates=!!l,m(),c.sortDetached=ia(function(a){return 1&a.compareDocumentPosition(n.createElement("div"))}),ia(function(a){return a.innerHTML="","#"===a.firstChild.getAttribute("href")})||ja("type|href|height|width",function(a,b,c){return c?void 0:a.getAttribute(b,"type"===b.toLowerCase()?1:2)}),c.attributes&&ia(function(a){return a.innerHTML="",a.firstChild.setAttribute("value",""),""===a.firstChild.getAttribute("value")})||ja("value",function(a,b,c){return c||"input"!==a.nodeName.toLowerCase()?void 0:a.defaultValue}),ia(function(a){return null==a.getAttribute("disabled")})||ja(K,function(a,b,c){var d;return c?void 0:a[b]===!0?b.toLowerCase():(d=a.getAttributeNode(b))&&d.specified?d.value:null}),fa}(a);n.find=t,n.expr=t.selectors,n.expr[":"]=n.expr.pseudos,n.uniqueSort=n.unique=t.uniqueSort,n.text=t.getText,n.isXMLDoc=t.isXML,n.contains=t.contains;var u=function(a,b,c){var d=[],e=void 0!==c;while((a=a[b])&&9!==a.nodeType)if(1===a.nodeType){if(e&&n(a).is(c))break;d.push(a)}return d},v=function(a,b){for(var c=[];a;a=a.nextSibling)1===a.nodeType&&a!==b&&c.push(a);return c},w=n.expr.match.needsContext,x=/^<([\w-]+)\s*\/?>(?:<\/\1>|)$/,y=/^.[^:#\[\.,]*$/;function z(a,b,c){if(n.isFunction(b))return n.grep(a,function(a,d){return!!b.call(a,d,a)!==c});if(b.nodeType)return n.grep(a,function(a){return a===b!==c});if("string"==typeof b){if(y.test(b))return n.filter(b,a,c);b=n.filter(b,a)}return n.grep(a,function(a){return n.inArray(a,b)>-1!==c})}n.filter=function(a,b,c){var d=b[0];return c&&(a=":not("+a+")"),1===b.length&&1===d.nodeType?n.find.matchesSelector(d,a)?[d]:[]:n.find.matches(a,n.grep(b,function(a){return 1===a.nodeType}))},n.fn.extend({find:function(a){var b,c=[],d=this,e=d.length;if("string"!=typeof a)return this.pushStack(n(a).filter(function(){for(b=0;e>b;b++)if(n.contains(d[b],this))return!0}));for(b=0;e>b;b++)n.find(a,d[b],c);return c=this.pushStack(e>1?n.unique(c):c),c.selector=this.selector?this.selector+" "+a:a,c},filter:function(a){return this.pushStack(z(this,a||[],!1))},not:function(a){return this.pushStack(z(this,a||[],!0))},is:function(a){return!!z(this,"string"==typeof a&&w.test(a)?n(a):a||[],!1).length}});var A,B=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,C=n.fn.init=function(a,b,c){var e,f;if(!a)return this;if(c=c||A,"string"==typeof a){if(e="<"===a.charAt(0)&&">"===a.charAt(a.length-1)&&a.length>=3?[null,a,null]:B.exec(a),!e||!e[1]&&b)return!b||b.jquery?(b||c).find(a):this.constructor(b).find(a);if(e[1]){if(b=b instanceof n?b[0]:b,n.merge(this,n.parseHTML(e[1],b&&b.nodeType?b.ownerDocument||b:d,!0)),x.test(e[1])&&n.isPlainObject(b))for(e in b)n.isFunction(this[e])?this[e](b[e]):this.attr(e,b[e]);return this}if(f=d.getElementById(e[2]),f&&f.parentNode){if(f.id!==e[2])return A.find(a);this.length=1,this[0]=f}return this.context=d,this.selector=a,this}return a.nodeType?(this.context=this[0]=a,this.length=1,this):n.isFunction(a)?"undefined"!=typeof c.ready?c.ready(a):a(n):(void 0!==a.selector&&(this.selector=a.selector,this.context=a.context),n.makeArray(a,this))};C.prototype=n.fn,A=n(d);var D=/^(?:parents|prev(?:Until|All))/,E={children:!0,contents:!0,next:!0,prev:!0};n.fn.extend({has:function(a){var b,c=n(a,this),d=c.length;return this.filter(function(){for(b=0;d>b;b++)if(n.contains(this,c[b]))return!0})},closest:function(a,b){for(var c,d=0,e=this.length,f=[],g=w.test(a)||"string"!=typeof a?n(a,b||this.context):0;e>d;d++)for(c=this[d];c&&c!==b;c=c.parentNode)if(c.nodeType<11&&(g?g.index(c)>-1:1===c.nodeType&&n.find.matchesSelector(c,a))){f.push(c);break}return this.pushStack(f.length>1?n.uniqueSort(f):f)},index:function(a){return a?"string"==typeof a?n.inArray(this[0],n(a)):n.inArray(a.jquery?a[0]:a,this):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(a,b){return this.pushStack(n.uniqueSort(n.merge(this.get(),n(a,b))))},addBack:function(a){return this.add(null==a?this.prevObject:this.prevObject.filter(a))}});function F(a,b){do a=a[b];while(a&&1!==a.nodeType);return a}n.each({parent:function(a){var b=a.parentNode;return b&&11!==b.nodeType?b:null},parents:function(a){return u(a,"parentNode")},parentsUntil:function(a,b,c){return u(a,"parentNode",c)},next:function(a){return F(a,"nextSibling")},prev:function(a){return F(a,"previousSibling")},nextAll:function(a){return u(a,"nextSibling")},prevAll:function(a){return u(a,"previousSibling")},nextUntil:function(a,b,c){return u(a,"nextSibling",c)},prevUntil:function(a,b,c){return u(a,"previousSibling",c)},siblings:function(a){return v((a.parentNode||{}).firstChild,a)},children:function(a){return v(a.firstChild)},contents:function(a){return n.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:n.merge([],a.childNodes)}},function(a,b){n.fn[a]=function(c,d){var e=n.map(this,b,c);return"Until"!==a.slice(-5)&&(d=c),d&&"string"==typeof d&&(e=n.filter(d,e)),this.length>1&&(E[a]||(e=n.uniqueSort(e)),D.test(a)&&(e=e.reverse())),this.pushStack(e)}});var G=/\S+/g;function H(a){var b={};return n.each(a.match(G)||[],function(a,c){b[c]=!0}),b}n.Callbacks=function(a){a="string"==typeof a?H(a):n.extend({},a);var b,c,d,e,f=[],g=[],h=-1,i=function(){for(e=a.once,d=b=!0;g.length;h=-1){c=g.shift();while(++h-1)f.splice(c,1),h>=c&&h--}),this},has:function(a){return a?n.inArray(a,f)>-1:f.length>0},empty:function(){return f&&(f=[]),this},disable:function(){return e=g=[],f=c="",this},disabled:function(){return!f},lock:function(){return e=!0,c||j.disable(),this},locked:function(){return!!e},fireWith:function(a,c){return e||(c=c||[],c=[a,c.slice?c.slice():c],g.push(c),b||i()),this},fire:function(){return j.fireWith(this,arguments),this},fired:function(){return!!d}};return j},n.extend({Deferred:function(a){var b=[["resolve","done",n.Callbacks("once memory"),"resolved"],["reject","fail",n.Callbacks("once memory"),"rejected"],["notify","progress",n.Callbacks("memory")]],c="pending",d={state:function(){return c},always:function(){return e.done(arguments).fail(arguments),this},then:function(){var a=arguments;return n.Deferred(function(c){n.each(b,function(b,f){var g=n.isFunction(a[b])&&a[b];e[f[1]](function(){var a=g&&g.apply(this,arguments);a&&n.isFunction(a.promise)?a.promise().progress(c.notify).done(c.resolve).fail(c.reject):c[f[0]+"With"](this===d?c.promise():this,g?[a]:arguments)})}),a=null}).promise()},promise:function(a){return null!=a?n.extend(a,d):d}},e={};return d.pipe=d.then,n.each(b,function(a,f){var g=f[2],h=f[3];d[f[1]]=g.add,h&&g.add(function(){c=h},b[1^a][2].disable,b[2][2].lock),e[f[0]]=function(){return e[f[0]+"With"](this===e?d:this,arguments),this},e[f[0]+"With"]=g.fireWith}),d.promise(e),a&&a.call(e,e),e},when:function(a){var b=0,c=e.call(arguments),d=c.length,f=1!==d||a&&n.isFunction(a.promise)?d:0,g=1===f?a:n.Deferred(),h=function(a,b,c){return function(d){b[a]=this,c[a]=arguments.length>1?e.call(arguments):d,c===i?g.notifyWith(b,c):--f||g.resolveWith(b,c)}},i,j,k;if(d>1)for(i=new Array(d),j=new Array(d),k=new Array(d);d>b;b++)c[b]&&n.isFunction(c[b].promise)?c[b].promise().progress(h(b,j,i)).done(h(b,k,c)).fail(g.reject):--f;return f||g.resolveWith(k,c),g.promise()}});var I;n.fn.ready=function(a){return n.ready.promise().done(a),this},n.extend({isReady:!1,readyWait:1,holdReady:function(a){a?n.readyWait++:n.ready(!0)},ready:function(a){(a===!0?--n.readyWait:n.isReady)||(n.isReady=!0,a!==!0&&--n.readyWait>0||(I.resolveWith(d,[n]),n.fn.triggerHandler&&(n(d).triggerHandler("ready"),n(d).off("ready"))))}});function J(){d.addEventListener?(d.removeEventListener("DOMContentLoaded",K),a.removeEventListener("load",K)):(d.detachEvent("onreadystatechange",K),a.detachEvent("onload",K))}function K(){(d.addEventListener||"load"===a.event.type||"complete"===d.readyState)&&(J(),n.ready())}n.ready.promise=function(b){if(!I)if(I=n.Deferred(),"complete"===d.readyState||"loading"!==d.readyState&&!d.documentElement.doScroll)a.setTimeout(n.ready);else if(d.addEventListener)d.addEventListener("DOMContentLoaded",K),a.addEventListener("load",K);else{d.attachEvent("onreadystatechange",K),a.attachEvent("onload",K);var c=!1;try{c=null==a.frameElement&&d.documentElement}catch(e){}c&&c.doScroll&&!function f(){if(!n.isReady){try{c.doScroll("left")}catch(b){return a.setTimeout(f,50)}J(),n.ready()}}()}return I.promise(b)},n.ready.promise();var L;for(L in n(l))break;l.ownFirst="0"===L,l.inlineBlockNeedsLayout=!1,n(function(){var a,b,c,e;c=d.getElementsByTagName("body")[0],c&&c.style&&(b=d.createElement("div"),e=d.createElement("div"),e.style.cssText="position:absolute;border:0;width:0;height:0;top:0;left:-9999px",c.appendChild(e).appendChild(b),"undefined"!=typeof b.style.zoom&&(b.style.cssText="display:inline;margin:0;border:0;padding:1px;width:1px;zoom:1",l.inlineBlockNeedsLayout=a=3===b.offsetWidth,a&&(c.style.zoom=1)),c.removeChild(e))}),function(){var a=d.createElement("div");l.deleteExpando=!0;try{delete a.test}catch(b){l.deleteExpando=!1}a=null}();var M=function(a){var b=n.noData[(a.nodeName+" ").toLowerCase()],c=+a.nodeType||1;return 1!==c&&9!==c?!1:!b||b!==!0&&a.getAttribute("classid")===b},N=/^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,O=/([A-Z])/g;function P(a,b,c){if(void 0===c&&1===a.nodeType){var d="data-"+b.replace(O,"-$1").toLowerCase();if(c=a.getAttribute(d),"string"==typeof c){try{c="true"===c?!0:"false"===c?!1:"null"===c?null:+c+""===c?+c:N.test(c)?n.parseJSON(c):c}catch(e){}n.data(a,b,c)}else c=void 0; +}return c}function Q(a){var b;for(b in a)if(("data"!==b||!n.isEmptyObject(a[b]))&&"toJSON"!==b)return!1;return!0}function R(a,b,d,e){if(M(a)){var f,g,h=n.expando,i=a.nodeType,j=i?n.cache:a,k=i?a[h]:a[h]&&h;if(k&&j[k]&&(e||j[k].data)||void 0!==d||"string"!=typeof b)return k||(k=i?a[h]=c.pop()||n.guid++:h),j[k]||(j[k]=i?{}:{toJSON:n.noop}),"object"!=typeof b&&"function"!=typeof b||(e?j[k]=n.extend(j[k],b):j[k].data=n.extend(j[k].data,b)),g=j[k],e||(g.data||(g.data={}),g=g.data),void 0!==d&&(g[n.camelCase(b)]=d),"string"==typeof b?(f=g[b],null==f&&(f=g[n.camelCase(b)])):f=g,f}}function S(a,b,c){if(M(a)){var d,e,f=a.nodeType,g=f?n.cache:a,h=f?a[n.expando]:n.expando;if(g[h]){if(b&&(d=c?g[h]:g[h].data)){n.isArray(b)?b=b.concat(n.map(b,n.camelCase)):b in d?b=[b]:(b=n.camelCase(b),b=b in d?[b]:b.split(" ")),e=b.length;while(e--)delete d[b[e]];if(c?!Q(d):!n.isEmptyObject(d))return}(c||(delete g[h].data,Q(g[h])))&&(f?n.cleanData([a],!0):l.deleteExpando||g!=g.window?delete g[h]:g[h]=void 0)}}}n.extend({cache:{},noData:{"applet ":!0,"embed ":!0,"object ":"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"},hasData:function(a){return a=a.nodeType?n.cache[a[n.expando]]:a[n.expando],!!a&&!Q(a)},data:function(a,b,c){return R(a,b,c)},removeData:function(a,b){return S(a,b)},_data:function(a,b,c){return R(a,b,c,!0)},_removeData:function(a,b){return S(a,b,!0)}}),n.fn.extend({data:function(a,b){var c,d,e,f=this[0],g=f&&f.attributes;if(void 0===a){if(this.length&&(e=n.data(f),1===f.nodeType&&!n._data(f,"parsedAttrs"))){c=g.length;while(c--)g[c]&&(d=g[c].name,0===d.indexOf("data-")&&(d=n.camelCase(d.slice(5)),P(f,d,e[d])));n._data(f,"parsedAttrs",!0)}return e}return"object"==typeof a?this.each(function(){n.data(this,a)}):arguments.length>1?this.each(function(){n.data(this,a,b)}):f?P(f,a,n.data(f,a)):void 0},removeData:function(a){return this.each(function(){n.removeData(this,a)})}}),n.extend({queue:function(a,b,c){var d;return a?(b=(b||"fx")+"queue",d=n._data(a,b),c&&(!d||n.isArray(c)?d=n._data(a,b,n.makeArray(c)):d.push(c)),d||[]):void 0},dequeue:function(a,b){b=b||"fx";var c=n.queue(a,b),d=c.length,e=c.shift(),f=n._queueHooks(a,b),g=function(){n.dequeue(a,b)};"inprogress"===e&&(e=c.shift(),d--),e&&("fx"===b&&c.unshift("inprogress"),delete f.stop,e.call(a,g,f)),!d&&f&&f.empty.fire()},_queueHooks:function(a,b){var c=b+"queueHooks";return n._data(a,c)||n._data(a,c,{empty:n.Callbacks("once memory").add(function(){n._removeData(a,b+"queue"),n._removeData(a,c)})})}}),n.fn.extend({queue:function(a,b){var c=2;return"string"!=typeof a&&(b=a,a="fx",c--),arguments.lengthh;h++)b(a[h],c,g?d:d.call(a[h],h,b(a[h],c)));return e?a:j?b.call(a):i?b(a[0],c):f},Z=/^(?:checkbox|radio)$/i,$=/<([\w:-]+)/,_=/^$|\/(?:java|ecma)script/i,aa=/^\s+/,ba="abbr|article|aside|audio|bdi|canvas|data|datalist|details|dialog|figcaption|figure|footer|header|hgroup|main|mark|meter|nav|output|picture|progress|section|summary|template|time|video";function ca(a){var b=ba.split("|"),c=a.createDocumentFragment();if(c.createElement)while(b.length)c.createElement(b.pop());return c}!function(){var a=d.createElement("div"),b=d.createDocumentFragment(),c=d.createElement("input");a.innerHTML="
    a",l.leadingWhitespace=3===a.firstChild.nodeType,l.tbody=!a.getElementsByTagName("tbody").length,l.htmlSerialize=!!a.getElementsByTagName("link").length,l.html5Clone="<:nav>"!==d.createElement("nav").cloneNode(!0).outerHTML,c.type="checkbox",c.checked=!0,b.appendChild(c),l.appendChecked=c.checked,a.innerHTML="",l.noCloneChecked=!!a.cloneNode(!0).lastChild.defaultValue,b.appendChild(a),c=d.createElement("input"),c.setAttribute("type","radio"),c.setAttribute("checked","checked"),c.setAttribute("name","t"),a.appendChild(c),l.checkClone=a.cloneNode(!0).cloneNode(!0).lastChild.checked,l.noCloneEvent=!!a.addEventListener,a[n.expando]=1,l.attributes=!a.getAttribute(n.expando)}();var da={option:[1,""],legend:[1,"
    ","
    "],area:[1,"",""],param:[1,"",""],thead:[1,"","
    "],tr:[2,"","
    "],col:[2,"","
    "],td:[3,"","
    "],_default:l.htmlSerialize?[0,"",""]:[1,"X
    ","
    "]};da.optgroup=da.option,da.tbody=da.tfoot=da.colgroup=da.caption=da.thead,da.th=da.td;function ea(a,b){var c,d,e=0,f="undefined"!=typeof a.getElementsByTagName?a.getElementsByTagName(b||"*"):"undefined"!=typeof a.querySelectorAll?a.querySelectorAll(b||"*"):void 0;if(!f)for(f=[],c=a.childNodes||a;null!=(d=c[e]);e++)!b||n.nodeName(d,b)?f.push(d):n.merge(f,ea(d,b));return void 0===b||b&&n.nodeName(a,b)?n.merge([a],f):f}function fa(a,b){for(var c,d=0;null!=(c=a[d]);d++)n._data(c,"globalEval",!b||n._data(b[d],"globalEval"))}var ga=/<|&#?\w+;/,ha=/r;r++)if(g=a[r],g||0===g)if("object"===n.type(g))n.merge(q,g.nodeType?[g]:g);else if(ga.test(g)){i=i||p.appendChild(b.createElement("div")),j=($.exec(g)||["",""])[1].toLowerCase(),m=da[j]||da._default,i.innerHTML=m[1]+n.htmlPrefilter(g)+m[2],f=m[0];while(f--)i=i.lastChild;if(!l.leadingWhitespace&&aa.test(g)&&q.push(b.createTextNode(aa.exec(g)[0])),!l.tbody){g="table"!==j||ha.test(g)?""!==m[1]||ha.test(g)?0:i:i.firstChild,f=g&&g.childNodes.length;while(f--)n.nodeName(k=g.childNodes[f],"tbody")&&!k.childNodes.length&&g.removeChild(k)}n.merge(q,i.childNodes),i.textContent="";while(i.firstChild)i.removeChild(i.firstChild);i=p.lastChild}else q.push(b.createTextNode(g));i&&p.removeChild(i),l.appendChecked||n.grep(ea(q,"input"),ia),r=0;while(g=q[r++])if(d&&n.inArray(g,d)>-1)e&&e.push(g);else if(h=n.contains(g.ownerDocument,g),i=ea(p.appendChild(g),"script"),h&&fa(i),c){f=0;while(g=i[f++])_.test(g.type||"")&&c.push(g)}return i=null,p}!function(){var b,c,e=d.createElement("div");for(b in{submit:!0,change:!0,focusin:!0})c="on"+b,(l[b]=c in a)||(e.setAttribute(c,"t"),l[b]=e.attributes[c].expando===!1);e=null}();var ka=/^(?:input|select|textarea)$/i,la=/^key/,ma=/^(?:mouse|pointer|contextmenu|drag|drop)|click/,na=/^(?:focusinfocus|focusoutblur)$/,oa=/^([^.]*)(?:\.(.+)|)/;function pa(){return!0}function qa(){return!1}function ra(){try{return d.activeElement}catch(a){}}function sa(a,b,c,d,e,f){var g,h;if("object"==typeof b){"string"!=typeof c&&(d=d||c,c=void 0);for(h in b)sa(a,h,c,d,b[h],f);return a}if(null==d&&null==e?(e=c,d=c=void 0):null==e&&("string"==typeof c?(e=d,d=void 0):(e=d,d=c,c=void 0)),e===!1)e=qa;else if(!e)return a;return 1===f&&(g=e,e=function(a){return n().off(a),g.apply(this,arguments)},e.guid=g.guid||(g.guid=n.guid++)),a.each(function(){n.event.add(this,b,e,d,c)})}n.event={global:{},add:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,o,p,q,r=n._data(a);if(r){c.handler&&(i=c,c=i.handler,e=i.selector),c.guid||(c.guid=n.guid++),(g=r.events)||(g=r.events={}),(k=r.handle)||(k=r.handle=function(a){return"undefined"==typeof n||a&&n.event.triggered===a.type?void 0:n.event.dispatch.apply(k.elem,arguments)},k.elem=a),b=(b||"").match(G)||[""],h=b.length;while(h--)f=oa.exec(b[h])||[],o=q=f[1],p=(f[2]||"").split(".").sort(),o&&(j=n.event.special[o]||{},o=(e?j.delegateType:j.bindType)||o,j=n.event.special[o]||{},l=n.extend({type:o,origType:q,data:d,handler:c,guid:c.guid,selector:e,needsContext:e&&n.expr.match.needsContext.test(e),namespace:p.join(".")},i),(m=g[o])||(m=g[o]=[],m.delegateCount=0,j.setup&&j.setup.call(a,d,p,k)!==!1||(a.addEventListener?a.addEventListener(o,k,!1):a.attachEvent&&a.attachEvent("on"+o,k))),j.add&&(j.add.call(a,l),l.handler.guid||(l.handler.guid=c.guid)),e?m.splice(m.delegateCount++,0,l):m.push(l),n.event.global[o]=!0);a=null}},remove:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,o,p,q,r=n.hasData(a)&&n._data(a);if(r&&(k=r.events)){b=(b||"").match(G)||[""],j=b.length;while(j--)if(h=oa.exec(b[j])||[],o=q=h[1],p=(h[2]||"").split(".").sort(),o){l=n.event.special[o]||{},o=(d?l.delegateType:l.bindType)||o,m=k[o]||[],h=h[2]&&new RegExp("(^|\\.)"+p.join("\\.(?:.*\\.|)")+"(\\.|$)"),i=f=m.length;while(f--)g=m[f],!e&&q!==g.origType||c&&c.guid!==g.guid||h&&!h.test(g.namespace)||d&&d!==g.selector&&("**"!==d||!g.selector)||(m.splice(f,1),g.selector&&m.delegateCount--,l.remove&&l.remove.call(a,g));i&&!m.length&&(l.teardown&&l.teardown.call(a,p,r.handle)!==!1||n.removeEvent(a,o,r.handle),delete k[o])}else for(o in k)n.event.remove(a,o+b[j],c,d,!0);n.isEmptyObject(k)&&(delete r.handle,n._removeData(a,"events"))}},trigger:function(b,c,e,f){var g,h,i,j,l,m,o,p=[e||d],q=k.call(b,"type")?b.type:b,r=k.call(b,"namespace")?b.namespace.split("."):[];if(i=m=e=e||d,3!==e.nodeType&&8!==e.nodeType&&!na.test(q+n.event.triggered)&&(q.indexOf(".")>-1&&(r=q.split("."),q=r.shift(),r.sort()),h=q.indexOf(":")<0&&"on"+q,b=b[n.expando]?b:new n.Event(q,"object"==typeof b&&b),b.isTrigger=f?2:3,b.namespace=r.join("."),b.rnamespace=b.namespace?new RegExp("(^|\\.)"+r.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,b.result=void 0,b.target||(b.target=e),c=null==c?[b]:n.makeArray(c,[b]),l=n.event.special[q]||{},f||!l.trigger||l.trigger.apply(e,c)!==!1)){if(!f&&!l.noBubble&&!n.isWindow(e)){for(j=l.delegateType||q,na.test(j+q)||(i=i.parentNode);i;i=i.parentNode)p.push(i),m=i;m===(e.ownerDocument||d)&&p.push(m.defaultView||m.parentWindow||a)}o=0;while((i=p[o++])&&!b.isPropagationStopped())b.type=o>1?j:l.bindType||q,g=(n._data(i,"events")||{})[b.type]&&n._data(i,"handle"),g&&g.apply(i,c),g=h&&i[h],g&&g.apply&&M(i)&&(b.result=g.apply(i,c),b.result===!1&&b.preventDefault());if(b.type=q,!f&&!b.isDefaultPrevented()&&(!l._default||l._default.apply(p.pop(),c)===!1)&&M(e)&&h&&e[q]&&!n.isWindow(e)){m=e[h],m&&(e[h]=null),n.event.triggered=q;try{e[q]()}catch(s){}n.event.triggered=void 0,m&&(e[h]=m)}return b.result}},dispatch:function(a){a=n.event.fix(a);var b,c,d,f,g,h=[],i=e.call(arguments),j=(n._data(this,"events")||{})[a.type]||[],k=n.event.special[a.type]||{};if(i[0]=a,a.delegateTarget=this,!k.preDispatch||k.preDispatch.call(this,a)!==!1){h=n.event.handlers.call(this,a,j),b=0;while((f=h[b++])&&!a.isPropagationStopped()){a.currentTarget=f.elem,c=0;while((g=f.handlers[c++])&&!a.isImmediatePropagationStopped())a.rnamespace&&!a.rnamespace.test(g.namespace)||(a.handleObj=g,a.data=g.data,d=((n.event.special[g.origType]||{}).handle||g.handler).apply(f.elem,i),void 0!==d&&(a.result=d)===!1&&(a.preventDefault(),a.stopPropagation()))}return k.postDispatch&&k.postDispatch.call(this,a),a.result}},handlers:function(a,b){var c,d,e,f,g=[],h=b.delegateCount,i=a.target;if(h&&i.nodeType&&("click"!==a.type||isNaN(a.button)||a.button<1))for(;i!=this;i=i.parentNode||this)if(1===i.nodeType&&(i.disabled!==!0||"click"!==a.type)){for(d=[],c=0;h>c;c++)f=b[c],e=f.selector+" ",void 0===d[e]&&(d[e]=f.needsContext?n(e,this).index(i)>-1:n.find(e,this,null,[i]).length),d[e]&&d.push(f);d.length&&g.push({elem:i,handlers:d})}return h]","i"),va=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:-]+)[^>]*)\/>/gi,wa=/\s*$/g,Aa=ca(d),Ba=Aa.appendChild(d.createElement("div"));function Ca(a,b){return n.nodeName(a,"table")&&n.nodeName(11!==b.nodeType?b:b.firstChild,"tr")?a.getElementsByTagName("tbody")[0]||a.appendChild(a.ownerDocument.createElement("tbody")):a}function Da(a){return a.type=(null!==n.find.attr(a,"type"))+"/"+a.type,a}function Ea(a){var b=ya.exec(a.type);return b?a.type=b[1]:a.removeAttribute("type"),a}function Fa(a,b){if(1===b.nodeType&&n.hasData(a)){var c,d,e,f=n._data(a),g=n._data(b,f),h=f.events;if(h){delete g.handle,g.events={};for(c in h)for(d=0,e=h[c].length;e>d;d++)n.event.add(b,c,h[c][d])}g.data&&(g.data=n.extend({},g.data))}}function Ga(a,b){var c,d,e;if(1===b.nodeType){if(c=b.nodeName.toLowerCase(),!l.noCloneEvent&&b[n.expando]){e=n._data(b);for(d in e.events)n.removeEvent(b,d,e.handle);b.removeAttribute(n.expando)}"script"===c&&b.text!==a.text?(Da(b).text=a.text,Ea(b)):"object"===c?(b.parentNode&&(b.outerHTML=a.outerHTML),l.html5Clone&&a.innerHTML&&!n.trim(b.innerHTML)&&(b.innerHTML=a.innerHTML)):"input"===c&&Z.test(a.type)?(b.defaultChecked=b.checked=a.checked,b.value!==a.value&&(b.value=a.value)):"option"===c?b.defaultSelected=b.selected=a.defaultSelected:"input"!==c&&"textarea"!==c||(b.defaultValue=a.defaultValue)}}function Ha(a,b,c,d){b=f.apply([],b);var e,g,h,i,j,k,m=0,o=a.length,p=o-1,q=b[0],r=n.isFunction(q);if(r||o>1&&"string"==typeof q&&!l.checkClone&&xa.test(q))return a.each(function(e){var f=a.eq(e);r&&(b[0]=q.call(this,e,f.html())),Ha(f,b,c,d)});if(o&&(k=ja(b,a[0].ownerDocument,!1,a,d),e=k.firstChild,1===k.childNodes.length&&(k=e),e||d)){for(i=n.map(ea(k,"script"),Da),h=i.length;o>m;m++)g=k,m!==p&&(g=n.clone(g,!0,!0),h&&n.merge(i,ea(g,"script"))),c.call(a[m],g,m);if(h)for(j=i[i.length-1].ownerDocument,n.map(i,Ea),m=0;h>m;m++)g=i[m],_.test(g.type||"")&&!n._data(g,"globalEval")&&n.contains(j,g)&&(g.src?n._evalUrl&&n._evalUrl(g.src):n.globalEval((g.text||g.textContent||g.innerHTML||"").replace(za,"")));k=e=null}return a}function Ia(a,b,c){for(var d,e=b?n.filter(b,a):a,f=0;null!=(d=e[f]);f++)c||1!==d.nodeType||n.cleanData(ea(d)),d.parentNode&&(c&&n.contains(d.ownerDocument,d)&&fa(ea(d,"script")),d.parentNode.removeChild(d));return a}n.extend({htmlPrefilter:function(a){return a.replace(va,"<$1>")},clone:function(a,b,c){var d,e,f,g,h,i=n.contains(a.ownerDocument,a);if(l.html5Clone||n.isXMLDoc(a)||!ua.test("<"+a.nodeName+">")?f=a.cloneNode(!0):(Ba.innerHTML=a.outerHTML,Ba.removeChild(f=Ba.firstChild)),!(l.noCloneEvent&&l.noCloneChecked||1!==a.nodeType&&11!==a.nodeType||n.isXMLDoc(a)))for(d=ea(f),h=ea(a),g=0;null!=(e=h[g]);++g)d[g]&&Ga(e,d[g]);if(b)if(c)for(h=h||ea(a),d=d||ea(f),g=0;null!=(e=h[g]);g++)Fa(e,d[g]);else Fa(a,f);return d=ea(f,"script"),d.length>0&&fa(d,!i&&ea(a,"script")),d=h=e=null,f},cleanData:function(a,b){for(var d,e,f,g,h=0,i=n.expando,j=n.cache,k=l.attributes,m=n.event.special;null!=(d=a[h]);h++)if((b||M(d))&&(f=d[i],g=f&&j[f])){if(g.events)for(e in g.events)m[e]?n.event.remove(d,e):n.removeEvent(d,e,g.handle);j[f]&&(delete j[f],k||"undefined"==typeof d.removeAttribute?d[i]=void 0:d.removeAttribute(i),c.push(f))}}}),n.fn.extend({domManip:Ha,detach:function(a){return Ia(this,a,!0)},remove:function(a){return Ia(this,a)},text:function(a){return Y(this,function(a){return void 0===a?n.text(this):this.empty().append((this[0]&&this[0].ownerDocument||d).createTextNode(a))},null,a,arguments.length)},append:function(){return Ha(this,arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=Ca(this,a);b.appendChild(a)}})},prepend:function(){return Ha(this,arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=Ca(this,a);b.insertBefore(a,b.firstChild)}})},before:function(){return Ha(this,arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this)})},after:function(){return Ha(this,arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this.nextSibling)})},empty:function(){for(var a,b=0;null!=(a=this[b]);b++){1===a.nodeType&&n.cleanData(ea(a,!1));while(a.firstChild)a.removeChild(a.firstChild);a.options&&n.nodeName(a,"select")&&(a.options.length=0)}return this},clone:function(a,b){return a=null==a?!1:a,b=null==b?a:b,this.map(function(){return n.clone(this,a,b)})},html:function(a){return Y(this,function(a){var b=this[0]||{},c=0,d=this.length;if(void 0===a)return 1===b.nodeType?b.innerHTML.replace(ta,""):void 0;if("string"==typeof a&&!wa.test(a)&&(l.htmlSerialize||!ua.test(a))&&(l.leadingWhitespace||!aa.test(a))&&!da[($.exec(a)||["",""])[1].toLowerCase()]){a=n.htmlPrefilter(a);try{for(;d>c;c++)b=this[c]||{},1===b.nodeType&&(n.cleanData(ea(b,!1)),b.innerHTML=a);b=0}catch(e){}}b&&this.empty().append(a)},null,a,arguments.length)},replaceWith:function(){var a=[];return Ha(this,arguments,function(b){var c=this.parentNode;n.inArray(this,a)<0&&(n.cleanData(ea(this)),c&&c.replaceChild(b,this))},a)}}),n.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){n.fn[a]=function(a){for(var c,d=0,e=[],f=n(a),h=f.length-1;h>=d;d++)c=d===h?this:this.clone(!0),n(f[d])[b](c),g.apply(e,c.get());return this.pushStack(e)}});var Ja,Ka={HTML:"block",BODY:"block"};function La(a,b){var c=n(b.createElement(a)).appendTo(b.body),d=n.css(c[0],"display");return c.detach(),d}function Ma(a){var b=d,c=Ka[a];return c||(c=La(a,b),"none"!==c&&c||(Ja=(Ja||n("':"");a._keyEvent=false;return I},_generateMonthYearHeader:function(a,b,c,e,f,h,i,g){var j=this._get(a,"changeMonth"),l=this._get(a,"changeYear"),u=this._get(a,"showMonthAfterYear"),k='
    ', -o="";if(h||!j)o+=''+i[b]+"";else{i=e&&e.getFullYear()==c;var m=f&&f.getFullYear()==c;o+='"}u||(k+=o+(h||!(j&& -l)?" ":""));a.yearshtml="";if(h||!l)k+=''+c+"";else{g=this._get(a,"yearRange").split(":");var r=(new Date).getFullYear();i=function(s){s=s.match(/c[+-].*/)?c+parseInt(s.substring(1),10):s.match(/[+-].*/)?r+parseInt(s,10):parseInt(s,10);return isNaN(s)?r:s};b=i(g[0]);g=Math.max(b,i(g[1]||""));b=e?Math.max(b,e.getFullYear()):b;g=f?Math.min(g,f.getFullYear()):g;for(a.yearshtml+='";if(d.browser.mozilla)k+='";else{k+=a.yearshtml;a.yearshtml=null}}k+=this._get(a,"yearSuffix");if(u)k+=(h||!(j&&l)?" ":"")+o;k+="
    ";return k},_adjustInstDate:function(a,b,c){var e= -a.drawYear+(c=="Y"?b:0),f=a.drawMonth+(c=="M"?b:0);b=Math.min(a.selectedDay,this._getDaysInMonth(e,f))+(c=="D"?b:0);e=this._restrictMinMax(a,this._daylightSavingAdjust(new Date(e,f,b)));a.selectedDay=e.getDate();a.drawMonth=a.selectedMonth=e.getMonth();a.drawYear=a.selectedYear=e.getFullYear();if(c=="M"||c=="Y")this._notifyChange(a)},_restrictMinMax:function(a,b){var c=this._getMinMaxDate(a,"min");a=this._getMinMaxDate(a,"max");b=c&&ba?a:b},_notifyChange:function(a){var b=this._get(a, -"onChangeMonthYear");if(b)b.apply(a.input?a.input[0]:null,[a.selectedYear,a.selectedMonth+1,a])},_getNumberOfMonths:function(a){a=this._get(a,"numberOfMonths");return a==null?[1,1]:typeof a=="number"?[1,a]:a},_getMinMaxDate:function(a,b){return this._determineDate(a,this._get(a,b+"Date"),null)},_getDaysInMonth:function(a,b){return 32-(new Date(a,b,32)).getDate()},_getFirstDayOfMonth:function(a,b){return(new Date(a,b,1)).getDay()},_canAdjustMonth:function(a,b,c,e){var f=this._getNumberOfMonths(a); -c=this._daylightSavingAdjust(new Date(c,e+(b<0?b:f[0]*f[1]),1));b<0&&c.setDate(this._getDaysInMonth(c.getFullYear(),c.getMonth()));return this._isInRange(a,c)},_isInRange:function(a,b){var c=this._getMinMaxDate(a,"min");a=this._getMinMaxDate(a,"max");return(!c||b.getTime()>=c.getTime())&&(!a||b.getTime()<=a.getTime())},_getFormatConfig:function(a){var b=this._get(a,"shortYearCutoff");b=typeof b!="string"?b:(new Date).getFullYear()%100+parseInt(b,10);return{shortYearCutoff:b,dayNamesShort:this._get(a, -"dayNamesShort"),dayNames:this._get(a,"dayNames"),monthNamesShort:this._get(a,"monthNamesShort"),monthNames:this._get(a,"monthNames")}},_formatDate:function(a,b,c,e){if(!b){a.currentDay=a.selectedDay;a.currentMonth=a.selectedMonth;a.currentYear=a.selectedYear}b=b?typeof b=="object"?b:this._daylightSavingAdjust(new Date(e,c,b)):this._daylightSavingAdjust(new Date(a.currentYear,a.currentMonth,a.currentDay));return this.formatDate(this._get(a,"dateFormat"),b,this._getFormatConfig(a))}});d.fn.datepicker= -function(a){if(!d.datepicker.initialized){d(document).mousedown(d.datepicker._checkExternalClick).find("body").append(d.datepicker.dpDiv);d.datepicker.initialized=true}var b=Array.prototype.slice.call(arguments,1);if(typeof a=="string"&&(a=="isDisabled"||a=="getDate"||a=="widget"))return d.datepicker["_"+a+"Datepicker"].apply(d.datepicker,[this[0]].concat(b));if(a=="option"&&arguments.length==2&&typeof arguments[1]=="string")return d.datepicker["_"+a+"Datepicker"].apply(d.datepicker,[this[0]].concat(b)); -return this.each(function(){typeof a=="string"?d.datepicker["_"+a+"Datepicker"].apply(d.datepicker,[this].concat(b)):d.datepicker._attachDatepicker(this,a)})};d.datepicker=new K;d.datepicker.initialized=false;d.datepicker.uuid=(new Date).getTime();d.datepicker.version="1.8.9";window["DP_jQuery_"+y]=d})(jQuery); -;/* - * jQuery UI Progressbar 1.8.9 - * - * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) - * Dual licensed under the MIT or GPL Version 2 licenses. - * http://jquery.org/license - * - * http://docs.jquery.com/UI/Progressbar - * - * Depends: - * jquery.ui.core.js - * jquery.ui.widget.js - */ -(function(b,d){b.widget("ui.progressbar",{options:{value:0,max:100},min:0,_create:function(){this.element.addClass("ui-progressbar ui-widget ui-widget-content ui-corner-all").attr({role:"progressbar","aria-valuemin":this.min,"aria-valuemax":this.options.max,"aria-valuenow":this._value()});this.valueDiv=b("
    ").appendTo(this.element);this.oldValue=this._value();this._refreshValue()},destroy:function(){this.element.removeClass("ui-progressbar ui-widget ui-widget-content ui-corner-all").removeAttr("role").removeAttr("aria-valuemin").removeAttr("aria-valuemax").removeAttr("aria-valuenow"); -this.valueDiv.remove();b.Widget.prototype.destroy.apply(this,arguments)},value:function(a){if(a===d)return this._value();this._setOption("value",a);return this},_setOption:function(a,c){if(a==="value"){this.options.value=c;this._refreshValue();this._value()===this.options.max&&this._trigger("complete")}b.Widget.prototype._setOption.apply(this,arguments)},_value:function(){var a=this.options.value;if(typeof a!=="number")a=0;return Math.min(this.options.max,Math.max(this.min,a))},_percentage:function(){return 100* -this._value()/this.options.max},_refreshValue:function(){var a=this.value(),c=this._percentage();if(this.oldValue!==a){this.oldValue=a;this._trigger("change")}this.valueDiv.toggleClass("ui-corner-right",a===this.options.max).width(c.toFixed(0)+"%");this.element.attr("aria-valuenow",a)}});b.extend(b.ui.progressbar,{version:"1.8.9"})})(jQuery); -;/* - * jQuery UI Effects 1.8.9 - * - * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) - * Dual licensed under the MIT or GPL Version 2 licenses. - * http://jquery.org/license - * - * http://docs.jquery.com/UI/Effects/ - */ -jQuery.effects||function(f,j){function n(c){var a;if(c&&c.constructor==Array&&c.length==3)return c;if(a=/rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/.exec(c))return[parseInt(a[1],10),parseInt(a[2],10),parseInt(a[3],10)];if(a=/rgb\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*\)/.exec(c))return[parseFloat(a[1])*2.55,parseFloat(a[2])*2.55,parseFloat(a[3])*2.55];if(a=/#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec(c))return[parseInt(a[1], -16),parseInt(a[2],16),parseInt(a[3],16)];if(a=/#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/.exec(c))return[parseInt(a[1]+a[1],16),parseInt(a[2]+a[2],16),parseInt(a[3]+a[3],16)];if(/rgba\(0, 0, 0, 0\)/.exec(c))return o.transparent;return o[f.trim(c).toLowerCase()]}function s(c,a){var b;do{b=f.curCSS(c,a);if(b!=""&&b!="transparent"||f.nodeName(c,"body"))break;a="backgroundColor"}while(c=c.parentNode);return n(b)}function p(){var c=document.defaultView?document.defaultView.getComputedStyle(this,null):this.currentStyle, -a={},b,d;if(c&&c.length&&c[0]&&c[c[0]])for(var e=c.length;e--;){b=c[e];if(typeof c[b]=="string"){d=b.replace(/\-(\w)/g,function(g,h){return h.toUpperCase()});a[d]=c[b]}}else for(b in c)if(typeof c[b]==="string")a[b]=c[b];return a}function q(c){var a,b;for(a in c){b=c[a];if(b==null||f.isFunction(b)||a in t||/scrollbar/.test(a)||!/color/i.test(a)&&isNaN(parseFloat(b)))delete c[a]}return c}function u(c,a){var b={_:0},d;for(d in a)if(c[d]!=a[d])b[d]=a[d];return b}function k(c,a,b,d){if(typeof c=="object"){d= -a;b=null;a=c;c=a.effect}if(f.isFunction(a)){d=a;b=null;a={}}if(typeof a=="number"||f.fx.speeds[a]){d=b;b=a;a={}}if(f.isFunction(b)){d=b;b=null}a=a||{};b=b||a.duration;b=f.fx.off?0:typeof b=="number"?b:b in f.fx.speeds?f.fx.speeds[b]:f.fx.speeds._default;d=d||a.complete;return[c,a,b,d]}function m(c){if(!c||typeof c==="number"||f.fx.speeds[c])return true;if(typeof c==="string"&&!f.effects[c])return true;return false}f.effects={};f.each(["backgroundColor","borderBottomColor","borderLeftColor","borderRightColor", -"borderTopColor","borderColor","color","outlineColor"],function(c,a){f.fx.step[a]=function(b){if(!b.colorInit){b.start=s(b.elem,a);b.end=n(b.end);b.colorInit=true}b.elem.style[a]="rgb("+Math.max(Math.min(parseInt(b.pos*(b.end[0]-b.start[0])+b.start[0],10),255),0)+","+Math.max(Math.min(parseInt(b.pos*(b.end[1]-b.start[1])+b.start[1],10),255),0)+","+Math.max(Math.min(parseInt(b.pos*(b.end[2]-b.start[2])+b.start[2],10),255),0)+")"}});var o={aqua:[0,255,255],azure:[240,255,255],beige:[245,245,220],black:[0, -0,0],blue:[0,0,255],brown:[165,42,42],cyan:[0,255,255],darkblue:[0,0,139],darkcyan:[0,139,139],darkgrey:[169,169,169],darkgreen:[0,100,0],darkkhaki:[189,183,107],darkmagenta:[139,0,139],darkolivegreen:[85,107,47],darkorange:[255,140,0],darkorchid:[153,50,204],darkred:[139,0,0],darksalmon:[233,150,122],darkviolet:[148,0,211],fuchsia:[255,0,255],gold:[255,215,0],green:[0,128,0],indigo:[75,0,130],khaki:[240,230,140],lightblue:[173,216,230],lightcyan:[224,255,255],lightgreen:[144,238,144],lightgrey:[211, -211,211],lightpink:[255,182,193],lightyellow:[255,255,224],lime:[0,255,0],magenta:[255,0,255],maroon:[128,0,0],navy:[0,0,128],olive:[128,128,0],orange:[255,165,0],pink:[255,192,203],purple:[128,0,128],violet:[128,0,128],red:[255,0,0],silver:[192,192,192],white:[255,255,255],yellow:[255,255,0],transparent:[255,255,255]},r=["add","remove","toggle"],t={border:1,borderBottom:1,borderColor:1,borderLeft:1,borderRight:1,borderTop:1,borderWidth:1,margin:1,padding:1};f.effects.animateClass=function(c,a,b, -d){if(f.isFunction(b)){d=b;b=null}return this.queue("fx",function(){var e=f(this),g=e.attr("style")||" ",h=q(p.call(this)),l,v=e.attr("className");f.each(r,function(w,i){c[i]&&e[i+"Class"](c[i])});l=q(p.call(this));e.attr("className",v);e.animate(u(h,l),a,b,function(){f.each(r,function(w,i){c[i]&&e[i+"Class"](c[i])});if(typeof e.attr("style")=="object"){e.attr("style").cssText="";e.attr("style").cssText=g}else e.attr("style",g);d&&d.apply(this,arguments)});h=f.queue(this);l=h.splice(h.length-1,1)[0]; -h.splice(1,0,l);f.dequeue(this)})};f.fn.extend({_addClass:f.fn.addClass,addClass:function(c,a,b,d){return a?f.effects.animateClass.apply(this,[{add:c},a,b,d]):this._addClass(c)},_removeClass:f.fn.removeClass,removeClass:function(c,a,b,d){return a?f.effects.animateClass.apply(this,[{remove:c},a,b,d]):this._removeClass(c)},_toggleClass:f.fn.toggleClass,toggleClass:function(c,a,b,d,e){return typeof a=="boolean"||a===j?b?f.effects.animateClass.apply(this,[a?{add:c}:{remove:c},b,d,e]):this._toggleClass(c, -a):f.effects.animateClass.apply(this,[{toggle:c},a,b,d])},switchClass:function(c,a,b,d,e){return f.effects.animateClass.apply(this,[{add:a,remove:c},b,d,e])}});f.extend(f.effects,{version:"1.8.9",save:function(c,a){for(var b=0;b").addClass("ui-effects-wrapper").css({fontSize:"100%",background:"transparent", -border:"none",margin:0,padding:0});c.wrap(b);b=c.parent();if(c.css("position")=="static"){b.css({position:"relative"});c.css({position:"relative"})}else{f.extend(a,{position:c.css("position"),zIndex:c.css("z-index")});f.each(["top","left","bottom","right"],function(d,e){a[e]=c.css(e);if(isNaN(parseInt(a[e],10)))a[e]="auto"});c.css({position:"relative",top:0,left:0,right:"auto",bottom:"auto"})}return b.css(a).show()},removeWrapper:function(c){if(c.parent().is(".ui-effects-wrapper"))return c.parent().replaceWith(c); -return c},setTransition:function(c,a,b,d){d=d||{};f.each(a,function(e,g){unit=c.cssUnit(g);if(unit[0]>0)d[g]=unit[0]*b+unit[1]});return d}});f.fn.extend({effect:function(c){var a=k.apply(this,arguments),b={options:a[1],duration:a[2],callback:a[3]};a=b.options.mode;var d=f.effects[c];if(f.fx.off||!d)return a?this[a](b.duration,b.callback):this.each(function(){b.callback&&b.callback.call(this)});return d.call(this,b)},_show:f.fn.show,show:function(c){if(m(c))return this._show.apply(this,arguments); -else{var a=k.apply(this,arguments);a[1].mode="show";return this.effect.apply(this,a)}},_hide:f.fn.hide,hide:function(c){if(m(c))return this._hide.apply(this,arguments);else{var a=k.apply(this,arguments);a[1].mode="hide";return this.effect.apply(this,a)}},__toggle:f.fn.toggle,toggle:function(c){if(m(c)||typeof c==="boolean"||f.isFunction(c))return this.__toggle.apply(this,arguments);else{var a=k.apply(this,arguments);a[1].mode="toggle";return this.effect.apply(this,a)}},cssUnit:function(c){var a=this.css(c), -b=[];f.each(["em","px","%","pt"],function(d,e){if(a.indexOf(e)>0)b=[parseFloat(a),e]});return b}});f.easing.jswing=f.easing.swing;f.extend(f.easing,{def:"easeOutQuad",swing:function(c,a,b,d,e){return f.easing[f.easing.def](c,a,b,d,e)},easeInQuad:function(c,a,b,d,e){return d*(a/=e)*a+b},easeOutQuad:function(c,a,b,d,e){return-d*(a/=e)*(a-2)+b},easeInOutQuad:function(c,a,b,d,e){if((a/=e/2)<1)return d/2*a*a+b;return-d/2*(--a*(a-2)-1)+b},easeInCubic:function(c,a,b,d,e){return d*(a/=e)*a*a+b},easeOutCubic:function(c, -a,b,d,e){return d*((a=a/e-1)*a*a+1)+b},easeInOutCubic:function(c,a,b,d,e){if((a/=e/2)<1)return d/2*a*a*a+b;return d/2*((a-=2)*a*a+2)+b},easeInQuart:function(c,a,b,d,e){return d*(a/=e)*a*a*a+b},easeOutQuart:function(c,a,b,d,e){return-d*((a=a/e-1)*a*a*a-1)+b},easeInOutQuart:function(c,a,b,d,e){if((a/=e/2)<1)return d/2*a*a*a*a+b;return-d/2*((a-=2)*a*a*a-2)+b},easeInQuint:function(c,a,b,d,e){return d*(a/=e)*a*a*a*a+b},easeOutQuint:function(c,a,b,d,e){return d*((a=a/e-1)*a*a*a*a+1)+b},easeInOutQuint:function(c, -a,b,d,e){if((a/=e/2)<1)return d/2*a*a*a*a*a+b;return d/2*((a-=2)*a*a*a*a+2)+b},easeInSine:function(c,a,b,d,e){return-d*Math.cos(a/e*(Math.PI/2))+d+b},easeOutSine:function(c,a,b,d,e){return d*Math.sin(a/e*(Math.PI/2))+b},easeInOutSine:function(c,a,b,d,e){return-d/2*(Math.cos(Math.PI*a/e)-1)+b},easeInExpo:function(c,a,b,d,e){return a==0?b:d*Math.pow(2,10*(a/e-1))+b},easeOutExpo:function(c,a,b,d,e){return a==e?b+d:d*(-Math.pow(2,-10*a/e)+1)+b},easeInOutExpo:function(c,a,b,d,e){if(a==0)return b;if(a== -e)return b+d;if((a/=e/2)<1)return d/2*Math.pow(2,10*(a-1))+b;return d/2*(-Math.pow(2,-10*--a)+2)+b},easeInCirc:function(c,a,b,d,e){return-d*(Math.sqrt(1-(a/=e)*a)-1)+b},easeOutCirc:function(c,a,b,d,e){return d*Math.sqrt(1-(a=a/e-1)*a)+b},easeInOutCirc:function(c,a,b,d,e){if((a/=e/2)<1)return-d/2*(Math.sqrt(1-a*a)-1)+b;return d/2*(Math.sqrt(1-(a-=2)*a)+1)+b},easeInElastic:function(c,a,b,d,e){c=1.70158;var g=0,h=d;if(a==0)return b;if((a/=e)==1)return b+d;g||(g=e*0.3);if(h").css({position:"absolute",visibility:"visible",left:-f*(h/d),top:-e*(i/c)}).parent().addClass("ui-effects-explode").css({position:"absolute",overflow:"hidden",width:h/d,height:i/c,left:g.left+f*(h/d)+(a.options.mode=="show"?(f-Math.floor(d/2))*(h/d):0),top:g.top+e*(i/c)+(a.options.mode=="show"?(e-Math.floor(c/2))*(i/c):0),opacity:a.options.mode=="show"?0:1}).animate({left:g.left+f*(h/d)+(a.options.mode=="show"?0:(f-Math.floor(d/2))*(h/d)),top:g.top+ -e*(i/c)+(a.options.mode=="show"?0:(e-Math.floor(c/2))*(i/c)),opacity:a.options.mode=="show"?1:0},a.duration||500);setTimeout(function(){a.options.mode=="show"?b.css({visibility:"visible"}):b.css({visibility:"visible"}).hide();a.callback&&a.callback.apply(b[0]);b.dequeue();j("div.ui-effects-explode").remove()},a.duration||500)})}})(jQuery); -;/* - * jQuery UI Effects Fade 1.8.9 - * - * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) - * Dual licensed under the MIT or GPL Version 2 licenses. - * http://jquery.org/license - * - * http://docs.jquery.com/UI/Effects/Fade - * - * Depends: - * jquery.effects.core.js - */ -(function(b){b.effects.fade=function(a){return this.queue(function(){var c=b(this),d=b.effects.setMode(c,a.options.mode||"hide");c.animate({opacity:d},{queue:false,duration:a.duration,easing:a.options.easing,complete:function(){a.callback&&a.callback.apply(this,arguments);c.dequeue()}})})}})(jQuery); -;/* - * jQuery UI Effects Fold 1.8.9 - * - * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) - * Dual licensed under the MIT or GPL Version 2 licenses. - * http://jquery.org/license - * - * http://docs.jquery.com/UI/Effects/Fold - * - * Depends: - * jquery.effects.core.js - */ -(function(c){c.effects.fold=function(a){return this.queue(function(){var b=c(this),j=["position","top","bottom","left","right"],d=c.effects.setMode(b,a.options.mode||"hide"),g=a.options.size||15,h=!!a.options.horizFirst,k=a.duration?a.duration/2:c.fx.speeds._default/2;c.effects.save(b,j);b.show();var e=c.effects.createWrapper(b).css({overflow:"hidden"}),f=d=="show"!=h,l=f?["width","height"]:["height","width"];f=f?[e.width(),e.height()]:[e.height(),e.width()];var i=/([0-9]+)%/.exec(g);if(i)g=parseInt(i[1], -10)/100*f[d=="hide"?0:1];if(d=="show")e.css(h?{height:0,width:g}:{height:g,width:0});h={};i={};h[l[0]]=d=="show"?f[0]:g;i[l[1]]=d=="show"?f[1]:0;e.animate(h,k,a.options.easing).animate(i,k,a.options.easing,function(){d=="hide"&&b.hide();c.effects.restore(b,j);c.effects.removeWrapper(b);a.callback&&a.callback.apply(b[0],arguments);b.dequeue()})})}})(jQuery); -;/* - * jQuery UI Effects Highlight 1.8.9 - * - * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) - * Dual licensed under the MIT or GPL Version 2 licenses. - * http://jquery.org/license - * - * http://docs.jquery.com/UI/Effects/Highlight - * - * Depends: - * jquery.effects.core.js - */ -(function(b){b.effects.highlight=function(c){return this.queue(function(){var a=b(this),e=["backgroundImage","backgroundColor","opacity"],d=b.effects.setMode(a,c.options.mode||"show"),f={backgroundColor:a.css("backgroundColor")};if(d=="hide")f.opacity=0;b.effects.save(a,e);a.show().css({backgroundImage:"none",backgroundColor:c.options.color||"#ffff99"}).animate(f,{queue:false,duration:c.duration,easing:c.options.easing,complete:function(){d=="hide"&&a.hide();b.effects.restore(a,e);d=="show"&&!b.support.opacity&& -this.style.removeAttribute("filter");c.callback&&c.callback.apply(this,arguments);a.dequeue()}})})}})(jQuery); -;/* - * jQuery UI Effects Pulsate 1.8.9 - * - * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) - * Dual licensed under the MIT or GPL Version 2 licenses. - * http://jquery.org/license - * - * http://docs.jquery.com/UI/Effects/Pulsate - * - * Depends: - * jquery.effects.core.js - */ -(function(d){d.effects.pulsate=function(a){return this.queue(function(){var b=d(this),c=d.effects.setMode(b,a.options.mode||"show");times=(a.options.times||5)*2-1;duration=a.duration?a.duration/2:d.fx.speeds._default/2;isVisible=b.is(":visible");animateTo=0;if(!isVisible){b.css("opacity",0).show();animateTo=1}if(c=="hide"&&isVisible||c=="show"&&!isVisible)times--;for(c=0;c').appendTo(document.body).addClass(a.options.className).css({top:d.top,left:d.left,height:b.innerHeight(),width:b.innerWidth(),position:"absolute"}).animate(c,a.duration,a.options.easing,function(){f.remove();a.callback&&a.callback.apply(b[0],arguments); -b.dequeue()})})}})(jQuery); -; \ No newline at end of file diff --git a/webapp/src/main/webapp/js/jquery.js b/webapp/src/main/webapp/js/jquery.js deleted file mode 100644 index 14fd6470f..000000000 --- a/webapp/src/main/webapp/js/jquery.js +++ /dev/null @@ -1,16 +0,0 @@ -/*! - * jQuery JavaScript Library v1.5.1 - * http://jquery.com/ - * - * Copyright 2011, John Resig - * Dual licensed under the MIT or GPL Version 2 licenses. - * http://jquery.org/license - * - * Includes Sizzle.js - * http://sizzlejs.com/ - * Copyright 2011, The Dojo Foundation - * Released under the MIT, BSD, and GPL Licenses. - * - * Date: Wed Feb 23 13:55:29 2011 -0500 - */ -(function(a,b){function cg(a){return d.isWindow(a)?a:a.nodeType===9?a.defaultView||a.parentWindow:!1}function cd(a){if(!bZ[a]){var b=d("<"+a+">").appendTo("body"),c=b.css("display");b.remove();if(c==="none"||c==="")c="block";bZ[a]=c}return bZ[a]}function cc(a,b){var c={};d.each(cb.concat.apply([],cb.slice(0,b)),function(){c[this]=a});return c}function bY(){try{return new a.ActiveXObject("Microsoft.XMLHTTP")}catch(b){}}function bX(){try{return new a.XMLHttpRequest}catch(b){}}function bW(){d(a).unload(function(){for(var a in bU)bU[a](0,1)})}function bQ(a,c){a.dataFilter&&(c=a.dataFilter(c,a.dataType));var e=a.dataTypes,f={},g,h,i=e.length,j,k=e[0],l,m,n,o,p;for(g=1;g=0===c})}function N(a){return!a||!a.parentNode||a.parentNode.nodeType===11}function F(a,b){return(a&&a!=="*"?a+".":"")+b.replace(r,"`").replace(s,"&")}function E(a){var b,c,e,f,g,h,i,j,k,l,m,n,o,q=[],r=[],s=d._data(this,"events");if(a.liveFired!==this&&s&&s.live&&!a.target.disabled&&(!a.button||a.type!=="click")){a.namespace&&(n=new RegExp("(^|\\.)"+a.namespace.split(".").join("\\.(?:.*\\.)?")+"(\\.|$)")),a.liveFired=this;var t=s.live.slice(0);for(i=0;ic)break;a.currentTarget=f.elem,a.data=f.handleObj.data,a.handleObj=f.handleObj,o=f.handleObj.origHandler.apply(f.elem,arguments);if(o===!1||a.isPropagationStopped()){c=f.level,o===!1&&(b=!1);if(a.isImmediatePropagationStopped())break}}return b}}function C(a,c,e){var f=d.extend({},e[0]);f.type=a,f.originalEvent={},f.liveFired=b,d.event.handle.call(c,f),f.isDefaultPrevented()&&e[0].preventDefault()}function w(){return!0}function v(){return!1}function g(a){for(var b in a)if(b!=="toJSON")return!1;return!0}function f(a,c,f){if(f===b&&a.nodeType===1){f=a.getAttribute("data-"+c);if(typeof f==="string"){try{f=f==="true"?!0:f==="false"?!1:f==="null"?null:d.isNaN(f)?e.test(f)?d.parseJSON(f):f:parseFloat(f)}catch(g){}d.data(a,c,f)}else f=b}return f}var c=a.document,d=function(){function I(){if(!d.isReady){try{c.documentElement.doScroll("left")}catch(a){setTimeout(I,1);return}d.ready()}}var d=function(a,b){return new d.fn.init(a,b,g)},e=a.jQuery,f=a.$,g,h=/^(?:[^<]*(<[\w\W]+>)[^>]*$|#([\w\-]+)$)/,i=/\S/,j=/^\s+/,k=/\s+$/,l=/\d/,m=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,n=/^[\],:{}\s]*$/,o=/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,p=/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,q=/(?:^|:|,)(?:\s*\[)+/g,r=/(webkit)[ \/]([\w.]+)/,s=/(opera)(?:.*version)?[ \/]([\w.]+)/,t=/(msie) ([\w.]+)/,u=/(mozilla)(?:.*? rv:([\w.]+))?/,v=navigator.userAgent,w,x=!1,y,z="then done fail isResolved isRejected promise".split(" "),A,B=Object.prototype.toString,C=Object.prototype.hasOwnProperty,D=Array.prototype.push,E=Array.prototype.slice,F=String.prototype.trim,G=Array.prototype.indexOf,H={};d.fn=d.prototype={constructor:d,init:function(a,e,f){var g,i,j,k;if(!a)return this;if(a.nodeType){this.context=this[0]=a,this.length=1;return this}if(a==="body"&&!e&&c.body){this.context=c,this[0]=c.body,this.selector="body",this.length=1;return this}if(typeof a==="string"){g=h.exec(a);if(!g||!g[1]&&e)return!e||e.jquery?(e||f).find(a):this.constructor(e).find(a);if(g[1]){e=e instanceof d?e[0]:e,k=e?e.ownerDocument||e:c,j=m.exec(a),j?d.isPlainObject(e)?(a=[c.createElement(j[1])],d.fn.attr.call(a,e,!0)):a=[k.createElement(j[1])]:(j=d.buildFragment([g[1]],[k]),a=(j.cacheable?d.clone(j.fragment):j.fragment).childNodes);return d.merge(this,a)}i=c.getElementById(g[2]);if(i&&i.parentNode){if(i.id!==g[2])return f.find(a);this.length=1,this[0]=i}this.context=c,this.selector=a;return this}if(d.isFunction(a))return f.ready(a);a.selector!==b&&(this.selector=a.selector,this.context=a.context);return d.makeArray(a,this)},selector:"",jquery:"1.5.1",length:0,size:function(){return this.length},toArray:function(){return E.call(this,0)},get:function(a){return a==null?this.toArray():a<0?this[this.length+a]:this[a]},pushStack:function(a,b,c){var e=this.constructor();d.isArray(a)?D.apply(e,a):d.merge(e,a),e.prevObject=this,e.context=this.context,b==="find"?e.selector=this.selector+(this.selector?" ":"")+c:b&&(e.selector=this.selector+"."+b+"("+c+")");return e},each:function(a,b){return d.each(this,a,b)},ready:function(a){d.bindReady(),y.done(a);return this},eq:function(a){return a===-1?this.slice(a):this.slice(a,+a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(E.apply(this,arguments),"slice",E.call(arguments).join(","))},map:function(a){return this.pushStack(d.map(this,function(b,c){return a.call(b,c,b)}))},end:function(){return this.prevObject||this.constructor(null)},push:D,sort:[].sort,splice:[].splice},d.fn.init.prototype=d.fn,d.extend=d.fn.extend=function(){var a,c,e,f,g,h,i=arguments[0]||{},j=1,k=arguments.length,l=!1;typeof i==="boolean"&&(l=i,i=arguments[1]||{},j=2),typeof i!=="object"&&!d.isFunction(i)&&(i={}),k===j&&(i=this,--j);for(;j0)return;y.resolveWith(c,[d]),d.fn.trigger&&d(c).trigger("ready").unbind("ready")}},bindReady:function(){if(!x){x=!0;if(c.readyState==="complete")return setTimeout(d.ready,1);if(c.addEventListener)c.addEventListener("DOMContentLoaded",A,!1),a.addEventListener("load",d.ready,!1);else if(c.attachEvent){c.attachEvent("onreadystatechange",A),a.attachEvent("onload",d.ready);var b=!1;try{b=a.frameElement==null}catch(e){}c.documentElement.doScroll&&b&&I()}}},isFunction:function(a){return d.type(a)==="function"},isArray:Array.isArray||function(a){return d.type(a)==="array"},isWindow:function(a){return a&&typeof a==="object"&&"setInterval"in a},isNaN:function(a){return a==null||!l.test(a)||isNaN(a)},type:function(a){return a==null?String(a):H[B.call(a)]||"object"},isPlainObject:function(a){if(!a||d.type(a)!=="object"||a.nodeType||d.isWindow(a))return!1;if(a.constructor&&!C.call(a,"constructor")&&!C.call(a.constructor.prototype,"isPrototypeOf"))return!1;var c;for(c in a){}return c===b||C.call(a,c)},isEmptyObject:function(a){for(var b in a)return!1;return!0},error:function(a){throw a},parseJSON:function(b){if(typeof b!=="string"||!b)return null;b=d.trim(b);if(n.test(b.replace(o,"@").replace(p,"]").replace(q,"")))return a.JSON&&a.JSON.parse?a.JSON.parse(b):(new Function("return "+b))();d.error("Invalid JSON: "+b)},parseXML:function(b,c,e){a.DOMParser?(e=new DOMParser,c=e.parseFromString(b,"text/xml")):(c=new ActiveXObject("Microsoft.XMLDOM"),c.async="false",c.loadXML(b)),e=c.documentElement,(!e||!e.nodeName||e.nodeName==="parsererror")&&d.error("Invalid XML: "+b);return c},noop:function(){},globalEval:function(a){if(a&&i.test(a)){var b=c.head||c.getElementsByTagName("head")[0]||c.documentElement,e=c.createElement("script");d.support.scriptEval()?e.appendChild(c.createTextNode(a)):e.text=a,b.insertBefore(e,b.firstChild),b.removeChild(e)}},nodeName:function(a,b){return a.nodeName&&a.nodeName.toUpperCase()===b.toUpperCase()},each:function(a,c,e){var f,g=0,h=a.length,i=h===b||d.isFunction(a);if(e){if(i){for(f in a)if(c.apply(a[f],e)===!1)break}else for(;g1){var f=E.call(arguments,0),g=b,h=function(a){return function(b){f[a]=arguments.length>1?E.call(arguments,0):b,--g||c.resolveWith(e,f)}};while(b--)a=f[b],a&&d.isFunction(a.promise)?a.promise().then(h(b),c.reject):--g;g||c.resolveWith(e,f)}else c!==a&&c.resolve(a);return e},uaMatch:function(a){a=a.toLowerCase();var b=r.exec(a)||s.exec(a)||t.exec(a)||a.indexOf("compatible")<0&&u.exec(a)||[];return{browser:b[1]||"",version:b[2]||"0"}},sub:function(){function a(b,c){return new a.fn.init(b,c)}d.extend(!0,a,this),a.superclass=this,a.fn=a.prototype=this(),a.fn.constructor=a,a.subclass=this.subclass,a.fn.init=function b(b,c){c&&c instanceof d&&!(c instanceof a)&&(c=a(c));return d.fn.init.call(this,b,c,e)},a.fn.init.prototype=a.fn;var e=a(c);return a},browser:{}}),y=d._Deferred(),d.each("Boolean Number String Function Array Date RegExp Object".split(" "),function(a,b){H["[object "+b+"]"]=b.toLowerCase()}),w=d.uaMatch(v),w.browser&&(d.browser[w.browser]=!0,d.browser.version=w.version),d.browser.webkit&&(d.browser.safari=!0),G&&(d.inArray=function(a,b){return G.call(b,a)}),i.test(" ")&&(j=/^[\s\xA0]+/,k=/[\s\xA0]+$/),g=d(c),c.addEventListener?A=function(){c.removeEventListener("DOMContentLoaded",A,!1),d.ready()}:c.attachEvent&&(A=function(){c.readyState==="complete"&&(c.detachEvent("onreadystatechange",A),d.ready())});return d}();(function(){d.support={};var b=c.createElement("div");b.style.display="none",b.innerHTML="
    a";var e=b.getElementsByTagName("*"),f=b.getElementsByTagName("a")[0],g=c.createElement("select"),h=g.appendChild(c.createElement("option")),i=b.getElementsByTagName("input")[0];if(e&&e.length&&f){d.support={leadingWhitespace:b.firstChild.nodeType===3,tbody:!b.getElementsByTagName("tbody").length,htmlSerialize:!!b.getElementsByTagName("link").length,style:/red/.test(f.getAttribute("style")),hrefNormalized:f.getAttribute("href")==="/a",opacity:/^0.55$/.test(f.style.opacity),cssFloat:!!f.style.cssFloat,checkOn:i.value==="on",optSelected:h.selected,deleteExpando:!0,optDisabled:!1,checkClone:!1,noCloneEvent:!0,noCloneChecked:!0,boxModel:null,inlineBlockNeedsLayout:!1,shrinkWrapBlocks:!1,reliableHiddenOffsets:!0},i.checked=!0,d.support.noCloneChecked=i.cloneNode(!0).checked,g.disabled=!0,d.support.optDisabled=!h.disabled;var j=null;d.support.scriptEval=function(){if(j===null){var b=c.documentElement,e=c.createElement("script"),f="script"+d.now();try{e.appendChild(c.createTextNode("window."+f+"=1;"))}catch(g){}b.insertBefore(e,b.firstChild),a[f]?(j=!0,delete a[f]):j=!1,b.removeChild(e),b=e=f=null}return j};try{delete b.test}catch(k){d.support.deleteExpando=!1}!b.addEventListener&&b.attachEvent&&b.fireEvent&&(b.attachEvent("onclick",function l(){d.support.noCloneEvent=!1,b.detachEvent("onclick",l)}),b.cloneNode(!0).fireEvent("onclick")),b=c.createElement("div"),b.innerHTML="";var m=c.createDocumentFragment();m.appendChild(b.firstChild),d.support.checkClone=m.cloneNode(!0).cloneNode(!0).lastChild.checked,d(function(){var a=c.createElement("div"),b=c.getElementsByTagName("body")[0];if(b){a.style.width=a.style.paddingLeft="1px",b.appendChild(a),d.boxModel=d.support.boxModel=a.offsetWidth===2,"zoom"in a.style&&(a.style.display="inline",a.style.zoom=1,d.support.inlineBlockNeedsLayout=a.offsetWidth===2,a.style.display="",a.innerHTML="
    ",d.support.shrinkWrapBlocks=a.offsetWidth!==2),a.innerHTML="
    t
    ";var e=a.getElementsByTagName("td");d.support.reliableHiddenOffsets=e[0].offsetHeight===0,e[0].style.display="",e[1].style.display="none",d.support.reliableHiddenOffsets=d.support.reliableHiddenOffsets&&e[0].offsetHeight===0,a.innerHTML="",b.removeChild(a).style.display="none",a=e=null}});var n=function(a){var b=c.createElement("div");a="on"+a;if(!b.attachEvent)return!0;var d=a in b;d||(b.setAttribute(a,"return;"),d=typeof b[a]==="function"),b=null;return d};d.support.submitBubbles=n("submit"),d.support.changeBubbles=n("change"),b=e=f=null}})();var e=/^(?:\{.*\}|\[.*\])$/;d.extend({cache:{},uuid:0,expando:"jQuery"+(d.fn.jquery+Math.random()).replace(/\D/g,""),noData:{embed:!0,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",applet:!0},hasData:function(a){a=a.nodeType?d.cache[a[d.expando]]:a[d.expando];return!!a&&!g(a)},data:function(a,c,e,f){if(d.acceptData(a)){var g=d.expando,h=typeof c==="string",i,j=a.nodeType,k=j?d.cache:a,l=j?a[d.expando]:a[d.expando]&&d.expando;if((!l||f&&l&&!k[l][g])&&h&&e===b)return;l||(j?a[d.expando]=l=++d.uuid:l=d.expando),k[l]||(k[l]={},j||(k[l].toJSON=d.noop));if(typeof c==="object"||typeof c==="function")f?k[l][g]=d.extend(k[l][g],c):k[l]=d.extend(k[l],c);i=k[l],f&&(i[g]||(i[g]={}),i=i[g]),e!==b&&(i[c]=e);if(c==="events"&&!i[c])return i[g]&&i[g].events;return h?i[c]:i}},removeData:function(b,c,e){if(d.acceptData(b)){var f=d.expando,h=b.nodeType,i=h?d.cache:b,j=h?b[d.expando]:d.expando;if(!i[j])return;if(c){var k=e?i[j][f]:i[j];if(k){delete k[c];if(!g(k))return}}if(e){delete i[j][f];if(!g(i[j]))return}var l=i[j][f];d.support.deleteExpando||i!=a?delete i[j]:i[j]=null,l?(i[j]={},h||(i[j].toJSON=d.noop),i[j][f]=l):h&&(d.support.deleteExpando?delete b[d.expando]:b.removeAttribute?b.removeAttribute(d.expando):b[d.expando]=null)}},_data:function(a,b,c){return d.data(a,b,c,!0)},acceptData:function(a){if(a.nodeName){var b=d.noData[a.nodeName.toLowerCase()];if(b)return b!==!0&&a.getAttribute("classid")===b}return!0}}),d.fn.extend({data:function(a,c){var e=null;if(typeof a==="undefined"){if(this.length){e=d.data(this[0]);if(this[0].nodeType===1){var g=this[0].attributes,h;for(var i=0,j=g.length;i-1)return!0;return!1},val:function(a){if(!arguments.length){var c=this[0];if(c){if(d.nodeName(c,"option")){var e=c.attributes.value;return!e||e.specified?c.value:c.text}if(d.nodeName(c,"select")){var f=c.selectedIndex,g=[],h=c.options,i=c.type==="select-one";if(f<0)return null;for(var k=i?f:0,l=i?f+1:h.length;k=0;else if(d.nodeName(this,"select")){var f=d.makeArray(e);d("option",this).each(function(){this.selected=d.inArray(d(this).val(),f)>=0}),f.length||(this.selectedIndex=-1)}else this.value=e}})}}),d.extend({attrFn:{val:!0,css:!0,html:!0,text:!0,data:!0,width:!0,height:!0,offset:!0},attr:function(a,c,e,f){if(!a||a.nodeType===3||a.nodeType===8||a.nodeType===2)return b;if(f&&c in d.attrFn)return d(a)[c](e);var g=a.nodeType!==1||!d.isXMLDoc(a),h=e!==b;c=g&&d.props[c]||c;if(a.nodeType===1){var i=k.test(c);if(c==="selected"&&!d.support.optSelected){var j=a.parentNode;j&&(j.selectedIndex,j.parentNode&&j.parentNode.selectedIndex)}if((c in a||a[c]!==b)&&g&&!i){h&&(c==="type"&&l.test(a.nodeName)&&a.parentNode&&d.error("type property can't be changed"),e===null?a.nodeType===1&&a.removeAttribute(c):a[c]=e);if(d.nodeName(a,"form")&&a.getAttributeNode(c))return a.getAttributeNode(c).nodeValue;if(c==="tabIndex"){var o=a.getAttributeNode("tabIndex");return o&&o.specified?o.value:m.test(a.nodeName)||n.test(a.nodeName)&&a.href?0:b}return a[c]}if(!d.support.style&&g&&c==="style"){h&&(a.style.cssText=""+e);return a.style.cssText}h&&a.setAttribute(c,""+e);if(!a.attributes[c]&&(a.hasAttribute&&!a.hasAttribute(c)))return b;var p=!d.support.hrefNormalized&&g&&i?a.getAttribute(c,2):a.getAttribute(c);return p===null?b:p}h&&(a[c]=e);return a[c]}});var p=/\.(.*)$/,q=/^(?:textarea|input|select)$/i,r=/\./g,s=/ /g,t=/[^\w\s.|`]/g,u=function(a){return a.replace(t,"\\$&")};d.event={add:function(c,e,f,g){if(c.nodeType!==3&&c.nodeType!==8){try{d.isWindow(c)&&(c!==a&&!c.frameElement)&&(c=a)}catch(h){}if(f===!1)f=v;else if(!f)return;var i,j;f.handler&&(i=f,f=i.handler),f.guid||(f.guid=d.guid++);var k=d._data(c);if(!k)return;var l=k.events,m=k.handle;l||(k.events=l={}),m||(k.handle=m=function(){return typeof d!=="undefined"&&!d.event.triggered?d.event.handle.apply(m.elem,arguments):b}),m.elem=c,e=e.split(" ");var n,o=0,p;while(n=e[o++]){j=i?d.extend({},i):{handler:f,data:g},n.indexOf(".")>-1?(p=n.split("."),n=p.shift(),j.namespace=p.slice(0).sort().join(".")):(p=[],j.namespace=""),j.type=n,j.guid||(j.guid=f.guid);var q=l[n],r=d.event.special[n]||{};if(!q){q=l[n]=[];if(!r.setup||r.setup.call(c,g,p,m)===!1)c.addEventListener?c.addEventListener(n,m,!1):c.attachEvent&&c.attachEvent("on"+n,m)}r.add&&(r.add.call(c,j),j.handler.guid||(j.handler.guid=f.guid)),q.push(j),d.event.global[n]=!0}c=null}},global:{},remove:function(a,c,e,f){if(a.nodeType!==3&&a.nodeType!==8){e===!1&&(e=v);var g,h,i,j,k=0,l,m,n,o,p,q,r,s=d.hasData(a)&&d._data(a),t=s&&s.events;if(!s||!t)return;c&&c.type&&(e=c.handler,c=c.type);if(!c||typeof c==="string"&&c.charAt(0)==="."){c=c||"";for(h in t)d.event.remove(a,h+c);return}c=c.split(" ");while(h=c[k++]){r=h,q=null,l=h.indexOf(".")<0,m=[],l||(m=h.split("."),h=m.shift(),n=new RegExp("(^|\\.)"+d.map(m.slice(0).sort(),u).join("\\.(?:.*\\.)?")+"(\\.|$)")),p=t[h];if(!p)continue;if(!e){for(j=0;j=0&&(a.type=f=f.slice(0,-1),a.exclusive=!0),e||(a.stopPropagation(),d.event.global[f]&&d.each(d.cache,function(){var b=d.expando,e=this[b];e&&e.events&&e.events[f]&&d.event.trigger(a,c,e.handle.elem)}));if(!e||e.nodeType===3||e.nodeType===8)return b;a.result=b,a.target=e,c=d.makeArray(c),c.unshift(a)}a.currentTarget=e;var h=d._data(e,"handle");h&&h.apply(e,c);var i=e.parentNode||e.ownerDocument;try{e&&e.nodeName&&d.noData[e.nodeName.toLowerCase()]||e["on"+f]&&e["on"+f].apply(e,c)===!1&&(a.result=!1,a.preventDefault())}catch(j){}if(!a.isPropagationStopped()&&i)d.event.trigger(a,c,i,!0);else if(!a.isDefaultPrevented()){var k,l=a.target,m=f.replace(p,""),n=d.nodeName(l,"a")&&m==="click",o=d.event.special[m]||{};if((!o._default||o._default.call(e,a)===!1)&&!n&&!(l&&l.nodeName&&d.noData[l.nodeName.toLowerCase()])){try{l[m]&&(k=l["on"+m],k&&(l["on"+m]=null),d.event.triggered=!0,l[m]())}catch(q){}k&&(l["on"+m]=k),d.event.triggered=!1}}},handle:function(c){var e,f,g,h,i,j=[],k=d.makeArray(arguments);c=k[0]=d.event.fix(c||a.event),c.currentTarget=this,e=c.type.indexOf(".")<0&&!c.exclusive,e||(g=c.type.split("."),c.type=g.shift(),j=g.slice(0).sort(),h=new RegExp("(^|\\.)"+j.join("\\.(?:.*\\.)?")+"(\\.|$)")),c.namespace=c.namespace||j.join("."),i=d._data(this,"events"),f=(i||{})[c.type];if(i&&f){f=f.slice(0);for(var l=0,m=f.length;l-1?d.map(a.options,function(a){return a.selected}).join("-"):"":a.nodeName.toLowerCase()==="select"&&(c=a.selectedIndex);return c},B=function B(a){var c=a.target,e,f;if(q.test(c.nodeName)&&!c.readOnly){e=d._data(c,"_change_data"),f=A(c),(a.type!=="focusout"||c.type!=="radio")&&d._data(c,"_change_data",f);if(e===b||f===e)return;if(e!=null||f)a.type="change",a.liveFired=b,d.event.trigger(a,arguments[1],c)}};d.event.special.change={filters:{focusout:B,beforedeactivate:B,click:function(a){var b=a.target,c=b.type;(c==="radio"||c==="checkbox"||b.nodeName.toLowerCase()==="select")&&B.call(this,a)},keydown:function(a){var b=a.target,c=b.type;(a.keyCode===13&&b.nodeName.toLowerCase()!=="textarea"||a.keyCode===32&&(c==="checkbox"||c==="radio")||c==="select-multiple")&&B.call(this,a)},beforeactivate:function(a){var b=a.target;d._data(b,"_change_data",A(b))}},setup:function(a,b){if(this.type==="file")return!1;for(var c in z)d.event.add(this,c+".specialChange",z[c]);return q.test(this.nodeName)},teardown:function(a){d.event.remove(this,".specialChange");return q.test(this.nodeName)}},z=d.event.special.change.filters,z.focus=z.beforeactivate}c.addEventListener&&d.each({focus:"focusin",blur:"focusout"},function(a,b){function c(a){a=d.event.fix(a),a.type=b;return d.event.handle.call(this,a)}d.event.special[b]={setup:function(){this.addEventListener(a,c,!0)},teardown:function(){this.removeEventListener(a,c,!0)}}}),d.each(["bind","one"],function(a,c){d.fn[c]=function(a,e,f){if(typeof a==="object"){for(var g in a)this[c](g,e,a[g],f);return this}if(d.isFunction(e)||e===!1)f=e,e=b;var h=c==="one"?d.proxy(f,function(a){d(this).unbind(a,h);return f.apply(this,arguments)}):f;if(a==="unload"&&c!=="one")this.one(a,e,f);else for(var i=0,j=this.length;i0?this.bind(b,a,c):this.trigger(b)},d.attrFn&&(d.attrFn[b]=!0)}),function(){function u(a,b,c,d,e,f){for(var g=0,h=d.length;g0){j=i;break}}i=i[a]}d[g]=j}}}function t(a,b,c,d,e,f){for(var g=0,h=d.length;g+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,e=0,f=Object.prototype.toString,g=!1,h=!0,i=/\\/g,j=/\W/;[0,0].sort(function(){h=!1;return 0});var k=function(b,d,e,g){e=e||[],d=d||c;var h=d;if(d.nodeType!==1&&d.nodeType!==9)return[];if(!b||typeof b!=="string")return e;var i,j,n,o,q,r,s,t,u=!0,w=k.isXML(d),x=[],y=b;do{a.exec(""),i=a.exec(y);if(i){y=i[3],x.push(i[1]);if(i[2]){o=i[3];break}}}while(i);if(x.length>1&&m.exec(b))if(x.length===2&&l.relative[x[0]])j=v(x[0]+x[1],d);else{j=l.relative[x[0]]?[d]:k(x.shift(),d);while(x.length)b=x.shift(),l.relative[b]&&(b+=x.shift()),j=v(b,j)}else{!g&&x.length>1&&d.nodeType===9&&!w&&l.match.ID.test(x[0])&&!l.match.ID.test(x[x.length-1])&&(q=k.find(x.shift(),d,w),d=q.expr?k.filter(q.expr,q.set)[0]:q.set[0]);if(d){q=g?{expr:x.pop(),set:p(g)}:k.find(x.pop(),x.length===1&&(x[0]==="~"||x[0]==="+")&&d.parentNode?d.parentNode:d,w),j=q.expr?k.filter(q.expr,q.set):q.set,x.length>0?n=p(j):u=!1;while(x.length)r=x.pop(),s=r,l.relative[r]?s=x.pop():r="",s==null&&(s=d),l.relative[r](n,s,w)}else n=x=[]}n||(n=j),n||k.error(r||b);if(f.call(n)==="[object Array]")if(u)if(d&&d.nodeType===1)for(t=0;n[t]!=null;t++)n[t]&&(n[t]===!0||n[t].nodeType===1&&k.contains(d,n[t]))&&e.push(j[t]);else for(t=0;n[t]!=null;t++)n[t]&&n[t].nodeType===1&&e.push(j[t]);else e.push.apply(e,n);else p(n,e);o&&(k(o,h,e,g),k.uniqueSort(e));return e};k.uniqueSort=function(a){if(r){g=h,a.sort(r);if(g)for(var b=1;b0},k.find=function(a,b,c){var d;if(!a)return[];for(var e=0,f=l.order.length;e":function(a,b){var c,d=typeof b==="string",e=0,f=a.length;if(d&&!j.test(b)){b=b.toLowerCase();for(;e=0)?c||d.push(h):c&&(b[g]=!1));return!1},ID:function(a){return a[1].replace(i,"")},TAG:function(a,b){return a[1].replace(i,"").toLowerCase()},CHILD:function(a){if(a[1]==="nth"){a[2]||k.error(a[0]),a[2]=a[2].replace(/^\+|\s*/g,"");var b=/(-?)(\d*)(?:n([+\-]?\d*))?/.exec(a[2]==="even"&&"2n"||a[2]==="odd"&&"2n+1"||!/\D/.test(a[2])&&"0n+"+a[2]||a[2]);a[2]=b[1]+(b[2]||1)-0,a[3]=b[3]-0}else a[2]&&k.error(a[0]);a[0]=e++;return a},ATTR:function(a,b,c,d,e,f){var g=a[1]=a[1].replace(i,"");!f&&l.attrMap[g]&&(a[1]=l.attrMap[g]),a[4]=(a[4]||a[5]||"").replace(i,""),a[2]==="~="&&(a[4]=" "+a[4]+" ");return a},PSEUDO:function(b,c,d,e,f){if(b[1]==="not")if((a.exec(b[3])||"").length>1||/^\w/.test(b[3]))b[3]=k(b[3],null,null,c);else{var g=k.filter(b[3],c,d,!0^f);d||e.push.apply(e,g);return!1}else if(l.match.POS.test(b[0])||l.match.CHILD.test(b[0]))return!0;return b},POS:function(a){a.unshift(!0);return a}},filters:{enabled:function(a){return a.disabled===!1&&a.type!=="hidden"},disabled:function(a){return a.disabled===!0},checked:function(a){return a.checked===!0},selected:function(a){a.parentNode&&a.parentNode.selectedIndex;return a.selected===!0},parent:function(a){return!!a.firstChild},empty:function(a){return!a.firstChild},has:function(a,b,c){return!!k(c[3],a).length},header:function(a){return/h\d/i.test(a.nodeName)},text:function(a){return"text"===a.getAttribute("type")},radio:function(a){return"radio"===a.type},checkbox:function(a){return"checkbox"===a.type},file:function(a){return"file"===a.type},password:function(a){return"password"===a.type},submit:function(a){return"submit"===a.type},image:function(a){return"image"===a.type},reset:function(a){return"reset"===a.type},button:function(a){return"button"===a.type||a.nodeName.toLowerCase()==="button"},input:function(a){return/input|select|textarea|button/i.test(a.nodeName)}},setFilters:{first:function(a,b){return b===0},last:function(a,b,c,d){return b===d.length-1},even:function(a,b){return b%2===0},odd:function(a,b){return b%2===1},lt:function(a,b,c){return bc[3]-0},nth:function(a,b,c){return c[3]-0===b},eq:function(a,b,c){return c[3]-0===b}},filter:{PSEUDO:function(a,b,c,d){var e=b[1],f=l.filters[e];if(f)return f(a,c,b,d);if(e==="contains")return(a.textContent||a.innerText||k.getText([a])||"").indexOf(b[3])>=0;if(e==="not"){var g=b[3];for(var h=0,i=g.length;h=0}},ID:function(a,b){return a.nodeType===1&&a.getAttribute("id")===b},TAG:function(a,b){return b==="*"&&a.nodeType===1||a.nodeName.toLowerCase()===b},CLASS:function(a,b){return(" "+(a.className||a.getAttribute("class"))+" ").indexOf(b)>-1},ATTR:function(a,b){var c=b[1],d=l.attrHandle[c]?l.attrHandle[c](a):a[c]!=null?a[c]:a.getAttribute(c),e=d+"",f=b[2],g=b[4];return d==null?f==="!=":f==="="?e===g:f==="*="?e.indexOf(g)>=0:f==="~="?(" "+e+" ").indexOf(g)>=0:g?f==="!="?e!==g:f==="^="?e.indexOf(g)===0:f==="$="?e.substr(e.length-g.length)===g:f==="|="?e===g||e.substr(0,g.length+1)===g+"-":!1:e&&d!==!1},POS:function(a,b,c,d){var e=b[2],f=l.setFilters[e];if(f)return f(a,c,b,d)}}},m=l.match.POS,n=function(a,b){return"\\"+(b-0+1)};for(var o in l.match)l.match[o]=new RegExp(l.match[o].source+/(?![^\[]*\])(?![^\(]*\))/.source),l.leftMatch[o]=new RegExp(/(^(?:.|\r|\n)*?)/.source+l.match[o].source.replace(/\\(\d+)/g,n));var p=function(a,b){a=Array.prototype.slice.call(a,0);if(b){b.push.apply(b,a);return b}return a};try{Array.prototype.slice.call(c.documentElement.childNodes,0)[0].nodeType}catch(q){p=function(a,b){var c=0,d=b||[];if(f.call(a)==="[object Array]")Array.prototype.push.apply(d,a);else if(typeof a.length==="number")for(var e=a.length;c",e.insertBefore(a,e.firstChild),c.getElementById(d)&&(l.find.ID=function(a,c,d){if(typeof c.getElementById!=="undefined"&&!d){var e=c.getElementById(a[1]);return e?e.id===a[1]||typeof e.getAttributeNode!=="undefined"&&e.getAttributeNode("id").nodeValue===a[1]?[e]:b:[]}},l.filter.ID=function(a,b){var c=typeof a.getAttributeNode!=="undefined"&&a.getAttributeNode("id");return a.nodeType===1&&c&&c.nodeValue===b}),e.removeChild(a),e=a=null}(),function(){var a=c.createElement("div");a.appendChild(c.createComment("")),a.getElementsByTagName("*").length>0&&(l.find.TAG=function(a,b){var c=b.getElementsByTagName(a[1]);if(a[1]==="*"){var d=[];for(var e=0;c[e];e++)c[e].nodeType===1&&d.push(c[e]);c=d}return c}),a.innerHTML="",a.firstChild&&typeof a.firstChild.getAttribute!=="undefined"&&a.firstChild.getAttribute("href")!=="#"&&(l.attrHandle.href=function(a){return a.getAttribute("href",2)}),a=null}(),c.querySelectorAll&&function(){var a=k,b=c.createElement("div"),d="__sizzle__";b.innerHTML="

    ";if(!b.querySelectorAll||b.querySelectorAll(".TEST").length!==0){k=function(b,e,f,g){e=e||c;if(!g&&!k.isXML(e)){var h=/^(\w+$)|^\.([\w\-]+$)|^#([\w\-]+$)/.exec(b);if(h&&(e.nodeType===1||e.nodeType===9)){if(h[1])return p(e.getElementsByTagName(b),f);if(h[2]&&l.find.CLASS&&e.getElementsByClassName)return p(e.getElementsByClassName(h[2]),f)}if(e.nodeType===9){if(b==="body"&&e.body)return p([e.body],f);if(h&&h[3]){var i=e.getElementById(h[3]);if(!i||!i.parentNode)return p([],f);if(i.id===h[3])return p([i],f)}try{return p(e.querySelectorAll(b),f)}catch(j){}}else if(e.nodeType===1&&e.nodeName.toLowerCase()!=="object"){var m=e,n=e.getAttribute("id"),o=n||d,q=e.parentNode,r=/^\s*[+~]/.test(b);n?o=o.replace(/'/g,"\\$&"):e.setAttribute("id",o),r&&q&&(e=e.parentNode);try{if(!r||q)return p(e.querySelectorAll("[id='"+o+"'] "+b),f)}catch(s){}finally{n||m.removeAttribute("id")}}}return a(b,e,f,g)};for(var e in a)k[e]=a[e];b=null}}(),function(){var a=c.documentElement,b=a.matchesSelector||a.mozMatchesSelector||a.webkitMatchesSelector||a.msMatchesSelector,d=!1;try{b.call(c.documentElement,"[test!='']:sizzle")}catch(e){d=!0}b&&(k.matchesSelector=function(a,c){c=c.replace(/\=\s*([^'"\]]*)\s*\]/g,"='$1']");if(!k.isXML(a))try{if(d||!l.match.PSEUDO.test(c)&&!/!=/.test(c))return b.call(a,c)}catch(e){}return k(c,null,null,[a]).length>0})}(),function(){var a=c.createElement("div");a.innerHTML="
    ";if(a.getElementsByClassName&&a.getElementsByClassName("e").length!==0){a.lastChild.className="e";if(a.getElementsByClassName("e").length===1)return;l.order.splice(1,0,"CLASS"),l.find.CLASS=function(a,b,c){if(typeof b.getElementsByClassName!=="undefined"&&!c)return b.getElementsByClassName(a[1])},a=null}}(),c.documentElement.contains?k.contains=function(a,b){return a!==b&&(a.contains?a.contains(b):!0)}:c.documentElement.compareDocumentPosition?k.contains=function(a,b){return!!(a.compareDocumentPosition(b)&16)}:k.contains=function(){return!1},k.isXML=function(a){var b=(a?a.ownerDocument||a:0).documentElement;return b?b.nodeName!=="HTML":!1};var v=function(a,b){var c,d=[],e="",f=b.nodeType?[b]:b;while(c=l.match.PSEUDO.exec(a))e+=c[0],a=a.replace(l.match.PSEUDO,"");a=l.relative[a]?a+"*":a;for(var g=0,h=f.length;g0)for(var g=c;g0},closest:function(a,b){var c=[],e,f,g=this[0];if(d.isArray(a)){var h,i,j={},k=1;if(g&&a.length){for(e=0,f=a.length;e-1:d(g).is(h))&&c.push({selector:i,elem:g,level:k});g=g.parentNode,k++}}return c}var l=L.test(a)?d(a,b||this.context):null;for(e=0,f=this.length;e-1:d.find.matchesSelector(g,a)){c.push(g);break}g=g.parentNode;if(!g||!g.ownerDocument||g===b)break}}c=c.length>1?d.unique(c):c;return this.pushStack(c,"closest",a)},index:function(a){if(!a||typeof a==="string")return d.inArray(this[0],a?d(a):this.parent().children());return d.inArray(a.jquery?a[0]:a,this)},add:function(a,b){var c=typeof a==="string"?d(a,b):d.makeArray(a),e=d.merge(this.get(),c);return this.pushStack(N(c[0])||N(e[0])?e:d.unique(e))},andSelf:function(){return this.add(this.prevObject)}}),d.each({parent:function(a){var b=a.parentNode;return b&&b.nodeType!==11?b:null},parents:function(a){return d.dir(a,"parentNode")},parentsUntil:function(a,b,c){return d.dir(a,"parentNode",c)},next:function(a){return d.nth(a,2,"nextSibling")},prev:function(a){return d.nth(a,2,"previousSibling")},nextAll:function(a){return d.dir(a,"nextSibling")},prevAll:function(a){return d.dir(a,"previousSibling")},nextUntil:function(a,b,c){return d.dir(a,"nextSibling",c)},prevUntil:function(a,b,c){return d.dir(a,"previousSibling",c)},siblings:function(a){return d.sibling(a.parentNode.firstChild,a)},children:function(a){return d.sibling(a.firstChild)},contents:function(a){return d.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:d.makeArray(a.childNodes)}},function(a,b){d.fn[a]=function(c,e){var f=d.map(this,b,c),g=K.call(arguments);G.test(a)||(e=c),e&&typeof e==="string"&&(f=d.filter(e,f)),f=this.length>1&&!M[a]?d.unique(f):f,(this.length>1||I.test(e))&&H.test(a)&&(f=f.reverse());return this.pushStack(f,a,g.join(","))}}),d.extend({filter:function(a,b,c){c&&(a=":not("+a+")");return b.length===1?d.find.matchesSelector(b[0],a)?[b[0]]:[]:d.find.matches(a,b)},dir:function(a,c,e){var f=[],g=a[c];while(g&&g.nodeType!==9&&(e===b||g.nodeType!==1||!d(g).is(e)))g.nodeType===1&&f.push(g),g=g[c];return f},nth:function(a,b,c,d){b=b||1;var e=0;for(;a;a=a[c])if(a.nodeType===1&&++e===b)break;return a},sibling:function(a,b){var c=[];for(;a;a=a.nextSibling)a.nodeType===1&&a!==b&&c.push(a);return c}});var P=/ jQuery\d+="(?:\d+|null)"/g,Q=/^\s+/,R=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig,S=/<([\w:]+)/,T=/",""],legend:[1,"
    ","
    "],thead:[1,"","
    "],tr:[2,"","
    "],td:[3,"","
    "],col:[2,"","
    "],area:[1,"",""],_default:[0,"",""]};X.optgroup=X.option,X.tbody=X.tfoot=X.colgroup=X.caption=X.thead,X.th=X.td,d.support.htmlSerialize||(X._default=[1,"div
    ","
    "]),d.fn.extend({text:function(a){if(d.isFunction(a))return this.each(function(b){var c=d(this);c.text(a.call(this,b,c.text()))});if(typeof a!=="object"&&a!==b)return this.empty().append((this[0]&&this[0].ownerDocument||c).createTextNode(a));return d.text(this)},wrapAll:function(a){if(d.isFunction(a))return this.each(function(b){d(this).wrapAll(a.call(this,b))});if(this[0]){var b=d(a,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&b.insertBefore(this[0]),b.map(function(){var a=this;while(a.firstChild&&a.firstChild.nodeType===1)a=a.firstChild;return a}).append(this)}return this},wrapInner:function(a){if(d.isFunction(a))return this.each(function(b){d(this).wrapInner(a.call(this,b))});return this.each(function(){var b=d(this),c=b.contents();c.length?c.wrapAll(a):b.append(a)})},wrap:function(a){return this.each(function(){d(this).wrapAll(a)})},unwrap:function(){return this.parent().each(function(){d.nodeName(this,"body")||d(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,!0,function(a){this.nodeType===1&&this.appendChild(a)})},prepend:function(){return this.domManip(arguments,!0,function(a){this.nodeType===1&&this.insertBefore(a,this.firstChild)})},before:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this)});if(arguments.length){var a=d(arguments[0]);a.push.apply(a,this.toArray());return this.pushStack(a,"before",arguments)}},after:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this.nextSibling)});if(arguments.length){var a=this.pushStack(this,"after",arguments);a.push.apply(a,d(arguments[0]).toArray());return a}},remove:function(a,b){for(var c=0,e;(e=this[c])!=null;c++)if(!a||d.filter(a,[e]).length)!b&&e.nodeType===1&&(d.cleanData(e.getElementsByTagName("*")),d.cleanData([e])),e.parentNode&&e.parentNode.removeChild(e);return this},empty:function(){for(var a=0,b;(b=this[a])!=null;a++){b.nodeType===1&&d.cleanData(b.getElementsByTagName("*"));while(b.firstChild)b.removeChild(b.firstChild)}return this},clone:function(a,b){a=a==null?!1:a,b=b==null?a:b;return this.map(function(){return d.clone(this,a,b)})},html:function(a){if(a===b)return this[0]&&this[0].nodeType===1?this[0].innerHTML.replace(P,""):null;if(typeof a!=="string"||V.test(a)||!d.support.leadingWhitespace&&Q.test(a)||X[(S.exec(a)||["",""])[1].toLowerCase()])d.isFunction(a)?this.each(function(b){var c=d(this);c.html(a.call(this,b,c.html()))}):this.empty().append(a);else{a=a.replace(R,"<$1>");try{for(var c=0,e=this.length;c1&&l0?this.clone(!0):this).get();d(f[h])[b](j),e=e.concat(j)}return this.pushStack(e,a,f.selector)}}),d.extend({clone:function(a,b,c){var e=a.cloneNode(!0),f,g,h;if((!d.support.noCloneEvent||!d.support.noCloneChecked)&&(a.nodeType===1||a.nodeType===11)&&!d.isXMLDoc(a)){$(a,e),f=_(a),g=_(e);for(h=0;f[h];++h)$(f[h],g[h])}if(b){Z(a,e);if(c){f=_(a),g=_(e);for(h=0;f[h];++h)Z(f[h],g[h])}}return e},clean:function(a,b,e,f){b=b||c,typeof b.createElement==="undefined"&&(b=b.ownerDocument||b[0]&&b[0].ownerDocument||c);var g=[];for(var h=0,i;(i=a[h])!=null;h++){typeof i==="number"&&(i+="");if(!i)continue;if(typeof i!=="string"||U.test(i)){if(typeof i==="string"){i=i.replace(R,"<$1>");var j=(S.exec(i)||["",""])[1].toLowerCase(),k=X[j]||X._default,l=k[0],m=b.createElement("div");m.innerHTML=k[1]+i+k[2];while(l--)m=m.lastChild;if(!d.support.tbody){var n=T.test(i),o=j==="table"&&!n?m.firstChild&&m.firstChild.childNodes:k[1]===""&&!n?m.childNodes:[];for(var p=o.length-1;p>=0;--p)d.nodeName(o[p],"tbody")&&!o[p].childNodes.length&&o[p].parentNode.removeChild(o[p])}!d.support.leadingWhitespace&&Q.test(i)&&m.insertBefore(b.createTextNode(Q.exec(i)[0]),m.firstChild),i=m.childNodes}}else i=b.createTextNode(i);i.nodeType?g.push(i):g=d.merge(g,i)}if(e)for(h=0;g[h];h++)!f||!d.nodeName(g[h],"script")||g[h].type&&g[h].type.toLowerCase()!=="text/javascript"?(g[h].nodeType===1&&g.splice.apply(g,[h+1,0].concat(d.makeArray(g[h].getElementsByTagName("script")))),e.appendChild(g[h])):f.push(g[h].parentNode?g[h].parentNode.removeChild(g[h]):g[h]);return g},cleanData:function(a){var b,c,e=d.cache,f=d.expando,g=d.event.special,h=d.support.deleteExpando;for(var i=0,j;(j=a[i])!=null;i++){if(j.nodeName&&d.noData[j.nodeName.toLowerCase()])continue;c=j[d.expando];if(c){b=e[c]&&e[c][f];if(b&&b.events){for(var k in b.events)g[k]?d.event.remove(j,k):d.removeEvent(j,k,b.handle);b.handle&&(b.handle.elem=null)}h?delete j[d.expando]:j.removeAttribute&&j.removeAttribute(d.expando),delete e[c]}}}});var bb=/alpha\([^)]*\)/i,bc=/opacity=([^)]*)/,bd=/-([a-z])/ig,be=/([A-Z])/g,bf=/^-?\d+(?:px)?$/i,bg=/^-?\d/,bh={position:"absolute",visibility:"hidden",display:"block"},bi=["Left","Right"],bj=["Top","Bottom"],bk,bl,bm,bn=function(a,b){return b.toUpperCase()};d.fn.css=function(a,c){if(arguments.length===2&&c===b)return this;return d.access(this,a,c,!0,function(a,c,e){return e!==b?d.style(a,c,e):d.css(a,c)})},d.extend({cssHooks:{opacity:{get:function(a,b){if(b){var c=bk(a,"opacity","opacity");return c===""?"1":c}return a.style.opacity}}},cssNumber:{zIndex:!0,fontWeight:!0,opacity:!0,zoom:!0,lineHeight:!0},cssProps:{"float":d.support.cssFloat?"cssFloat":"styleFloat"},style:function(a,c,e,f){if(a&&a.nodeType!==3&&a.nodeType!==8&&a.style){var g,h=d.camelCase(c),i=a.style,j=d.cssHooks[h];c=d.cssProps[h]||h;if(e===b){if(j&&"get"in j&&(g=j.get(a,!1,f))!==b)return g;return i[c]}if(typeof e==="number"&&isNaN(e)||e==null)return;typeof e==="number"&&!d.cssNumber[h]&&(e+="px");if(!j||!("set"in j)||(e=j.set(a,e))!==b)try{i[c]=e}catch(k){}}},css:function(a,c,e){var f,g=d.camelCase(c),h=d.cssHooks[g];c=d.cssProps[g]||g;if(h&&"get"in h&&(f=h.get(a,!0,e))!==b)return f;if(bk)return bk(a,c,g)},swap:function(a,b,c){var d={};for(var e in b)d[e]=a.style[e],a.style[e]=b[e];c.call(a);for(e in b)a.style[e]=d[e]},camelCase:function(a){return a.replace(bd,bn)}}),d.curCSS=d.css,d.each(["height","width"],function(a,b){d.cssHooks[b]={get:function(a,c,e){var f;if(c){a.offsetWidth!==0?f=bo(a,b,e):d.swap(a,bh,function(){f=bo(a,b,e)});if(f<=0){f=bk(a,b,b),f==="0px"&&bm&&(f=bm(a,b,b));if(f!=null)return f===""||f==="auto"?"0px":f}if(f<0||f==null){f=a.style[b];return f===""||f==="auto"?"0px":f}return typeof f==="string"?f:f+"px"}},set:function(a,b){if(!bf.test(b))return b;b=parseFloat(b);if(b>=0)return b+"px"}}}),d.support.opacity||(d.cssHooks.opacity={get:function(a,b){return bc.test((b&&a.currentStyle?a.currentStyle.filter:a.style.filter)||"")?parseFloat(RegExp.$1)/100+"":b?"1":""},set:function(a,b){var c=a.style;c.zoom=1;var e=d.isNaN(b)?"":"alpha(opacity="+b*100+")",f=c.filter||"";c.filter=bb.test(f)?f.replace(bb,e):c.filter+" "+e}}),c.defaultView&&c.defaultView.getComputedStyle&&(bl=function(a,c,e){var f,g,h;e=e.replace(be,"-$1").toLowerCase();if(!(g=a.ownerDocument.defaultView))return b;if(h=g.getComputedStyle(a,null))f=h.getPropertyValue(e),f===""&&!d.contains(a.ownerDocument.documentElement,a)&&(f=d.style(a,e));return f}),c.documentElement.currentStyle&&(bm=function(a,b){var c,d=a.currentStyle&&a.currentStyle[b],e=a.runtimeStyle&&a.runtimeStyle[b],f=a.style;!bf.test(d)&&bg.test(d)&&(c=f.left,e&&(a.runtimeStyle.left=a.currentStyle.left),f.left=b==="fontSize"?"1em":d||0,d=f.pixelLeft+"px",f.left=c,e&&(a.runtimeStyle.left=e));return d===""?"auto":d}),bk=bl||bm,d.expr&&d.expr.filters&&(d.expr.filters.hidden=function(a){var b=a.offsetWidth,c=a.offsetHeight;return b===0&&c===0||!d.support.reliableHiddenOffsets&&(a.style.display||d.css(a,"display"))==="none"},d.expr.filters.visible=function(a){return!d.expr.filters.hidden(a)});var bp=/%20/g,bq=/\[\]$/,br=/\r?\n/g,bs=/#.*$/,bt=/^(.*?):[ \t]*([^\r\n]*)\r?$/mg,bu=/^(?:color|date|datetime|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,bv=/(?:^file|^widget|\-extension):$/,bw=/^(?:GET|HEAD)$/,bx=/^\/\//,by=/\?/,bz=/)<[^<]*)*<\/script>/gi,bA=/^(?:select|textarea)/i,bB=/\s+/,bC=/([?&])_=[^&]*/,bD=/(^|\-)([a-z])/g,bE=function(a,b,c){return b+c.toUpperCase()},bF=/^([\w\+\.\-]+:)\/\/([^\/?#:]*)(?::(\d+))?/,bG=d.fn.load,bH={},bI={},bJ,bK;try{bJ=c.location.href}catch(bL){bJ=c.createElement("a"),bJ.href="",bJ=bJ.href}bK=bF.exec(bJ.toLowerCase()),d.fn.extend({load:function(a,c,e){if(typeof a!=="string"&&bG)return bG.apply(this,arguments);if(!this.length)return this;var f=a.indexOf(" ");if(f>=0){var g=a.slice(f,a.length);a=a.slice(0,f)}var h="GET";c&&(d.isFunction(c)?(e=c,c=b):typeof c==="object"&&(c=d.param(c,d.ajaxSettings.traditional),h="POST"));var i=this;d.ajax({url:a,type:h,dataType:"html",data:c,complete:function(a,b,c){c=a.responseText,a.isResolved()&&(a.done(function(a){c=a}),i.html(g?d("
    ").append(c.replace(bz,"")).find(g):c)),e&&i.each(e,[c,b,a])}});return this},serialize:function(){return d.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?d.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||bA.test(this.nodeName)||bu.test(this.type))}).map(function(a,b){var c=d(this).val();return c==null?null:d.isArray(c)?d.map(c,function(a,c){return{name:b.name,value:a.replace(br,"\r\n")}}):{name:b.name,value:c.replace(br,"\r\n")}}).get()}}),d.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),function(a,b){d.fn[b]=function(a){return this.bind(b,a)}}),d.each(["get","post"],function(a,c){d[c]=function(a,e,f,g){d.isFunction(e)&&(g=g||f,f=e,e=b);return d.ajax({type:c,url:a,data:e,success:f,dataType:g})}}),d.extend({getScript:function(a,c){return d.get(a,b,c,"script")},getJSON:function(a,b,c){return d.get(a,b,c,"json")},ajaxSetup:function(a,b){b?d.extend(!0,a,d.ajaxSettings,b):(b=a,a=d.extend(!0,d.ajaxSettings,b));for(var c in {context:1,url:1})c in b?a[c]=b[c]:c in d.ajaxSettings&&(a[c]=d.ajaxSettings[c]);return a},ajaxSettings:{url:bJ,isLocal:bv.test(bK[1]),global:!0,type:"GET",contentType:"application/x-www-form-urlencoded",processData:!0,async:!0,accepts:{xml:"application/xml, text/xml",html:"text/html",text:"text/plain",json:"application/json, text/javascript","*":"*/*"},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText"},converters:{"* text":a.String,"text html":!0,"text json":d.parseJSON,"text xml":d.parseXML}},ajaxPrefilter:bM(bH),ajaxTransport:bM(bI),ajax:function(a,c){function v(a,c,l,n){if(r!==2){r=2,p&&clearTimeout(p),o=b,m=n||"",u.readyState=a?4:0;var q,t,v,w=l?bP(e,u,l):b,x,y;if(a>=200&&a<300||a===304){if(e.ifModified){if(x=u.getResponseHeader("Last-Modified"))d.lastModified[k]=x;if(y=u.getResponseHeader("Etag"))d.etag[k]=y}if(a===304)c="notmodified",q=!0;else try{t=bQ(e,w),c="success",q=!0}catch(z){c="parsererror",v=z}}else{v=c;if(!c||a)c="error",a<0&&(a=0)}u.status=a,u.statusText=c,q?h.resolveWith(f,[t,c,u]):h.rejectWith(f,[u,c,v]),u.statusCode(j),j=b,s&&g.trigger("ajax"+(q?"Success":"Error"),[u,e,q?t:v]),i.resolveWith(f,[u,c]),s&&(g.trigger("ajaxComplete",[u,e]),--d.active||d.event.trigger("ajaxStop"))}}typeof a==="object"&&(c=a,a=b),c=c||{};var e=d.ajaxSetup({},c),f=e.context||e,g=f!==e&&(f.nodeType||f instanceof d)?d(f):d.event,h=d.Deferred(),i=d._Deferred(),j=e.statusCode||{},k,l={},m,n,o,p,q,r=0,s,t,u={readyState:0,setRequestHeader:function(a,b){r||(l[a.toLowerCase().replace(bD,bE)]=b);return this},getAllResponseHeaders:function(){return r===2?m:null},getResponseHeader:function(a){var c;if(r===2){if(!n){n={};while(c=bt.exec(m))n[c[1].toLowerCase()]=c[2]}c=n[a.toLowerCase()]}return c===b?null:c},overrideMimeType:function(a){r||(e.mimeType=a);return this},abort:function(a){a=a||"abort",o&&o.abort(a),v(0,a);return this}};h.promise(u),u.success=u.done,u.error=u.fail,u.complete=i.done,u.statusCode=function(a){if(a){var b;if(r<2)for(b in a)j[b]=[j[b],a[b]];else b=a[u.status],u.then(b,b)}return this},e.url=((a||e.url)+"").replace(bs,"").replace(bx,bK[1]+"//"),e.dataTypes=d.trim(e.dataType||"*").toLowerCase().split(bB),e.crossDomain||(q=bF.exec(e.url.toLowerCase()),e.crossDomain=q&&(q[1]!=bK[1]||q[2]!=bK[2]||(q[3]||(q[1]==="http:"?80:443))!=(bK[3]||(bK[1]==="http:"?80:443)))),e.data&&e.processData&&typeof e.data!=="string"&&(e.data=d.param(e.data,e.traditional)),bN(bH,e,c,u);if(r===2)return!1;s=e.global,e.type=e.type.toUpperCase(),e.hasContent=!bw.test(e.type),s&&d.active++===0&&d.event.trigger("ajaxStart");if(!e.hasContent){e.data&&(e.url+=(by.test(e.url)?"&":"?")+e.data),k=e.url;if(e.cache===!1){var w=d.now(),x=e.url.replace(bC,"$1_="+w);e.url=x+(x===e.url?(by.test(e.url)?"&":"?")+"_="+w:"")}}if(e.data&&e.hasContent&&e.contentType!==!1||c.contentType)l["Content-Type"]=e.contentType;e.ifModified&&(k=k||e.url,d.lastModified[k]&&(l["If-Modified-Since"]=d.lastModified[k]),d.etag[k]&&(l["If-None-Match"]=d.etag[k])),l.Accept=e.dataTypes[0]&&e.accepts[e.dataTypes[0]]?e.accepts[e.dataTypes[0]]+(e.dataTypes[0]!=="*"?", */*; q=0.01":""):e.accepts["*"];for(t in e.headers)u.setRequestHeader(t,e.headers[t]);if(e.beforeSend&&(e.beforeSend.call(f,u,e)===!1||r===2)){u.abort();return!1}for(t in {success:1,error:1,complete:1})u[t](e[t]);o=bN(bI,e,c,u);if(o){u.readyState=1,s&&g.trigger("ajaxSend",[u,e]),e.async&&e.timeout>0&&(p=setTimeout(function(){u.abort("timeout")},e.timeout));try{r=1,o.send(l,v)}catch(y){status<2?v(-1,y):d.error(y)}}else v(-1,"No Transport");return u},param:function(a,c){var e=[],f=function(a,b){b=d.isFunction(b)?b():b,e[e.length]=encodeURIComponent(a)+"="+encodeURIComponent(b)};c===b&&(c=d.ajaxSettings.traditional);if(d.isArray(a)||a.jquery&&!d.isPlainObject(a))d.each(a,function(){f(this.name,this.value)});else for(var g in a)bO(g,a[g],c,f);return e.join("&").replace(bp,"+")}}),d.extend({active:0,lastModified:{},etag:{}});var bR=d.now(),bS=/(\=)\?(&|$)|()\?\?()/i;d.ajaxSetup({jsonp:"callback",jsonpCallback:function(){return d.expando+"_"+bR++}}),d.ajaxPrefilter("json jsonp",function(b,c,e){var f=typeof b.data==="string";if(b.dataTypes[0]==="jsonp"||c.jsonpCallback||c.jsonp!=null||b.jsonp!==!1&&(bS.test(b.url)||f&&bS.test(b.data))){var g,h=b.jsonpCallback=d.isFunction(b.jsonpCallback)?b.jsonpCallback():b.jsonpCallback,i=a[h],j=b.url,k=b.data,l="$1"+h+"$2",m=function(){a[h]=i,g&&d.isFunction(i)&&a[h](g[0])};b.jsonp!==!1&&(j=j.replace(bS,l),b.url===j&&(f&&(k=k.replace(bS,l)),b.data===k&&(j+=(/\?/.test(j)?"&":"?")+b.jsonp+"="+h))),b.url=j,b.data=k,a[h]=function(a){g=[a]},e.then(m,m),b.converters["script json"]=function(){g||d.error(h+" was not called");return g[0]},b.dataTypes[0]="json";return"script"}}),d.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/javascript|ecmascript/},converters:{"text script":function(a){d.globalEval(a);return a}}}),d.ajaxPrefilter("script",function(a){a.cache===b&&(a.cache=!1),a.crossDomain&&(a.type="GET",a.global=!1)}),d.ajaxTransport("script",function(a){if(a.crossDomain){var d,e=c.head||c.getElementsByTagName("head")[0]||c.documentElement;return{send:function(f,g){d=c.createElement("script"),d.async="async",a.scriptCharset&&(d.charset=a.scriptCharset),d.src=a.url,d.onload=d.onreadystatechange=function(a,c){if(!d.readyState||/loaded|complete/.test(d.readyState))d.onload=d.onreadystatechange=null,e&&d.parentNode&&e.removeChild(d),d=b,c||g(200,"success")},e.insertBefore(d,e.firstChild)},abort:function(){d&&d.onload(0,1)}}}});var bT=d.now(),bU,bV;d.ajaxSettings.xhr=a.ActiveXObject?function(){return!this.isLocal&&bX()||bY()}:bX,bV=d.ajaxSettings.xhr(),d.support.ajax=!!bV,d.support.cors=bV&&"withCredentials"in bV,bV=b,d.support.ajax&&d.ajaxTransport(function(a){if(!a.crossDomain||d.support.cors){var c;return{send:function(e,f){var g=a.xhr(),h,i;a.username?g.open(a.type,a.url,a.async,a.username,a.password):g.open(a.type,a.url,a.async);if(a.xhrFields)for(i in a.xhrFields)g[i]=a.xhrFields[i];a.mimeType&&g.overrideMimeType&&g.overrideMimeType(a.mimeType),(!a.crossDomain||a.hasContent)&&!e["X-Requested-With"]&&(e["X-Requested-With"]="XMLHttpRequest");try{for(i in e)g.setRequestHeader(i,e[i])}catch(j){}g.send(a.hasContent&&a.data||null),c=function(e,i){var j,k,l,m,n;try{if(c&&(i||g.readyState===4)){c=b,h&&(g.onreadystatechange=d.noop,delete bU[h]);if(i)g.readyState!==4&&g.abort();else{j=g.status,l=g.getAllResponseHeaders(),m={},n=g.responseXML,n&&n.documentElement&&(m.xml=n),m.text=g.responseText;try{k=g.statusText}catch(o){k=""}j||!a.isLocal||a.crossDomain?j===1223&&(j=204):j=m.text?200:404}}}catch(p){i||f(-1,p)}m&&f(j,k,m,l)},a.async&&g.readyState!==4?(bU||(bU={},bW()),h=bT++,g.onreadystatechange=bU[h]=c):c()},abort:function(){c&&c(0,1)}}}});var bZ={},b$=/^(?:toggle|show|hide)$/,b_=/^([+\-]=)?([\d+.\-]+)([a-z%]*)$/i,ca,cb=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]];d.fn.extend({show:function(a,b,c){var e,f;if(a||a===0)return this.animate(cc("show",3),a,b,c);for(var g=0,h=this.length;g=0;a--)c[a].elem===this&&(b&&c[a](!0),c.splice(a,1))}),b||this.dequeue();return this}}),d.each({slideDown:cc("show",1),slideUp:cc("hide",1),slideToggle:cc("toggle",1),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(a,b){d.fn[a]=function(a,c,d){return this.animate(b,a,c,d)}}),d.extend({speed:function(a,b,c){var e=a&&typeof a==="object"?d.extend({},a):{complete:c||!c&&b||d.isFunction(a)&&a,duration:a,easing:c&&b||b&&!d.isFunction(b)&&b};e.duration=d.fx.off?0:typeof e.duration==="number"?e.duration:e.duration in d.fx.speeds?d.fx.speeds[e.duration]:d.fx.speeds._default,e.old=e.complete,e.complete=function(){e.queue!==!1&&d(this).dequeue(),d.isFunction(e.old)&&e.old.call(this)};return e},easing:{linear:function(a,b,c,d){return c+d*a},swing:function(a,b,c,d){return(-Math.cos(a*Math.PI)/2+.5)*d+c}},timers:[],fx:function(a,b,c){this.options=b,this.elem=a,this.prop=c,b.orig||(b.orig={})}}),d.fx.prototype={update:function(){this.options.step&&this.options.step.call(this.elem,this.now,this),(d.fx.step[this.prop]||d.fx.step._default)(this)},cur:function(){if(this.elem[this.prop]!=null&&(!this.elem.style||this.elem.style[this.prop]==null))return this.elem[this.prop];var a,b=d.css(this.elem,this.prop);return isNaN(a=parseFloat(b))?!b||b==="auto"?0:b:a},custom:function(a,b,c){function g(a){return e.step(a)}var e=this,f=d.fx;this.startTime=d.now(),this.start=a,this.end=b,this.unit=c||this.unit||(d.cssNumber[this.prop]?"":"px"),this.now=this.start,this.pos=this.state=0,g.elem=this.elem,g()&&d.timers.push(g)&&!ca&&(ca=setInterval(f.tick,f.interval))},show:function(){this.options.orig[this.prop]=d.style(this.elem,this.prop),this.options.show=!0,this.custom(this.prop==="width"||this.prop==="height"?1:0,this.cur()),d(this.elem).show()},hide:function(){this.options.orig[this.prop]=d.style(this.elem,this.prop),this.options.hide=!0,this.custom(this.cur(),0)},step:function(a){var b=d.now(),c=!0;if(a||b>=this.options.duration+this.startTime){this.now=this.end,this.pos=this.state=1,this.update(),this.options.curAnim[this.prop]=!0;for(var e in this.options.curAnim)this.options.curAnim[e]!==!0&&(c=!1);if(c){if(this.options.overflow!=null&&!d.support.shrinkWrapBlocks){var f=this.elem,g=this.options;d.each(["","X","Y"],function(a,b){f.style["overflow"+b]=g.overflow[a]})}this.options.hide&&d(this.elem).hide();if(this.options.hide||this.options.show)for(var h in this.options.curAnim)d.style(this.elem,h,this.options.orig[h]);this.options.complete.call(this.elem)}return!1}var i=b-this.startTime;this.state=i/this.options.duration;var j=this.options.specialEasing&&this.options.specialEasing[this.prop],k=this.options.easing||(d.easing.swing?"swing":"linear");this.pos=d.easing[j||k](this.state,i,0,1,this.options.duration),this.now=this.start+(this.end-this.start)*this.pos,this.update();return!0}},d.extend(d.fx,{tick:function(){var a=d.timers;for(var b=0;b
    ";d.extend(b.style,{position:"absolute",top:0,left:0,margin:0,border:0,width:"1px",height:"1px",visibility:"hidden"}),b.innerHTML=j,a.insertBefore(b,a.firstChild),e=b.firstChild,f=e.firstChild,h=e.nextSibling.firstChild.firstChild,this.doesNotAddBorder=f.offsetTop!==5,this.doesAddBorderForTableAndCells=h.offsetTop===5,f.style.position="fixed",f.style.top="20px",this.supportsFixedPosition=f.offsetTop===20||f.offsetTop===15,f.style.position=f.style.top="",e.style.overflow="hidden",e.style.position="relative",this.subtractsBorderForOverflowNotVisible=f.offsetTop===-5,this.doesNotIncludeMarginInBodyOffset=a.offsetTop!==i,a.removeChild(b),a=b=e=f=g=h=null,d.offset.initialize=d.noop},bodyOffset:function(a){var b=a.offsetTop,c=a.offsetLeft;d.offset.initialize(),d.offset.doesNotIncludeMarginInBodyOffset&&(b+=parseFloat(d.css(a,"marginTop"))||0,c+=parseFloat(d.css(a,"marginLeft"))||0);return{top:b,left:c}},setOffset:function(a,b,c){var e=d.css(a,"position");e==="static"&&(a.style.position="relative");var f=d(a),g=f.offset(),h=d.css(a,"top"),i=d.css(a,"left"),j=e==="absolute"&&d.inArray("auto",[h,i])>-1,k={},l={},m,n;j&&(l=f.position()),m=j?l.top:parseInt(h,10)||0,n=j?l.left:parseInt(i,10)||0,d.isFunction(b)&&(b=b.call(a,c,g)),b.top!=null&&(k.top=b.top-g.top+m),b.left!=null&&(k.left=b.left-g.left+n),"using"in b?b.using.call(a,k):f.css(k)}},d.fn.extend({position:function(){if(!this[0])return null;var a=this[0],b=this.offsetParent(),c=this.offset(),e=cf.test(b[0].nodeName)?{top:0,left:0}:b.offset();c.top-=parseFloat(d.css(a,"marginTop"))||0,c.left-=parseFloat(d.css(a,"marginLeft"))||0,e.top+=parseFloat(d.css(b[0],"borderTopWidth"))||0,e.left+=parseFloat(d.css(b[0],"borderLeftWidth"))||0;return{top:c.top-e.top,left:c.left-e.left}},offsetParent:function(){return this.map(function(){var a=this.offsetParent||c.body;while(a&&(!cf.test(a.nodeName)&&d.css(a,"position")==="static"))a=a.offsetParent;return a})}}),d.each(["Left","Top"],function(a,c){var e="scroll"+c;d.fn[e]=function(c){var f=this[0],g;if(!f)return null;if(c!==b)return this.each(function(){g=cg(this),g?g.scrollTo(a?d(g).scrollLeft():c,a?c:d(g).scrollTop()):this[e]=c});g=cg(f);return g?"pageXOffset"in g?g[a?"pageYOffset":"pageXOffset"]:d.support.boxModel&&g.document.documentElement[e]||g.document.body[e]:f[e]}}),d.each(["Height","Width"],function(a,c){var e=c.toLowerCase();d.fn["inner"+c]=function(){return this[0]?parseFloat(d.css(this[0],e,"padding")):null},d.fn["outer"+c]=function(a){return this[0]?parseFloat(d.css(this[0],e,a?"margin":"border")):null},d.fn[e]=function(a){var f=this[0];if(!f)return a==null?null:this;if(d.isFunction(a))return this.each(function(b){var c=d(this);c[e](a.call(this,b,c[e]()))});if(d.isWindow(f)){var g=f.document.documentElement["client"+c];return f.document.compatMode==="CSS1Compat"&&g||f.document.body["client"+c]||g}if(f.nodeType===9)return Math.max(f.documentElement["client"+c],f.body["scroll"+c],f.documentElement["scroll"+c],f.body["offset"+c],f.documentElement["offset"+c]);if(a===b){var h=d.css(f,e),i=parseFloat(h);return d.isNaN(i)?h:i}return this.css(e,typeof a==="string"?a:a+"px")}}),a.jQuery=a.$=d})(window); \ No newline at end of file diff --git a/webapp/src/main/webapp/js/jquery_plugins/jcrop/jquery.Jcrop.css b/webapp/src/main/webapp/js/jquery_plugins/jcrop/jquery.Jcrop.css index 2c5934e25..31636fee2 100644 --- a/webapp/src/main/webapp/js/jquery_plugins/jcrop/jquery.Jcrop.css +++ b/webapp/src/main/webapp/js/jquery_plugins/jcrop/jquery.Jcrop.css @@ -1,40 +1,165 @@ -/* Fixes issue here http://code.google.com/p/jcrop/issues/detail?id=1 */ -.jcrop-holder { text-align: left; } +/* jquery.Jcrop.css v0.9.12 - MIT License */ +/* + The outer-most container in a typical Jcrop instance + If you are having difficulty with formatting related to styles + on a parent element, place any fixes here or in a like selector -.jcrop-vline, .jcrop-hline -{ + You can also style this element if you want to add a border, etc + A better method for styling can be seen below with .jcrop-light + (Add a class to the holder and style elements for that extended class) +*/ +.jcrop-holder { + direction: ltr; + text-align: left; +} +/* Selection Border */ +.jcrop-vline, +.jcrop-hline { + background: #ffffff url("Jcrop.gif"); font-size: 0; position: absolute; - background: white url('Jcrop.gif') top left repeat; } -.jcrop-vline { height: 100%; width: 1px !important; } -.jcrop-hline { width: 100%; height: 1px !important; } +.jcrop-vline { + height: 100%; + width: 1px !important; +} +.jcrop-vline.right { + right: 0; +} +.jcrop-hline { + height: 1px !important; + width: 100%; +} +.jcrop-hline.bottom { + bottom: 0; +} +/* Invisible click targets */ +.jcrop-tracker { + height: 100%; + width: 100%; + /* "turn off" link highlight */ + -webkit-tap-highlight-color: transparent; + /* disable callout, image save panel */ + -webkit-touch-callout: none; + /* disable cut copy paste */ + -webkit-user-select: none; +} +/* Selection Handles */ .jcrop-handle { + background-color: #333333; + border: 1px #eeeeee solid; + width: 7px; + height: 7px; font-size: 1px; - width: 7px !important; - height: 7px !important; - border: 1px #eee solid; - background-color: #333; - *width: 9px; - *height: 9px; } - -.jcrop-tracker { width: 100%; height: 100%; } - -.custom .jcrop-vline, -.custom .jcrop-hline -{ - background: yellow; +.jcrop-handle.ord-n { + left: 50%; + margin-left: -4px; + margin-top: -4px; + top: 0; } -.custom .jcrop-handle -{ - border-color: black; - background-color: #C7BB00; +.jcrop-handle.ord-s { + bottom: 0; + left: 50%; + margin-bottom: -4px; + margin-left: -4px; +} +.jcrop-handle.ord-e { + margin-right: -4px; + margin-top: -4px; + right: 0; + top: 50%; +} +.jcrop-handle.ord-w { + left: 0; + margin-left: -4px; + margin-top: -4px; + top: 50%; +} +.jcrop-handle.ord-nw { + left: 0; + margin-left: -4px; + margin-top: -4px; + top: 0; +} +.jcrop-handle.ord-ne { + margin-right: -4px; + margin-top: -4px; + right: 0; + top: 0; +} +.jcrop-handle.ord-se { + bottom: 0; + margin-bottom: -4px; + margin-right: -4px; + right: 0; +} +.jcrop-handle.ord-sw { + bottom: 0; + left: 0; + margin-bottom: -4px; + margin-left: -4px; +} +/* Dragbars */ +.jcrop-dragbar.ord-n, +.jcrop-dragbar.ord-s { + height: 7px; + width: 100%; +} +.jcrop-dragbar.ord-e, +.jcrop-dragbar.ord-w { + height: 100%; + width: 7px; +} +.jcrop-dragbar.ord-n { + margin-top: -4px; +} +.jcrop-dragbar.ord-s { + bottom: 0; + margin-bottom: -4px; +} +.jcrop-dragbar.ord-e { + margin-right: -4px; + right: 0; +} +.jcrop-dragbar.ord-w { + margin-left: -4px; +} +/* The "jcrop-light" class/extension */ +.jcrop-light .jcrop-vline, +.jcrop-light .jcrop-hline { + background: #ffffff; + filter: alpha(opacity=70) !important; + opacity: .70!important; +} +.jcrop-light .jcrop-handle { -moz-border-radius: 3px; -webkit-border-radius: 3px; + background-color: #000000; + border-color: #ffffff; + border-radius: 3px; } - - - - - +/* The "jcrop-dark" class/extension */ +.jcrop-dark .jcrop-vline, +.jcrop-dark .jcrop-hline { + background: #000000; + filter: alpha(opacity=70) !important; + opacity: 0.7 !important; +} +.jcrop-dark .jcrop-handle { + -moz-border-radius: 3px; + -webkit-border-radius: 3px; + background-color: #ffffff; + border-color: #000000; + border-radius: 3px; +} +/* Simple macro to turn off the antlines */ +.solid-line .jcrop-vline, +.solid-line .jcrop-hline { + background: #ffffff; +} +/* Fix for twitter bootstrap et al. */ +.jcrop-holder img, +img.jcrop-preview { + max-width: none; +} \ No newline at end of file diff --git a/webapp/src/main/webapp/js/jquery_plugins/jcrop/jquery.Jcrop.js b/webapp/src/main/webapp/js/jquery_plugins/jcrop/jquery.Jcrop.js index ad261f97a..7eaaaba98 100644 --- a/webapp/src/main/webapp/js/jquery_plugins/jcrop/jquery.Jcrop.js +++ b/webapp/src/main/webapp/js/jquery_plugins/jcrop/jquery.Jcrop.js @@ -1,8 +1,9 @@ /** - * jquery.Jcrop.js v0.9.8 - * jQuery Image Cropping Plugin - * @author Kelly Hallman - * Copyright (c) 2008-2009 Kelly Hallman - released under MIT License {{{ + * jquery.Jcrop.js v0.9.12 + * jQuery Image Cropping Plugin - released under MIT License + * Author: Kelly Hallman + * http://github.com/tapmodo/Jcrop + * Copyright (c) 2008-2013 Tapmodo Interactive LLC {{{ * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation @@ -12,10 +13,10 @@ * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: - + * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. - + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND @@ -24,1174 +25,1670 @@ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. - + * * }}} */ -(function($) { - -$.Jcrop = function(obj,opt) -{ - // Initialization {{{ - - // Sanitize some options {{{ - var obj = obj, opt = opt; - - if (typeof(obj) !== 'object') obj = $(obj)[0]; - if (typeof(opt) !== 'object') opt = { }; - - // Some on-the-fly fixes for MSIE...sigh - if (!('trackDocument' in opt)) - { - opt.trackDocument = $.browser.msie ? false : true; - if ($.browser.msie && $.browser.version.split('.')[0] == '8') - opt.trackDocument = true; - } - - if (!('keySupport' in opt)) - opt.keySupport = $.browser.msie ? false : true; - - // }}} - // Extend the default options {{{ - var defaults = { - - // Basic Settings - trackDocument: false, - baseClass: 'jcrop', - addClass: null, - - // Styling Options - bgColor: 'black', - bgOpacity: .6, - borderOpacity: .4, - handleOpacity: .5, - - handlePad: 5, - handleSize: 9, - handleOffset: 5, - edgeMargin: 14, - - aspectRatio: 0, - keySupport: true, - cornerHandles: true, - sideHandles: true, - drawBorders: true, - dragEdges: true, - - boxWidth: 0, - boxHeight: 0, - - boundary: 8, - animationDelay: 20, - swingSpeed: 3, - - allowSelect: true, - allowMove: true, - allowResize: true, - - minSelect: [ 0, 0 ], - maxSize: [ 0, 0 ], - minSize: [ 0, 0 ], - - // Callbacks / Event Handlers - onChange: function() { }, - onSelect: function() { } - - }; - var options = defaults; - setOptions(opt); - - // }}} - // Initialize some jQuery objects {{{ - - var $origimg = $(obj); - var $img = $origimg.clone().removeAttr('id').css({ position: 'absolute' }); - - $img.width($origimg.width()); - $img.height($origimg.height()); - $origimg.after($img).hide(); - - presize($img,options.boxWidth,options.boxHeight); - - var boundx = $img.width(), - boundy = $img.height(), - - $div = $('
    ') - .width(boundx).height(boundy) - .addClass(cssClass('holder')) - .css({ - position: 'relative', - backgroundColor: options.bgColor - }).insertAfter($origimg).append($img); - ; - - if (options.addClass) $div.addClass(options.addClass); - //$img.wrap($div); - - var $img2 = $('')/*{{{*/ - .attr('src',$img.attr('src')) - .css('position','absolute') - .width(boundx).height(boundy) - ;/*}}}*/ - var $img_holder = $('
    ')/*{{{*/ - .width(pct(100)).height(pct(100)) - .css({ - zIndex: 310, - position: 'absolute', - overflow: 'hidden' - }) - .append($img2) - ;/*}}}*/ - var $hdl_holder = $('
    ')/*{{{*/ - .width(pct(100)).height(pct(100)) - .css('zIndex',320); - /*}}}*/ - var $sel = $('
    ')/*{{{*/ - .css({ - position: 'absolute', - zIndex: 300 - }) - .insertBefore($img) - .append($img_holder,$hdl_holder) - ;/*}}}*/ - - var bound = options.boundary; - var $trk = newTracker().width(boundx+(bound*2)).height(boundy+(bound*2)) - .css({ position: 'absolute', top: px(-bound), left: px(-bound), zIndex: 290 }) - .mousedown(newSelection); - - /* }}} */ - // Set more variables {{{ - - var xlimit, ylimit, xmin, ymin; - var xscale, yscale, enabled = true; - var docOffset = getPos($img), - // Internal states - btndown, lastcurs, dimmed, animating, - shift_down; - - // }}} - - - // }}} - // Internal Modules {{{ - - var Coords = function()/*{{{*/ - { - var x1 = 0, y1 = 0, x2 = 0, y2 = 0, ox, oy; - - function setPressed(pos)/*{{{*/ - { - var pos = rebound(pos); - x2 = x1 = pos[0]; - y2 = y1 = pos[1]; - }; - /*}}}*/ - function setCurrent(pos)/*{{{*/ - { - var pos = rebound(pos); - ox = pos[0] - x2; - oy = pos[1] - y2; - x2 = pos[0]; - y2 = pos[1]; - }; - /*}}}*/ - function getOffset()/*{{{*/ - { - return [ ox, oy ]; - }; - /*}}}*/ - function moveOffset(offset)/*{{{*/ - { - var ox = offset[0], oy = offset[1]; - - if (0 > x1 + ox) ox -= ox + x1; - if (0 > y1 + oy) oy -= oy + y1; - - if (boundy < y2 + oy) oy += boundy - (y2 + oy); - if (boundx < x2 + ox) ox += boundx - (x2 + ox); - - x1 += ox; - x2 += ox; - y1 += oy; - y2 += oy; - }; - /*}}}*/ - function getCorner(ord)/*{{{*/ - { - var c = getFixed(); - switch(ord) - { - case 'ne': return [ c.x2, c.y ]; - case 'nw': return [ c.x, c.y ]; - case 'se': return [ c.x2, c.y2 ]; - case 'sw': return [ c.x, c.y2 ]; - } - }; - /*}}}*/ - function getFixed()/*{{{*/ - { - if (!options.aspectRatio) return getRect(); - // This function could use some optimization I think... - var aspect = options.aspectRatio, - min_x = options.minSize[0]/xscale, - min_y = options.minSize[1]/yscale, - max_x = options.maxSize[0]/xscale, - max_y = options.maxSize[1]/yscale, - rw = x2 - x1, - rh = y2 - y1, - rwa = Math.abs(rw), - rha = Math.abs(rh), - real_ratio = rwa / rha, - xx, yy - ; - if (max_x == 0) { max_x = boundx * 10 } - if (max_y == 0) { max_y = boundy * 10 } - if (real_ratio < aspect) - { - yy = y2; - w = rha * aspect; - xx = rw < 0 ? x1 - w : w + x1; - - if (xx < 0) - { - xx = 0; - h = Math.abs((xx - x1) / aspect); - yy = rh < 0 ? y1 - h: h + y1; - } - else if (xx > boundx) - { - xx = boundx; - h = Math.abs((xx - x1) / aspect); - yy = rh < 0 ? y1 - h : h + y1; - } - } - else - { - xx = x2; - h = rwa / aspect; - yy = rh < 0 ? y1 - h : y1 + h; - if (yy < 0) - { - yy = 0; - w = Math.abs((yy - y1) * aspect); - xx = rw < 0 ? x1 - w : w + x1; - } - else if (yy > boundy) - { - yy = boundy; - w = Math.abs(yy - y1) * aspect; - xx = rw < 0 ? x1 - w : w + x1; - } - } - - // Magic %-) - if(xx > x1) { // right side - if(xx - x1 < min_x) { - xx = x1 + min_x; - } else if (xx - x1 > max_x) { - xx = x1 + max_x; - } - if(yy > y1) { - yy = y1 + (xx - x1)/aspect; - } else { - yy = y1 - (xx - x1)/aspect; - } - } else if (xx < x1) { // left side - if(x1 - xx < min_x) { - xx = x1 - min_x - } else if (x1 - xx > max_x) { - xx = x1 - max_x; - } - if(yy > y1) { - yy = y1 + (x1 - xx)/aspect; - } else { - yy = y1 - (x1 - xx)/aspect; - } - } - - if(xx < 0) { - x1 -= xx; - xx = 0; - } else if (xx > boundx) { - x1 -= xx - boundx; - xx = boundx; - } - - if(yy < 0) { - y1 -= yy; - yy = 0; - } else if (yy > boundy) { - y1 -= yy - boundy; - yy = boundy; - } - - return last = makeObj(flipCoords(x1,y1,xx,yy)); - }; - /*}}}*/ - function rebound(p)/*{{{*/ - { - if (p[0] < 0) p[0] = 0; - if (p[1] < 0) p[1] = 0; - - if (p[0] > boundx) p[0] = boundx; - if (p[1] > boundy) p[1] = boundy; - - return [ p[0], p[1] ]; - }; - /*}}}*/ - function flipCoords(x1,y1,x2,y2)/*{{{*/ - { - var xa = x1, xb = x2, ya = y1, yb = y2; - if (x2 < x1) - { - xa = x2; - xb = x1; - } - if (y2 < y1) - { - ya = y2; - yb = y1; - } - return [ Math.round(xa), Math.round(ya), Math.round(xb), Math.round(yb) ]; - }; - /*}}}*/ - function getRect()/*{{{*/ - { - var xsize = x2 - x1; - var ysize = y2 - y1; - - if (xlimit && (Math.abs(xsize) > xlimit)) - x2 = (xsize > 0) ? (x1 + xlimit) : (x1 - xlimit); - if (ylimit && (Math.abs(ysize) > ylimit)) - y2 = (ysize > 0) ? (y1 + ylimit) : (y1 - ylimit); - - if (ymin && (Math.abs(ysize) < ymin)) - y2 = (ysize > 0) ? (y1 + ymin) : (y1 - ymin); - if (xmin && (Math.abs(xsize) < xmin)) - x2 = (xsize > 0) ? (x1 + xmin) : (x1 - xmin); - - if (x1 < 0) { x2 -= x1; x1 -= x1; } - if (y1 < 0) { y2 -= y1; y1 -= y1; } - if (x2 < 0) { x1 -= x2; x2 -= x2; } - if (y2 < 0) { y1 -= y2; y2 -= y2; } - if (x2 > boundx) { var delta = x2 - boundx; x1 -= delta; x2 -= delta; } - if (y2 > boundy) { var delta = y2 - boundy; y1 -= delta; y2 -= delta; } - if (x1 > boundx) { var delta = x1 - boundy; y2 -= delta; y1 -= delta; } - if (y1 > boundy) { var delta = y1 - boundy; y2 -= delta; y1 -= delta; } - - return makeObj(flipCoords(x1,y1,x2,y2)); - }; - /*}}}*/ - function makeObj(a)/*{{{*/ - { - return { x: a[0], y: a[1], x2: a[2], y2: a[3], - w: a[2] - a[0], h: a[3] - a[1] }; - }; - /*}}}*/ - - return { - flipCoords: flipCoords, - setPressed: setPressed, - setCurrent: setCurrent, - getOffset: getOffset, - moveOffset: moveOffset, - getCorner: getCorner, - getFixed: getFixed - }; - }(); - - /*}}}*/ - var Selection = function()/*{{{*/ - { - var start, end, dragmode, awake, hdep = 370; - var borders = { }; - var handle = { }; - var seehandles = false; - var hhs = options.handleOffset; - - /* Insert draggable elements {{{*/ - - // Insert border divs for outline - if (options.drawBorders) { - borders = { - top: insertBorder('hline') - .css('top',$.browser.msie?px(-1):px(0)), - bottom: insertBorder('hline'), - left: insertBorder('vline'), - right: insertBorder('vline') - }; - } - - // Insert handles on edges - if (options.dragEdges) { - handle.t = insertDragbar('n'); - handle.b = insertDragbar('s'); - handle.r = insertDragbar('e'); - handle.l = insertDragbar('w'); - } - - // Insert side handles - options.sideHandles && - createHandles(['n','s','e','w']); - - // Insert corner handles - options.cornerHandles && - createHandles(['sw','nw','ne','se']); - - /*}}}*/ - // Private Methods - function insertBorder(type)/*{{{*/ - { - var jq = $('
    ') - .css({position: 'absolute', opacity: options.borderOpacity }) - .addClass(cssClass(type)); - $img_holder.append(jq); - return jq; - }; - /*}}}*/ - function dragDiv(ord,zi)/*{{{*/ - { - var jq = $('
    ') - .mousedown(createDragger(ord)) - .css({ - cursor: ord+'-resize', - position: 'absolute', - zIndex: zi - }) - ; - $hdl_holder.append(jq); - return jq; - }; - /*}}}*/ - function insertHandle(ord)/*{{{*/ - { - return dragDiv(ord,hdep++) - .css({ top: px(-hhs+1), left: px(-hhs+1), opacity: options.handleOpacity }) - .addClass(cssClass('handle')); - }; - /*}}}*/ - function insertDragbar(ord)/*{{{*/ - { - var s = options.handleSize, - o = hhs, - h = s, w = s, - t = o, l = o; - - switch(ord) - { - case 'n': case 's': w = pct(100); break; - case 'e': case 'w': h = pct(100); break; - } - - return dragDiv(ord,hdep++).width(w).height(h) - .css({ top: px(-t+1), left: px(-l+1)}); - }; - /*}}}*/ - function createHandles(li)/*{{{*/ - { - for(i in li) handle[li[i]] = insertHandle(li[i]); - }; - /*}}}*/ - function moveHandles(c)/*{{{*/ - { - var midvert = Math.round((c.h / 2) - hhs), - midhoriz = Math.round((c.w / 2) - hhs), - north = west = -hhs+1, - east = c.w - hhs, - south = c.h - hhs, - x, y; - - 'e' in handle && - handle.e.css({ top: px(midvert), left: px(east) }) && - handle.w.css({ top: px(midvert) }) && - handle.s.css({ top: px(south), left: px(midhoriz) }) && - handle.n.css({ left: px(midhoriz) }); - - 'ne' in handle && - handle.ne.css({ left: px(east) }) && - handle.se.css({ top: px(south), left: px(east) }) && - handle.sw.css({ top: px(south) }); - - 'b' in handle && - handle.b.css({ top: px(south) }) && - handle.r.css({ left: px(east) }); - }; - /*}}}*/ - function moveto(x,y)/*{{{*/ - { - $img2.css({ top: px(-y), left: px(-x) }); - $sel.css({ top: px(y), left: px(x) }); - }; - /*}}}*/ - function resize(w,h)/*{{{*/ - { - $sel.width(w).height(h); - }; - /*}}}*/ - function refresh()/*{{{*/ - { - var c = Coords.getFixed(); - - Coords.setPressed([c.x,c.y]); - Coords.setCurrent([c.x2,c.y2]); - - updateVisible(); - }; - /*}}}*/ - - // Internal Methods - function updateVisible()/*{{{*/ - { if (awake) return update(); }; - /*}}}*/ - function update()/*{{{*/ - { - var c = Coords.getFixed(); - - resize(c.w,c.h); - moveto(c.x,c.y); - - options.drawBorders && - borders['right'].css({ left: px(c.w-1) }) && - borders['bottom'].css({ top: px(c.h-1) }); - - seehandles && moveHandles(c); - awake || show(); - - options.onChange(unscale(c)); - }; - /*}}}*/ - function show()/*{{{*/ - { - $sel.show(); - $img.css('opacity',options.bgOpacity); - awake = true; - }; - /*}}}*/ - function release()/*{{{*/ - { - disableHandles(); - $sel.hide(); - $img.css('opacity',1); - awake = false; - }; - /*}}}*/ - function showHandles()//{{{ - { - if (seehandles) - { - moveHandles(Coords.getFixed()); - $hdl_holder.show(); - } - }; - //}}} - function enableHandles()/*{{{*/ - { - seehandles = true; - if (options.allowResize) - { - moveHandles(Coords.getFixed()); - $hdl_holder.show(); - return true; - } - }; - /*}}}*/ - function disableHandles()/*{{{*/ - { - seehandles = false; - $hdl_holder.hide(); - }; - /*}}}*/ - function animMode(v)/*{{{*/ - { - (animating = v) ? disableHandles(): enableHandles(); - }; - /*}}}*/ - function done()/*{{{*/ - { - animMode(false); - refresh(); - }; - /*}}}*/ - - var $track = newTracker().mousedown(createDragger('move')) - .css({ cursor: 'move', position: 'absolute', zIndex: 360 }) - - $img_holder.append($track); - disableHandles(); - - return { - updateVisible: updateVisible, - update: update, - release: release, - refresh: refresh, - setCursor: function (cursor) { $track.css('cursor',cursor); }, - enableHandles: enableHandles, - enableOnly: function() { seehandles = true; }, - showHandles: showHandles, - disableHandles: disableHandles, - animMode: animMode, - done: done - }; - }(); - /*}}}*/ - var Tracker = function()/*{{{*/ - { - var onMove = function() { }, - onDone = function() { }, - trackDoc = options.trackDocument; - - if (!trackDoc) - { - $trk - .mousemove(trackMove) - .mouseup(trackUp) - .mouseout(trackUp) - ; - } - - function toFront()/*{{{*/ - { - $trk.css({zIndex:450}); - if (trackDoc) - { - $(document) - .mousemove(trackMove) - .mouseup(trackUp) - ; - } - } - /*}}}*/ - function toBack()/*{{{*/ - { - $trk.css({zIndex:290}); - if (trackDoc) - { - $(document) - .unbind('mousemove',trackMove) - .unbind('mouseup',trackUp) - ; - } - } - /*}}}*/ - function trackMove(e)/*{{{*/ - { - onMove(mouseAbs(e)); - }; - /*}}}*/ - function trackUp(e)/*{{{*/ - { - e.preventDefault(); - e.stopPropagation(); - - if (btndown) - { - btndown = false; - - onDone(mouseAbs(e)); - options.onSelect(unscale(Coords.getFixed())); - toBack(); - onMove = function() { }; - onDone = function() { }; - } - - return false; - }; - /*}}}*/ - - function activateHandlers(move,done)/* {{{ */ - { - btndown = true; - onMove = move; - onDone = done; - toFront(); - return false; - }; +(function ($) { + + $.Jcrop = function (obj, opt) { + var options = $.extend({}, $.Jcrop.defaults), + docOffset, + _ua = navigator.userAgent.toLowerCase(), + is_msie = /msie/.test(_ua), + ie6mode = /msie [1-6]\./.test(_ua); + + // Internal Methods {{{ + function px(n) { + return Math.round(n) + 'px'; + } + function cssClass(cl) { + return options.baseClass + '-' + cl; + } + function supportsColorFade() { + return $.fx.step.hasOwnProperty('backgroundColor'); + } + function getPos(obj) //{{{ + { + var pos = $(obj).offset(); + return [pos.left, pos.top]; + } + //}}} + function mouseAbs(e) //{{{ + { + return [(e.pageX - docOffset[0]), (e.pageY - docOffset[1])]; + } + //}}} + function setOptions(opt) //{{{ + { + if (typeof(opt) !== 'object') opt = {}; + options = $.extend(options, opt); + + $.each(['onChange','onSelect','onRelease','onDblClick'],function(i,e) { + if (typeof(options[e]) !== 'function') options[e] = function () {}; + }); + } + //}}} + function startDragMode(mode, pos, touch) //{{{ + { + docOffset = getPos($img); + Tracker.setCursor(mode === 'move' ? mode : mode + '-resize'); + + if (mode === 'move') { + return Tracker.activateHandlers(createMover(pos), doneSelect, touch); + } + + var fc = Coords.getFixed(); + var opp = oppLockCorner(mode); + var opc = Coords.getCorner(oppLockCorner(opp)); + + Coords.setPressed(Coords.getCorner(opp)); + Coords.setCurrent(opc); + + Tracker.activateHandlers(dragmodeHandler(mode, fc), doneSelect, touch); + } + //}}} + function dragmodeHandler(mode, f) //{{{ + { + return function (pos) { + if (!options.aspectRatio) { + switch (mode) { + case 'e': + pos[1] = f.y2; + break; + case 'w': + pos[1] = f.y2; + break; + case 'n': + pos[0] = f.x2; + break; + case 's': + pos[0] = f.x2; + break; + } + } else { + switch (mode) { + case 'e': + pos[1] = f.y + 1; + break; + case 'w': + pos[1] = f.y + 1; + break; + case 'n': + pos[0] = f.x + 1; + break; + case 's': + pos[0] = f.x + 1; + break; + } + } + Coords.setCurrent(pos); + Selection.update(); + }; + } + //}}} + function createMover(pos) //{{{ + { + var lloc = pos; + KeyManager.watchKeys(); + + return function (pos) { + Coords.moveOffset([pos[0] - lloc[0], pos[1] - lloc[1]]); + lloc = pos; + + Selection.update(); + }; + } + //}}} + function oppLockCorner(ord) //{{{ + { + switch (ord) { + case 'n': + return 'sw'; + case 's': + return 'nw'; + case 'e': + return 'nw'; + case 'w': + return 'ne'; + case 'ne': + return 'sw'; + case 'nw': + return 'se'; + case 'se': + return 'nw'; + case 'sw': + return 'ne'; + } + } + //}}} + function createDragger(ord) //{{{ + { + return function (e) { + if (options.disabled) { + return false; + } + if ((ord === 'move') && !options.allowMove) { + return false; + } + + // Fix position of crop area when dragged the very first time. + // Necessary when crop image is in a hidden element when page is loaded. + docOffset = getPos($img); + + btndown = true; + startDragMode(ord, mouseAbs(e)); + e.stopPropagation(); + e.preventDefault(); + return false; + }; + } + //}}} + function presize($obj, w, h) //{{{ + { + var nw = $obj.width(), + nh = $obj.height(); + if ((nw > w) && w > 0) { + nw = w; + nh = (w / $obj.width()) * $obj.height(); + } + if ((nh > h) && h > 0) { + nh = h; + nw = (h / $obj.height()) * $obj.width(); + } + xscale = $obj.width() / nw; + yscale = $obj.height() / nh; + $obj.width(nw).height(nh); + } + //}}} + function unscale(c) //{{{ + { + return { + x: c.x * xscale, + y: c.y * yscale, + x2: c.x2 * xscale, + y2: c.y2 * yscale, + w: c.w * xscale, + h: c.h * yscale + }; + } + //}}} + function doneSelect(pos) //{{{ + { + var c = Coords.getFixed(); + if ((c.w > options.minSelect[0]) && (c.h > options.minSelect[1])) { + Selection.enableHandles(); + Selection.done(); + } else { + Selection.release(); + } + Tracker.setCursor(options.allowSelect ? 'crosshair' : 'default'); + } + //}}} + function newSelection(e) //{{{ + { + if (options.disabled) { + return false; + } + if (!options.allowSelect) { + return false; + } + btndown = true; + docOffset = getPos($img); + Selection.disableHandles(); + Tracker.setCursor('crosshair'); + var pos = mouseAbs(e); + Coords.setPressed(pos); + Selection.update(); + Tracker.activateHandlers(selectDrag, doneSelect, e.type.substring(0,5)==='touch'); + KeyManager.watchKeys(); + + e.stopPropagation(); + e.preventDefault(); + return false; + } + //}}} + function selectDrag(pos) //{{{ + { + Coords.setCurrent(pos); + Selection.update(); + } + //}}} + function newTracker() //{{{ + { + var trk = $('
    ').addClass(cssClass('tracker')); + if (is_msie) { + trk.css({ + opacity: 0, + backgroundColor: 'white' + }); + } + return trk; + } + //}}} + + // }}} + // Initialization {{{ + // Sanitize some options {{{ + if (typeof(obj) !== 'object') { + obj = $(obj)[0]; + } + if (typeof(opt) !== 'object') { + opt = {}; + } + // }}} + setOptions(opt); + // Initialize some jQuery objects {{{ + // The values are SET on the image(s) for the interface + // If the original image has any of these set, they will be reset + // However, if you destroy() the Jcrop instance the original image's + // character in the DOM will be as you left it. + var img_css = { + border: 'none', + visibility: 'visible', + margin: 0, + padding: 0, + position: 'absolute', + top: 0, + left: 0 + }; + + var $origimg = $(obj), + img_mode = true; + + if (obj.tagName == 'IMG') { + // Fix size of crop image. + // Necessary when crop image is within a hidden element when page is loaded. + if ($origimg[0].width != 0 && $origimg[0].height != 0) { + // Obtain dimensions from contained img element. + $origimg.width($origimg[0].width); + $origimg.height($origimg[0].height); + } else { + // Obtain dimensions from temporary image in case the original is not loaded yet (e.g. IE 7.0). + var tempImage = new Image(); + tempImage.src = $origimg[0].src; + $origimg.width(tempImage.width); + $origimg.height(tempImage.height); + } + + var $img = $origimg.clone().removeAttr('id').css(img_css).show(); + + $img.width($origimg.width()); + $img.height($origimg.height()); + $origimg.after($img).hide(); + + } else { + $img = $origimg.css(img_css).show(); + img_mode = false; + if (options.shade === null) { options.shade = true; } + } + + presize($img, options.boxWidth, options.boxHeight); + + var boundx = $img.width(), + boundy = $img.height(), + + + $div = $('
    ').width(boundx).height(boundy).addClass(cssClass('holder')).css({ + position: 'relative', + backgroundColor: options.bgColor + }).insertAfter($origimg).append($img); + + if (options.addClass) { + $div.addClass(options.addClass); + } + + var $img2 = $('
    '), + + $img_holder = $('
    ') + .width('100%').height('100%').css({ + zIndex: 310, + position: 'absolute', + overflow: 'hidden' + }), + + $hdl_holder = $('
    ') + .width('100%').height('100%').css('zIndex', 320), + + $sel = $('
    ') + .css({ + position: 'absolute', + zIndex: 600 + }).dblclick(function(){ + var c = Coords.getFixed(); + options.onDblClick.call(api,c); + }).insertBefore($img).append($img_holder, $hdl_holder); + + if (img_mode) { + + $img2 = $('') + .attr('src', $img.attr('src')).css(img_css).width(boundx).height(boundy), + + $img_holder.append($img2); + + } + + if (ie6mode) { + $sel.css({ + overflowY: 'hidden' + }); + } + + var bound = options.boundary; + var $trk = newTracker().width(boundx + (bound * 2)).height(boundy + (bound * 2)).css({ + position: 'absolute', + top: px(-bound), + left: px(-bound), + zIndex: 290 + }).mousedown(newSelection); + /* }}} */ + // Set more variables {{{ + var bgcolor = options.bgColor, + bgopacity = options.bgOpacity, + xlimit, ylimit, xmin, ymin, xscale, yscale, enabled = true, + btndown, animating, shift_down; - function setCursor(t) { $trk.css('cursor',t); }; + docOffset = getPos($img); + // }}} + // }}} + // Internal Modules {{{ + // Touch Module {{{ + var Touch = (function () { + // Touch support detection function adapted (under MIT License) + // from code by Jeffrey Sambells - http://github.com/iamamused/ + function hasTouchSupport() { + var support = {}, events = ['touchstart', 'touchmove', 'touchend'], + el = document.createElement('div'), i; - $img.before($trk); - return { - activateHandlers: activateHandlers, - setCursor: setCursor - }; - }(); - /*}}}*/ - var KeyManager = function()/*{{{*/ - { - var $keymgr = $('') - .css({ position: 'absolute', left: '-30px' }) - .keypress(parseKey) - .blur(onBlur), + try { + for(i=0; i') - .css({ - position: 'absolute', - overflow: 'hidden' - }) - .append($keymgr) - ; + function detectSupport() { + if ((options.touchSupport === true) || (options.touchSupport === false)) return options.touchSupport; + else return hasTouchSupport(); + } + return { + createDragger: function (ord) { + return function (e) { + if (options.disabled) { + return false; + } + if ((ord === 'move') && !options.allowMove) { + return false; + } + docOffset = getPos($img); + btndown = true; + startDragMode(ord, mouseAbs(Touch.cfilter(e)), true); + e.stopPropagation(); + e.preventDefault(); + return false; + }; + }, + newSelection: function (e) { + return newSelection(Touch.cfilter(e)); + }, + cfilter: function (e){ + e.pageX = e.originalEvent.changedTouches[0].pageX; + e.pageY = e.originalEvent.changedTouches[0].pageY; + return e; + }, + isSupported: hasTouchSupport, + support: detectSupport() + }; + }()); + // }}} + // Coords Module {{{ + var Coords = (function () { + var x1 = 0, + y1 = 0, + x2 = 0, + y2 = 0, + ox, oy; - function watchKeys()/*{{{*/ - { - if (options.keySupport) - { - $keymgr.show(); - $keymgr.focus(); - } - }; - /*}}}*/ - function onBlur(e)/*{{{*/ - { - $keymgr.hide(); - }; - /*}}}*/ - function doNudge(e,x,y)/*{{{*/ - { - if (options.allowMove) { - Coords.moveOffset([x,y]); - Selection.updateVisible(); - }; - e.preventDefault(); - e.stopPropagation(); - }; - /*}}}*/ - function parseKey(e)/*{{{*/ - { - if (e.ctrlKey) return true; - shift_down = e.shiftKey ? true : false; - var nudge = shift_down ? 10 : 1; - switch(e.keyCode) - { - case 37: doNudge(e,-nudge,0); break; - case 39: doNudge(e,nudge,0); break; - case 38: doNudge(e,0,-nudge); break; - case 40: doNudge(e,0,nudge); break; + function setPressed(pos) //{{{ + { + pos = rebound(pos); + x2 = x1 = pos[0]; + y2 = y1 = pos[1]; + } + //}}} + function setCurrent(pos) //{{{ + { + pos = rebound(pos); + ox = pos[0] - x2; + oy = pos[1] - y2; + x2 = pos[0]; + y2 = pos[1]; + } + //}}} + function getOffset() //{{{ + { + return [ox, oy]; + } + //}}} + function moveOffset(offset) //{{{ + { + var ox = offset[0], + oy = offset[1]; - case 27: Selection.release(); break; + if (0 > x1 + ox) { + ox -= ox + x1; + } + if (0 > y1 + oy) { + oy -= oy + y1; + } - case 9: return true; - } + if (boundy < y2 + oy) { + oy += boundy - (y2 + oy); + } + if (boundx < x2 + ox) { + ox += boundx - (x2 + ox); + } - return nothing(e); - }; - /*}}}*/ - - if (options.keySupport) $keywrap.insertBefore($img); - return { - watchKeys: watchKeys - }; - }(); - /*}}}*/ + x1 += ox; + x2 += ox; + y1 += oy; + y2 += oy; + } + //}}} + function getCorner(ord) //{{{ + { + var c = getFixed(); + switch (ord) { + case 'ne': + return [c.x2, c.y]; + case 'nw': + return [c.x, c.y]; + case 'se': + return [c.x2, c.y2]; + case 'sw': + return [c.x, c.y2]; + } + } + //}}} + function getFixed() //{{{ + { + if (!options.aspectRatio) { + return getRect(); + } + // This function could use some optimization I think... + var aspect = options.aspectRatio, + min_x = options.minSize[0] / xscale, - // }}} - // Internal Methods {{{ - function px(n) { return '' + parseInt(n) + 'px'; }; - function pct(n) { return '' + parseInt(n) + '%'; }; - function cssClass(cl) { return options.baseClass + '-' + cl; }; - function getPos(obj)/*{{{*/ - { - // Updated in v0.9.4 to use built-in dimensions plugin - var pos = $(obj).offset(); - return [ pos.left, pos.top ]; - }; - /*}}}*/ - function mouseAbs(e)/*{{{*/ - { - return [ (e.pageX - docOffset[0]), (e.pageY - docOffset[1]) ]; - }; - /*}}}*/ - function myCursor(type)/*{{{*/ - { - if (type != lastcurs) - { - Tracker.setCursor(type); - //Handles.xsetCursor(type); - lastcurs = type; - } - }; - /*}}}*/ - function startDragMode(mode,pos)/*{{{*/ - { - docOffset = getPos($img); - Tracker.setCursor(mode=='move'?mode:mode+'-resize'); + //min_y = options.minSize[1]/yscale, + max_x = options.maxSize[0] / xscale, + max_y = options.maxSize[1] / yscale, + rw = x2 - x1, + rh = y2 - y1, + rwa = Math.abs(rw), + rha = Math.abs(rh), + real_ratio = rwa / rha, + xx, yy, w, h; - if (mode == 'move') - return Tracker.activateHandlers(createMover(pos), doneSelect); + if (max_x === 0) { + max_x = boundx * 10; + } + if (max_y === 0) { + max_y = boundy * 10; + } + if (real_ratio < aspect) { + yy = y2; + w = rha * aspect; + xx = rw < 0 ? x1 - w : w + x1; - var fc = Coords.getFixed(); - var opp = oppLockCorner(mode); - var opc = Coords.getCorner(oppLockCorner(opp)); + if (xx < 0) { + xx = 0; + h = Math.abs((xx - x1) / aspect); + yy = rh < 0 ? y1 - h : h + y1; + } else if (xx > boundx) { + xx = boundx; + h = Math.abs((xx - x1) / aspect); + yy = rh < 0 ? y1 - h : h + y1; + } + } else { + xx = x2; + h = rwa / aspect; + yy = rh < 0 ? y1 - h : y1 + h; + if (yy < 0) { + yy = 0; + w = Math.abs((yy - y1) * aspect); + xx = rw < 0 ? x1 - w : w + x1; + } else if (yy > boundy) { + yy = boundy; + w = Math.abs(yy - y1) * aspect; + xx = rw < 0 ? x1 - w : w + x1; + } + } - Coords.setPressed(Coords.getCorner(opp)); - Coords.setCurrent(opc); + // Magic %-) + if (xx > x1) { // right side + if (xx - x1 < min_x) { + xx = x1 + min_x; + } else if (xx - x1 > max_x) { + xx = x1 + max_x; + } + if (yy > y1) { + yy = y1 + (xx - x1) / aspect; + } else { + yy = y1 - (xx - x1) / aspect; + } + } else if (xx < x1) { // left side + if (x1 - xx < min_x) { + xx = x1 - min_x; + } else if (x1 - xx > max_x) { + xx = x1 - max_x; + } + if (yy > y1) { + yy = y1 + (x1 - xx) / aspect; + } else { + yy = y1 - (x1 - xx) / aspect; + } + } - Tracker.activateHandlers(dragmodeHandler(mode,fc),doneSelect); - }; - /*}}}*/ - function dragmodeHandler(mode,f)/*{{{*/ - { - return function(pos) { - if (!options.aspectRatio) switch(mode) - { - case 'e': pos[1] = f.y2; break; - case 'w': pos[1] = f.y2; break; - case 'n': pos[0] = f.x2; break; - case 's': pos[0] = f.x2; break; - } - else switch(mode) - { - case 'e': pos[1] = f.y+1; break; - case 'w': pos[1] = f.y+1; break; - case 'n': pos[0] = f.x+1; break; - case 's': pos[0] = f.x+1; break; - } - Coords.setCurrent(pos); - Selection.update(); - }; - }; - /*}}}*/ - function createMover(pos)/*{{{*/ - { - var lloc = pos; - KeyManager.watchKeys(); + if (xx < 0) { + x1 -= xx; + xx = 0; + } else if (xx > boundx) { + x1 -= xx - boundx; + xx = boundx; + } - return function(pos) - { - Coords.moveOffset([pos[0] - lloc[0], pos[1] - lloc[1]]); - lloc = pos; - - Selection.update(); - }; - }; - /*}}}*/ - function oppLockCorner(ord)/*{{{*/ - { - switch(ord) - { - case 'n': return 'sw'; - case 's': return 'nw'; - case 'e': return 'nw'; - case 'w': return 'ne'; - case 'ne': return 'sw'; - case 'nw': return 'se'; - case 'se': return 'nw'; - case 'sw': return 'ne'; - }; - }; - /*}}}*/ - function createDragger(ord)/*{{{*/ - { - return function(e) { - if (options.disabled) return false; - if ((ord == 'move') && !options.allowMove) return false; - btndown = true; - startDragMode(ord,mouseAbs(e)); - e.stopPropagation(); - e.preventDefault(); - return false; - }; - }; - /*}}}*/ - function presize($obj,w,h)/*{{{*/ - { - var nw = $obj.width(), nh = $obj.height(); - if ((nw > w) && w > 0) - { - nw = w; - nh = (w/$obj.width()) * $obj.height(); - } - if ((nh > h) && h > 0) - { - nh = h; - nw = (h/$obj.height()) * $obj.width(); - } - xscale = $obj.width() / nw; - yscale = $obj.height() / nh; - $obj.width(nw).height(nh); - }; - /*}}}*/ - function unscale(c)/*{{{*/ - { - return { - x: parseInt(c.x * xscale), y: parseInt(c.y * yscale), - x2: parseInt(c.x2 * xscale), y2: parseInt(c.y2 * yscale), - w: parseInt(c.w * xscale), h: parseInt(c.h * yscale) - }; - }; - /*}}}*/ - function doneSelect(pos)/*{{{*/ - { - var c = Coords.getFixed(); - if (c.w > options.minSelect[0] && c.h > options.minSelect[1]) - { - Selection.enableHandles(); - Selection.done(); - } - else - { - Selection.release(); - } - Tracker.setCursor( options.allowSelect?'crosshair':'default' ); - }; - /*}}}*/ - function newSelection(e)/*{{{*/ - { - if (options.disabled) return false; - if (!options.allowSelect) return false; - btndown = true; - docOffset = getPos($img); - Selection.disableHandles(); - myCursor('crosshair'); - var pos = mouseAbs(e); - Coords.setPressed(pos); - Tracker.activateHandlers(selectDrag,doneSelect); - KeyManager.watchKeys(); - Selection.update(); + if (yy < 0) { + y1 -= yy; + yy = 0; + } else if (yy > boundy) { + y1 -= yy - boundy; + yy = boundy; + } - e.stopPropagation(); - e.preventDefault(); - return false; - }; - /*}}}*/ - function selectDrag(pos)/*{{{*/ - { - Coords.setCurrent(pos); - Selection.update(); - }; - /*}}}*/ - function newTracker() - { - var trk = $('
    ').addClass(cssClass('tracker')); - $.browser.msie && trk.css({ opacity: 0, backgroundColor: 'white' }); - return trk; - }; + return makeObj(flipCoords(x1, y1, xx, yy)); + } + //}}} + function rebound(p) //{{{ + { + if (p[0] < 0) p[0] = 0; + if (p[1] < 0) p[1] = 0; - // }}} - // API methods {{{ - - function animateTo(a)/*{{{*/ - { - var x1 = a[0] / xscale, - y1 = a[1] / yscale, - x2 = a[2] / xscale, - y2 = a[3] / yscale; + if (p[0] > boundx) p[0] = boundx; + if (p[1] > boundy) p[1] = boundy; - if (animating) return; + return [Math.round(p[0]), Math.round(p[1])]; + } + //}}} + function flipCoords(x1, y1, x2, y2) //{{{ + { + var xa = x1, + xb = x2, + ya = y1, + yb = y2; + if (x2 < x1) { + xa = x2; + xb = x1; + } + if (y2 < y1) { + ya = y2; + yb = y1; + } + return [xa, ya, xb, yb]; + } + //}}} + function getRect() //{{{ + { + var xsize = x2 - x1, + ysize = y2 - y1, + delta; - var animto = Coords.flipCoords(x1,y1,x2,y2); - var c = Coords.getFixed(); - var animat = initcr = [ c.x, c.y, c.x2, c.y2 ]; - var interv = options.animationDelay; + if (xlimit && (Math.abs(xsize) > xlimit)) { + x2 = (xsize > 0) ? (x1 + xlimit) : (x1 - xlimit); + } + if (ylimit && (Math.abs(ysize) > ylimit)) { + y2 = (ysize > 0) ? (y1 + ylimit) : (y1 - ylimit); + } - var x = animat[0]; - var y = animat[1]; - var x2 = animat[2]; - var y2 = animat[3]; - var ix1 = animto[0] - initcr[0]; - var iy1 = animto[1] - initcr[1]; - var ix2 = animto[2] - initcr[2]; - var iy2 = animto[3] - initcr[3]; - var pcent = 0; - var velocity = options.swingSpeed; + if (ymin / yscale && (Math.abs(ysize) < ymin / yscale)) { + y2 = (ysize > 0) ? (y1 + ymin / yscale) : (y1 - ymin / yscale); + } + if (xmin / xscale && (Math.abs(xsize) < xmin / xscale)) { + x2 = (xsize > 0) ? (x1 + xmin / xscale) : (x1 - xmin / xscale); + } - Selection.animMode(true); + if (x1 < 0) { + x2 -= x1; + x1 -= x1; + } + if (y1 < 0) { + y2 -= y1; + y1 -= y1; + } + if (x2 < 0) { + x1 -= x2; + x2 -= x2; + } + if (y2 < 0) { + y1 -= y2; + y2 -= y2; + } + if (x2 > boundx) { + delta = x2 - boundx; + x1 -= delta; + x2 -= delta; + } + if (y2 > boundy) { + delta = y2 - boundy; + y1 -= delta; + y2 -= delta; + } + if (x1 > boundx) { + delta = x1 - boundy; + y2 -= delta; + y1 -= delta; + } + if (y1 > boundy) { + delta = y1 - boundy; + y2 -= delta; + y1 -= delta; + } - var animator = function() - { - return function() - { - pcent += (100 - pcent) / velocity; + return makeObj(flipCoords(x1, y1, x2, y2)); + } + //}}} + function makeObj(a) //{{{ + { + return { + x: a[0], + y: a[1], + x2: a[2], + y2: a[3], + w: a[2] - a[0], + h: a[3] - a[1] + }; + } + //}}} - animat[0] = x + ((pcent / 100) * ix1); - animat[1] = y + ((pcent / 100) * iy1); - animat[2] = x2 + ((pcent / 100) * ix2); - animat[3] = y2 + ((pcent / 100) * iy2); + return { + flipCoords: flipCoords, + setPressed: setPressed, + setCurrent: setCurrent, + getOffset: getOffset, + moveOffset: moveOffset, + getCorner: getCorner, + getFixed: getFixed + }; + }()); - if (pcent < 100) animateStart(); - else Selection.done(); + //}}} + // Shade Module {{{ + var Shade = (function() { + var enabled = false, + holder = $('
    ').css({ + position: 'absolute', + zIndex: 240, + opacity: 0 + }), + shades = { + top: createShade(), + left: createShade().height(boundy), + right: createShade().height(boundy), + bottom: createShade() + }; - if (pcent >= 99.8) pcent = 100; + function resizeShades(w,h) { + shades.left.css({ height: px(h) }); + shades.right.css({ height: px(h) }); + } + function updateAuto() + { + return updateShade(Coords.getFixed()); + } + function updateShade(c) + { + shades.top.css({ + left: px(c.x), + width: px(c.w), + height: px(c.y) + }); + shades.bottom.css({ + top: px(c.y2), + left: px(c.x), + width: px(c.w), + height: px(boundy-c.y2) + }); + shades.right.css({ + left: px(c.x2), + width: px(boundx-c.x2) + }); + shades.left.css({ + width: px(c.x) + }); + } + function createShade() { + return $('
    ').css({ + position: 'absolute', + backgroundColor: options.shadeColor||options.bgColor + }).appendTo(holder); + } + function enableShade() { + if (!enabled) { + enabled = true; + holder.insertBefore($img); + updateAuto(); + Selection.setBgOpacity(1,0,1); + $img2.hide(); - setSelectRaw(animat); - }; - }(); + setBgColor(options.shadeColor||options.bgColor,1); + if (Selection.isAwake()) + { + setOpacity(options.bgOpacity,1); + } + else setOpacity(1,1); + } + } + function setBgColor(color,now) { + colorChangeMacro(getShades(),color,now); + } + function disableShade() { + if (enabled) { + holder.remove(); + $img2.show(); + enabled = false; + if (Selection.isAwake()) { + Selection.setBgOpacity(options.bgOpacity,1,1); + } else { + Selection.setBgOpacity(1,1,1); + Selection.disableHandles(); + } + colorChangeMacro($div,0,1); + } + } + function setOpacity(opacity,now) { + if (enabled) { + if (options.bgFade && !now) { + holder.animate({ + opacity: 1-opacity + },{ + queue: false, + duration: options.fadeTime + }); + } + else holder.css({opacity:1-opacity}); + } + } + function refreshAll() { + options.shade ? enableShade() : disableShade(); + if (Selection.isAwake()) setOpacity(options.bgOpacity); + } + function getShades() { + return holder.children(); + } - function animateStart() - { window.setTimeout(animator,interv); }; + return { + update: updateAuto, + updateRaw: updateShade, + getShades: getShades, + setBgColor: setBgColor, + enable: enableShade, + disable: disableShade, + resize: resizeShades, + refresh: refreshAll, + opacity: setOpacity + }; + }()); + // }}} + // Selection Module {{{ + var Selection = (function () { + var awake, + hdep = 370, + borders = {}, + handle = {}, + dragbar = {}, + seehandles = false; - animateStart(); - }; - /*}}}*/ - function setSelect(rect)//{{{ - { - setSelectRaw([rect[0]/xscale,rect[1]/yscale,rect[2]/xscale,rect[3]/yscale]); - }; - //}}} - function setSelectRaw(l) /*{{{*/ - { - Coords.setPressed([l[0],l[1]]); - Coords.setCurrent([l[2],l[3]]); - Selection.update(); - }; - /*}}}*/ - function setOptions(opt)/*{{{*/ - { - if (typeof(opt) != 'object') opt = { }; - options = $.extend(options,opt); + // Private Methods + function insertBorder(type) //{{{ + { + var jq = $('
    ').css({ + position: 'absolute', + opacity: options.borderOpacity + }).addClass(cssClass(type)); + $img_holder.append(jq); + return jq; + } + //}}} + function dragDiv(ord, zi) //{{{ + { + var jq = $('
    ').mousedown(createDragger(ord)).css({ + cursor: ord + '-resize', + position: 'absolute', + zIndex: zi + }).addClass('ord-'+ord); - if (typeof(options.onChange)!=='function') - options.onChange = function() { }; + if (Touch.support) { + jq.bind('touchstart.jcrop', Touch.createDragger(ord)); + } - if (typeof(options.onSelect)!=='function') - options.onSelect = function() { }; + $hdl_holder.append(jq); + return jq; + } + //}}} + function insertHandle(ord) //{{{ + { + var hs = options.handleSize, - }; - /*}}}*/ - function tellSelect()/*{{{*/ - { - return unscale(Coords.getFixed()); - }; - /*}}}*/ - function tellScaled()/*{{{*/ - { - return Coords.getFixed(); - }; - /*}}}*/ - function setOptionsNew(opt)/*{{{*/ - { - setOptions(opt); - interfaceUpdate(); - }; - /*}}}*/ - function disableCrop()//{{{ - { - options.disabled = true; - Selection.disableHandles(); - Selection.setCursor('default'); - Tracker.setCursor('default'); - }; - //}}} - function enableCrop()//{{{ - { - options.disabled = false; - interfaceUpdate(); - }; - //}}} - function cancelCrop()//{{{ - { - Selection.done(); - Tracker.activateHandlers(null,null); - }; - //}}} - function destroy()//{{{ - { - $div.remove(); - $origimg.show(); - }; - //}}} + div = dragDiv(ord, hdep++).css({ + opacity: options.handleOpacity + }).addClass(cssClass('handle')); - function interfaceUpdate(alt)//{{{ - // This method tweaks the interface based on options object. - // Called when options are changed and at end of initialization. - { - options.allowResize ? - alt?Selection.enableOnly():Selection.enableHandles(): - Selection.disableHandles(); + if (hs) { div.width(hs).height(hs); } - Tracker.setCursor( options.allowSelect? 'crosshair': 'default' ); - Selection.setCursor( options.allowMove? 'move': 'default' ); + return div; + } + //}}} + function insertDragbar(ord) //{{{ + { + return dragDiv(ord, hdep++).addClass('jcrop-dragbar'); + } + //}}} + function createDragbars(li) //{{{ + { + var i; + for (i = 0; i < li.length; i++) { + dragbar[li[i]] = insertDragbar(li[i]); + } + } + //}}} + function createBorders(li) //{{{ + { + var cl,i; + for (i = 0; i < li.length; i++) { + switch(li[i]){ + case'n': cl='hline'; break; + case's': cl='hline bottom'; break; + case'e': cl='vline right'; break; + case'w': cl='vline'; break; + } + borders[li[i]] = insertBorder(cl); + } + } + //}}} + function createHandles(li) //{{{ + { + var i; + for (i = 0; i < li.length; i++) { + handle[li[i]] = insertHandle(li[i]); + } + } + //}}} + function moveto(x, y) //{{{ + { + if (!options.shade) { + $img2.css({ + top: px(-y), + left: px(-x) + }); + } + $sel.css({ + top: px(y), + left: px(x) + }); + } + //}}} + function resize(w, h) //{{{ + { + $sel.width(Math.round(w)).height(Math.round(h)); + } + //}}} + function refresh() //{{{ + { + var c = Coords.getFixed(); - $div.css('backgroundColor',options.bgColor); + Coords.setPressed([c.x, c.y]); + Coords.setCurrent([c.x2, c.y2]); - if ('setSelect' in options) { - setSelect(opt.setSelect); - Selection.done(); - delete(options.setSelect); - } + updateVisible(); + } + //}}} - if ('trueSize' in options) { - xscale = options.trueSize[0] / boundx; - yscale = options.trueSize[1] / boundy; - } + // Internal Methods + function updateVisible(select) //{{{ + { + if (awake) { + return update(select); + } + } + //}}} + function update(select) //{{{ + { + var c = Coords.getFixed(); - xlimit = options.maxSize[0] || 0; - ylimit = options.maxSize[1] || 0; - xmin = options.minSize[0] || 0; - ymin = options.minSize[1] || 0; + resize(c.w, c.h); + moveto(c.x, c.y); + if (options.shade) Shade.updateRaw(c); - if ('outerImage' in options) - { - $img.attr('src',options.outerImage); - delete(options.outerImage); - } + awake || show(); - Selection.refresh(); - }; - //}}} + if (select) { + options.onSelect.call(api, unscale(c)); + } else { + options.onChange.call(api, unscale(c)); + } + } + //}}} + function setBgOpacity(opacity,force,now) //{{{ + { + if (!awake && !force) return; + if (options.bgFade && !now) { + $img.animate({ + opacity: opacity + },{ + queue: false, + duration: options.fadeTime + }); + } else { + $img.css('opacity', opacity); + } + } + //}}} + function show() //{{{ + { + $sel.show(); - // }}} + if (options.shade) Shade.opacity(bgopacity); + else setBgOpacity(bgopacity,true); - $hdl_holder.hide(); - interfaceUpdate(true); - - var api = { - animateTo: animateTo, - setSelect: setSelect, - setOptions: setOptionsNew, - tellSelect: tellSelect, - tellScaled: tellScaled, + awake = true; + } + //}}} + function release() //{{{ + { + disableHandles(); + $sel.hide(); - disable: disableCrop, - enable: enableCrop, - cancel: cancelCrop, + if (options.shade) Shade.opacity(1); + else setBgOpacity(1); - focus: KeyManager.watchKeys, + awake = false; + options.onRelease.call(api); + } + //}}} + function showHandles() //{{{ + { + if (seehandles) { + $hdl_holder.show(); + } + } + //}}} + function enableHandles() //{{{ + { + seehandles = true; + if (options.allowResize) { + $hdl_holder.show(); + return true; + } + } + //}}} + function disableHandles() //{{{ + { + seehandles = false; + $hdl_holder.hide(); + } + //}}} + function animMode(v) //{{{ + { + if (v) { + animating = true; + disableHandles(); + } else { + animating = false; + enableHandles(); + } + } + //}}} + function done() //{{{ + { + animMode(false); + refresh(); + } + //}}} + // Insert draggable elements {{{ + // Insert border divs for outline - getBounds: function() { return [ boundx * xscale, boundy * yscale ]; }, - getWidgetSize: function() { return [ boundx, boundy ]; }, + if (options.dragEdges && $.isArray(options.createDragbars)) + createDragbars(options.createDragbars); - release: Selection.release, - destroy: destroy + if ($.isArray(options.createHandles)) + createHandles(options.createHandles); - }; + if (options.drawBorders && $.isArray(options.createBorders)) + createBorders(options.createBorders); - $origimg.data('Jcrop',api); - return api; -}; + //}}} -$.fn.Jcrop = function(options)/*{{{*/ -{ - function attachWhenDone(from)/*{{{*/ - { - var loadsrc = options.useImg || from.src; - var img = new Image(); - img.onload = function() { $.Jcrop(from,options); }; - img.src = loadsrc; - }; - /*}}}*/ - if (typeof(options) !== 'object') options = { }; + // This is a hack for iOS5 to support drag/move touch functionality + $(document).bind('touchstart.jcrop-ios',function(e) { + if ($(e.currentTarget).hasClass('jcrop-tracker')) e.stopPropagation(); + }); - // Iterate over each object, attach Jcrop - this.each(function() - { - // If we've already attached to this object - if ($(this).data('Jcrop')) - { - // The API can be requested this way (undocumented) - if (options == 'api') return $(this).data('Jcrop'); - // Otherwise, we just reset the options... - else $(this).data('Jcrop').setOptions(options); - } - // If we haven't been attached, preload and attach - else attachWhenDone(this); - }); + var $track = newTracker().mousedown(createDragger('move')).css({ + cursor: 'move', + position: 'absolute', + zIndex: 360 + }); - // Return "this" so we're chainable a la jQuery plugin-style! - return this; -}; -/*}}}*/ + if (Touch.support) { + $track.bind('touchstart.jcrop', Touch.createDragger('move')); + } -})(jQuery); + $img_holder.append($track); + disableHandles(); + + return { + updateVisible: updateVisible, + update: update, + release: release, + refresh: refresh, + isAwake: function () { + return awake; + }, + setCursor: function (cursor) { + $track.css('cursor', cursor); + }, + enableHandles: enableHandles, + enableOnly: function () { + seehandles = true; + }, + showHandles: showHandles, + disableHandles: disableHandles, + animMode: animMode, + setBgOpacity: setBgOpacity, + done: done + }; + }()); + + //}}} + // Tracker Module {{{ + var Tracker = (function () { + var onMove = function () {}, + onDone = function () {}, + trackDoc = options.trackDocument; + + function toFront(touch) //{{{ + { + $trk.css({ + zIndex: 450 + }); + + if (touch) + $(document) + .bind('touchmove.jcrop', trackTouchMove) + .bind('touchend.jcrop', trackTouchEnd); + + else if (trackDoc) + $(document) + .bind('mousemove.jcrop',trackMove) + .bind('mouseup.jcrop',trackUp); + } + //}}} + function toBack() //{{{ + { + $trk.css({ + zIndex: 290 + }); + $(document).unbind('.jcrop'); + } + //}}} + function trackMove(e) //{{{ + { + onMove(mouseAbs(e)); + return false; + } + //}}} + function trackUp(e) //{{{ + { + e.preventDefault(); + e.stopPropagation(); + + if (btndown) { + btndown = false; + + onDone(mouseAbs(e)); + + if (Selection.isAwake()) { + options.onSelect.call(api, unscale(Coords.getFixed())); + } + + toBack(); + onMove = function () {}; + onDone = function () {}; + } + + return false; + } + //}}} + function activateHandlers(move, done, touch) //{{{ + { + btndown = true; + onMove = move; + onDone = done; + toFront(touch); + return false; + } + //}}} + function trackTouchMove(e) //{{{ + { + onMove(mouseAbs(Touch.cfilter(e))); + return false; + } + //}}} + function trackTouchEnd(e) //{{{ + { + return trackUp(Touch.cfilter(e)); + } + //}}} + function setCursor(t) //{{{ + { + $trk.css('cursor', t); + } + //}}} + + if (!trackDoc) { + $trk.mousemove(trackMove).mouseup(trackUp).mouseout(trackUp); + } + + $img.before($trk); + return { + activateHandlers: activateHandlers, + setCursor: setCursor + }; + }()); + //}}} + // KeyManager Module {{{ + var KeyManager = (function () { + var $keymgr = $('').css({ + position: 'fixed', + left: '-120px', + width: '12px' + }).addClass('jcrop-keymgr'), + + $keywrap = $('
    ').css({ + position: 'absolute', + overflow: 'hidden' + }).append($keymgr); + + function watchKeys() //{{{ + { + if (options.keySupport) { + $keymgr.show(); + $keymgr.focus(); + } + } + //}}} + function onBlur(e) //{{{ + { + $keymgr.hide(); + } + //}}} + function doNudge(e, x, y) //{{{ + { + if (options.allowMove) { + Coords.moveOffset([x, y]); + Selection.updateVisible(true); + } + e.preventDefault(); + e.stopPropagation(); + } + //}}} + function parseKey(e) //{{{ + { + if (e.ctrlKey || e.metaKey) { + return true; + } + shift_down = e.shiftKey ? true : false; + var nudge = shift_down ? 10 : 1; + + switch (e.keyCode) { + case 37: + doNudge(e, -nudge, 0); + break; + case 39: + doNudge(e, nudge, 0); + break; + case 38: + doNudge(e, 0, -nudge); + break; + case 40: + doNudge(e, 0, nudge); + break; + case 27: + if (options.allowSelect) Selection.release(); + break; + case 9: + return true; + } + + return false; + } + //}}} + + if (options.keySupport) { + $keymgr.keydown(parseKey).blur(onBlur); + if (ie6mode || !options.fixedSupport) { + $keymgr.css({ + position: 'absolute', + left: '-20px' + }); + $keywrap.append($keymgr).insertBefore($img); + } else { + $keymgr.insertBefore($img); + } + } + + + return { + watchKeys: watchKeys + }; + }()); + //}}} + // }}} + // API methods {{{ + function setClass(cname) //{{{ + { + $div.removeClass().addClass(cssClass('holder')).addClass(cname); + } + //}}} + function animateTo(a, callback) //{{{ + { + var x1 = a[0] / xscale, + y1 = a[1] / yscale, + x2 = a[2] / xscale, + y2 = a[3] / yscale; + + if (animating) { + return; + } + + var animto = Coords.flipCoords(x1, y1, x2, y2), + c = Coords.getFixed(), + initcr = [c.x, c.y, c.x2, c.y2], + animat = initcr, + interv = options.animationDelay, + ix1 = animto[0] - initcr[0], + iy1 = animto[1] - initcr[1], + ix2 = animto[2] - initcr[2], + iy2 = animto[3] - initcr[3], + pcent = 0, + velocity = options.swingSpeed; + + x1 = animat[0]; + y1 = animat[1]; + x2 = animat[2]; + y2 = animat[3]; + + Selection.animMode(true); + var anim_timer; + + function queueAnimator() { + window.setTimeout(animator, interv); + } + var animator = (function () { + return function () { + pcent += (100 - pcent) / velocity; + + animat[0] = Math.round(x1 + ((pcent / 100) * ix1)); + animat[1] = Math.round(y1 + ((pcent / 100) * iy1)); + animat[2] = Math.round(x2 + ((pcent / 100) * ix2)); + animat[3] = Math.round(y2 + ((pcent / 100) * iy2)); + + if (pcent >= 99.8) { + pcent = 100; + } + if (pcent < 100) { + setSelectRaw(animat); + queueAnimator(); + } else { + Selection.done(); + Selection.animMode(false); + if (typeof(callback) === 'function') { + callback.call(api); + } + } + }; + }()); + queueAnimator(); + } + //}}} + function setSelect(rect) //{{{ + { + setSelectRaw([rect[0] / xscale, rect[1] / yscale, rect[2] / xscale, rect[3] / yscale]); + options.onSelect.call(api, unscale(Coords.getFixed())); + Selection.enableHandles(); + } + //}}} + function setSelectRaw(l) //{{{ + { + Coords.setPressed([l[0], l[1]]); + Coords.setCurrent([l[2], l[3]]); + Selection.update(); + } + //}}} + function tellSelect() //{{{ + { + return unscale(Coords.getFixed()); + } + //}}} + function tellScaled() //{{{ + { + return Coords.getFixed(); + } + //}}} + function setOptionsNew(opt) //{{{ + { + setOptions(opt); + interfaceUpdate(); + } + //}}} + function disableCrop() //{{{ + { + options.disabled = true; + Selection.disableHandles(); + Selection.setCursor('default'); + Tracker.setCursor('default'); + } + //}}} + function enableCrop() //{{{ + { + options.disabled = false; + interfaceUpdate(); + } + //}}} + function cancelCrop() //{{{ + { + Selection.done(); + Tracker.activateHandlers(null, null); + } + //}}} + function destroy() //{{{ + { + $div.remove(); + $origimg.show(); + $origimg.css('visibility','visible'); + $(obj).removeData('Jcrop'); + } + //}}} + function setImage(src, callback) //{{{ + { + Selection.release(); + disableCrop(); + var img = new Image(); + img.onload = function () { + var iw = img.width; + var ih = img.height; + var bw = options.boxWidth; + var bh = options.boxHeight; + $img.width(iw).height(ih); + $img.attr('src', src); + $img2.attr('src', src); + presize($img, bw, bh); + boundx = $img.width(); + boundy = $img.height(); + $img2.width(boundx).height(boundy); + $trk.width(boundx + (bound * 2)).height(boundy + (bound * 2)); + $div.width(boundx).height(boundy); + Shade.resize(boundx,boundy); + enableCrop(); + + if (typeof(callback) === 'function') { + callback.call(api); + } + }; + img.src = src; + } + //}}} + function colorChangeMacro($obj,color,now) { + var mycolor = color || options.bgColor; + if (options.bgFade && supportsColorFade() && options.fadeTime && !now) { + $obj.animate({ + backgroundColor: mycolor + }, { + queue: false, + duration: options.fadeTime + }); + } else { + $obj.css('backgroundColor', mycolor); + } + } + function interfaceUpdate(alt) //{{{ + // This method tweaks the interface based on options object. + // Called when options are changed and at end of initialization. + { + if (options.allowResize) { + if (alt) { + Selection.enableOnly(); + } else { + Selection.enableHandles(); + } + } else { + Selection.disableHandles(); + } + + Tracker.setCursor(options.allowSelect ? 'crosshair' : 'default'); + Selection.setCursor(options.allowMove ? 'move' : 'default'); + + if (options.hasOwnProperty('trueSize')) { + xscale = options.trueSize[0] / boundx; + yscale = options.trueSize[1] / boundy; + } + + if (options.hasOwnProperty('setSelect')) { + setSelect(options.setSelect); + Selection.done(); + delete(options.setSelect); + } + + Shade.refresh(); + + if (options.bgColor != bgcolor) { + colorChangeMacro( + options.shade? Shade.getShades(): $div, + options.shade? + (options.shadeColor || options.bgColor): + options.bgColor + ); + bgcolor = options.bgColor; + } + + if (bgopacity != options.bgOpacity) { + bgopacity = options.bgOpacity; + if (options.shade) Shade.refresh(); + else Selection.setBgOpacity(bgopacity); + } + + xlimit = options.maxSize[0] || 0; + ylimit = options.maxSize[1] || 0; + xmin = options.minSize[0] || 0; + ymin = options.minSize[1] || 0; + + if (options.hasOwnProperty('outerImage')) { + $img.attr('src', options.outerImage); + delete(options.outerImage); + } + + Selection.refresh(); + } + //}}} + //}}} + + if (Touch.support) $trk.bind('touchstart.jcrop', Touch.newSelection); + + $hdl_holder.hide(); + interfaceUpdate(true); + + var api = { + setImage: setImage, + animateTo: animateTo, + setSelect: setSelect, + setOptions: setOptionsNew, + tellSelect: tellSelect, + tellScaled: tellScaled, + setClass: setClass, + + disable: disableCrop, + enable: enableCrop, + cancel: cancelCrop, + release: Selection.release, + destroy: destroy, + + focus: KeyManager.watchKeys, + + getBounds: function () { + return [boundx * xscale, boundy * yscale]; + }, + getWidgetSize: function () { + return [boundx, boundy]; + }, + getScaleFactor: function () { + return [xscale, yscale]; + }, + getOptions: function() { + // careful: internal values are returned + return options; + }, + + ui: { + holder: $div, + selection: $sel + } + }; + + if (is_msie) $div.bind('selectstart', function () { return false; }); + + $origimg.data('Jcrop', api); + return api; + }; + $.fn.Jcrop = function (options, callback) //{{{ + { + var api; + // Iterate over each object, attach Jcrop + this.each(function () { + // If we've already attached to this object + if ($(this).data('Jcrop')) { + // The API can be requested this way (undocumented) + if (options === 'api') return $(this).data('Jcrop'); + // Otherwise, we just reset the options... + else $(this).data('Jcrop').setOptions(options); + } + // If we haven't been attached, preload and attach + else { + if (this.tagName == 'IMG') + $.Jcrop.Loader(this,function(){ + $(this).css({display:'block',visibility:'hidden'}); + api = $.Jcrop(this, options); + if ($.isFunction(callback)) callback.call(api); + }); + else { + $(this).css({display:'block',visibility:'hidden'}); + api = $.Jcrop(this, options); + if ($.isFunction(callback)) callback.call(api); + } + } + }); + + // Return "this" so the object is chainable (jQuery-style) + return this; + }; + //}}} + // $.Jcrop.Loader - basic image loader {{{ + + $.Jcrop.Loader = function(imgobj,success,error){ + var $img = $(imgobj), img = $img[0]; + + function completeCheck(){ + if (img.complete) { + $img.unbind('.jcloader'); + if ($.isFunction(success)) success.call(img); + } + else window.setTimeout(completeCheck,50); + } + + $img + .bind('load.jcloader',completeCheck) + .bind('error.jcloader',function(e){ + $img.unbind('.jcloader'); + if ($.isFunction(error)) error.call(img); + }); + + if (img.complete && $.isFunction(success)){ + $img.unbind('.jcloader'); + success.call(img); + } + }; + + //}}} + // Global Defaults {{{ + $.Jcrop.defaults = { + + // Basic Settings + allowSelect: true, + allowMove: true, + allowResize: true, + + trackDocument: true, + + // Styling Options + baseClass: 'jcrop', + addClass: null, + bgColor: 'black', + bgOpacity: 0.6, + bgFade: false, + borderOpacity: 0.4, + handleOpacity: 0.5, + handleSize: null, + + aspectRatio: 0, + keySupport: true, + createHandles: ['n','s','e','w','nw','ne','se','sw'], + createDragbars: ['n','s','e','w'], + createBorders: ['n','s','e','w'], + drawBorders: true, + dragEdges: true, + fixedSupport: true, + touchSupport: null, + + shade: null, + + boxWidth: 0, + boxHeight: 0, + boundary: 2, + fadeTime: 400, + animationDelay: 20, + swingSpeed: 3, + + minSelect: [0, 0], + maxSize: [0, 0], + minSize: [0, 0], + + // Callbacks / Event Handlers + onChange: function () {}, + onSelect: function () {}, + onDblClick: function () {}, + onRelease: function () {} + }; + + // }}} +}(jQuery)); \ No newline at end of file diff --git a/webapp/src/main/webapp/js/jquery_plugins/jquery.realperson.js b/webapp/src/main/webapp/js/jquery_plugins/jquery.realperson.js index 29f7c4c37..256ccc830 100644 --- a/webapp/src/main/webapp/js/jquery_plugins/jquery.realperson.js +++ b/webapp/src/main/webapp/js/jquery_plugins/jquery.realperson.js @@ -176,7 +176,7 @@ $.fn.realperson = function(options) { /* Initialise the real person functionality. */ $.realperson = new RealPerson(); // singleton instance -$('.realperson-challenge').live('click', function() { +$( document ).on('click', '.realperson-challenge', function() { $(this).next().next().realperson('change'); }); diff --git a/webapp/src/main/webapp/js/jquery_plugins/qtip/jquery.qtip-1.0.0-rc3.min.js b/webapp/src/main/webapp/js/jquery_plugins/qtip/jquery.qtip-1.0.0-rc3.min.js deleted file mode 100644 index f50d843fc..000000000 --- a/webapp/src/main/webapp/js/jquery_plugins/qtip/jquery.qtip-1.0.0-rc3.min.js +++ /dev/null @@ -1,15 +0,0 @@ -/* - * jquery.qtip. The jQuery tooltip plugin - * - * Copyright (c) 2009 Craig Thompson - * http://craigsworks.com - * - * Licensed under MIT - * http://www.opensource.org/licenses/mit-license.php - * - * Launch : February 2009 - * Version : 1.0.0-rc3 - * Released: Tuesday 12th May, 2009 - 00:00 - * Debug: jquery.qtip.debug.js - */ -(function(f){f.fn.qtip=function(B,u){var y,t,A,s,x,w,v,z;if(typeof B=="string"){if(typeof f(this).data("qtip")!=="object"){f.fn.qtip.log.error.call(self,1,f.fn.qtip.constants.NO_TOOLTIP_PRESENT,false)}if(B=="api"){return f(this).data("qtip").interfaces[f(this).data("qtip").current]}else{if(B=="interfaces"){return f(this).data("qtip").interfaces}}}else{if(!B){B={}}if(typeof B.content!=="object"||(B.content.jquery&&B.content.length>0)){B.content={text:B.content}}if(typeof B.content.title!=="object"){B.content.title={text:B.content.title}}if(typeof B.position!=="object"){B.position={corner:B.position}}if(typeof B.position.corner!=="object"){B.position.corner={target:B.position.corner,tooltip:B.position.corner}}if(typeof B.show!=="object"){B.show={when:B.show}}if(typeof B.show.when!=="object"){B.show.when={event:B.show.when}}if(typeof B.show.effect!=="object"){B.show.effect={type:B.show.effect}}if(typeof B.hide!=="object"){B.hide={when:B.hide}}if(typeof B.hide.when!=="object"){B.hide.when={event:B.hide.when}}if(typeof B.hide.effect!=="object"){B.hide.effect={type:B.hide.effect}}if(typeof B.style!=="object"){B.style={name:B.style}}B.style=c(B.style);s=f.extend(true,{},f.fn.qtip.defaults,B);s.style=a.call({options:s},s.style);s.user=f.extend(true,{},B)}return f(this).each(function(){if(typeof B=="string"){w=B.toLowerCase();A=f(this).qtip("interfaces");if(typeof A=="object"){if(u===true&&w=="destroy"){while(A.length>0){A[A.length-1].destroy()}}else{if(u!==true){A=[f(this).qtip("api")]}for(y=0;y0))}if(typeof s.options.show.solo=="object"){z=f(s.options.show.solo)}else{if(s.options.show.solo===true){z=f("div.qtip").not(s.elements.tooltip)}}if(z){z.each(function(){if(f(this).qtip("api").status.rendered===true){f(this).qtip("api").hide()}})}if(typeof s.options.show.effect.type=="function"){s.options.show.effect.type.call(s.elements.tooltip,s.options.show.effect.length);s.elements.tooltip.queue(function(){w();f(this).dequeue()})}else{switch(s.options.show.effect.type.toLowerCase()){case"fade":s.elements.tooltip.fadeIn(s.options.show.effect.length,w);break;case"slide":s.elements.tooltip.slideDown(s.options.show.effect.length,function(){w();if(s.options.position.type!=="static"){s.updatePosition(y,true)}});break;case"grow":s.elements.tooltip.show(s.options.show.effect.length,w);break;default:s.elements.tooltip.show(null,w);break}s.elements.tooltip.addClass(s.options.style.classes.active)}return f.fn.qtip.log.error.call(s,1,f.fn.qtip.constants.EVENT_SHOWN,"show")},hide:function(y){var x;if(!s.status.rendered){return f.fn.qtip.log.error.call(s,2,f.fn.qtip.constants.TOOLTIP_NOT_RENDERED,"hide")}else{if(s.elements.tooltip.css("display")==="none"){return s}}clearTimeout(s.timers.show);s.elements.tooltip.stop(true,false);x=s.beforeHide.call(s,y);if(x===false){return s}function w(){s.onHide.call(s,y)}s.cache.toggle=0;if(typeof s.options.hide.effect.type=="function"){s.options.hide.effect.type.call(s.elements.tooltip,s.options.hide.effect.length);s.elements.tooltip.queue(function(){w();f(this).dequeue()})}else{switch(s.options.hide.effect.type.toLowerCase()){case"fade":s.elements.tooltip.fadeOut(s.options.hide.effect.length,w);break;case"slide":s.elements.tooltip.slideUp(s.options.hide.effect.length,w);break;case"grow":s.elements.tooltip.hide(s.options.hide.effect.length,w);break;default:s.elements.tooltip.hide(null,w);break}s.elements.tooltip.removeClass(s.options.style.classes.active)}return f.fn.qtip.log.error.call(s,1,f.fn.qtip.constants.EVENT_HIDDEN,"hide")},updatePosition:function(w,x){var C,G,L,J,H,E,y,I,B,D,K,A,F,z;if(!s.status.rendered){return f.fn.qtip.log.error.call(s,2,f.fn.qtip.constants.TOOLTIP_NOT_RENDERED,"updatePosition")}else{if(s.options.position.type=="static"){return f.fn.qtip.log.error.call(s,1,f.fn.qtip.constants.CANNOT_POSITION_STATIC,"updatePosition")}}G={position:{left:0,top:0},dimensions:{height:0,width:0},corner:s.options.position.corner.target};L={position:s.getPosition(),dimensions:s.getDimensions(),corner:s.options.position.corner.tooltip};if(s.options.position.target!=="mouse"){if(s.options.position.target.get(0).nodeName.toLowerCase()=="area"){J=s.options.position.target.attr("coords").split(",");for(C=0;CG.dimensions.width){G.dimensions.width=J[C]}if(J[C]G.dimensions.height){G.dimensions.height=J[C]}if(J[C]0){if(L.corner.search(/Left/)!==-1){y.left-=s.options.style.border.radius}else{if(L.corner.search(/Right/)!==-1){y.left+=s.options.style.border.radius}}if(L.corner.search(/Top/)!==-1){y.top-=s.options.style.border.radius}else{if(L.corner.search(/Bottom/)!==-1){y.top+=s.options.style.border.radius}}}if(I){if(L.corner.search(/top/)!==-1){y.top-=I}else{if(L.corner.search(/bottom/)!==-1){y.top+=I}}if(L.corner.search(/left/)!==-1){y.left-=I}else{if(L.corner.search(/right/)!==-1){y.left+=I}}if(L.corner.search(/leftMiddle|rightMiddle/)!==-1){y.top-=1}}if(s.options.position.adjust.screen===true){y=o.call(s,y,G,L)}if(s.options.position.target==="mouse"&&s.options.position.adjust.mouse===true){if(s.options.position.adjust.screen===true&&s.elements.tip){K=s.elements.tip.attr("rel")}else{K=s.options.position.corner.tooltip}y.left+=(K.search(/right/i)!==-1)?-6:6;y.top+=(K.search(/bottom/i)!==-1)?-6:6}if(!s.elements.bgiframe&&f.browser.msie&&parseInt(f.browser.version.charAt(0))==6){f("select, object").each(function(){A=f(this).offset();A.bottom=A.top+f(this).height();A.right=A.left+f(this).width();if(y.top+L.dimensions.height>=A.top&&y.left+L.dimensions.width>=A.left){k.call(s)}})}y.left+=s.options.position.adjust.x;y.top+=s.options.position.adjust.y;F=s.getPosition();if(y.left!=F.left||y.top!=F.top){z=s.beforePositionUpdate.call(s,w);if(z===false){return s}s.cache.position=y;if(x===true){s.status.animated=true;s.elements.tooltip.animate(y,200,"swing",function(){s.status.animated=false})}else{s.elements.tooltip.css(y)}s.onPositionUpdate.call(s,w);if(typeof w!=="undefined"&&w.type&&w.type!=="mousemove"){f.fn.qtip.log.error.call(s,1,f.fn.qtip.constants.EVENT_POSITION_UPDATED,"updatePosition")}}return s},updateWidth:function(w){var x;if(!s.status.rendered){return f.fn.qtip.log.error.call(s,2,f.fn.qtip.constants.TOOLTIP_NOT_RENDERED,"updateWidth")}else{if(w&&typeof w!=="number"){return f.fn.qtip.log.error.call(s,2,"newWidth must be of type number","updateWidth")}}x=s.elements.contentWrapper.siblings().add(s.elements.tip).add(s.elements.button);if(!w){if(typeof s.options.style.width.value=="number"){w=s.options.style.width.value}else{s.elements.tooltip.css({width:"auto"});x.hide();if(f.browser.msie){s.elements.wrapper.add(s.elements.contentWrapper.children()).css({zoom:"normal"})}w=s.getDimensions().width+1;if(!s.options.style.width.value){if(w>s.options.style.width.max){w=s.options.style.width.max}if(w").get(0).getContext){z=s.elements.tooltip.find(".qtip-tip canvas:first");x=z.get(0).getContext("2d");x.clearRect(0,0,300,300);y=z.parent("div[rel]:first").attr("rel");B=b(y,s.options.style.tip.size.width,s.options.style.tip.size.height);h.call(s,z,B,s.options.style.tip.color||s.options.style.border.color)}else{if(f.browser.msie){z=s.elements.tooltip.find('.qtip-tip [nodeName="shape"]');z.attr("fillcolor",s.options.style.tip.color||s.options.style.border.color)}}}if(s.options.style.border.radius>0){s.elements.tooltip.find(".qtip-betweenCorners").css({backgroundColor:s.options.style.border.color});if(f("").get(0).getContext){A=g(s.options.style.border.radius);s.elements.tooltip.find(".qtip-wrapper canvas").each(function(){x=f(this).get(0).getContext("2d");x.clearRect(0,0,300,300);y=f(this).parent("div[rel]:first").attr("rel");r.call(s,f(this),A[y],s.options.style.border.radius,s.options.style.border.color)})}else{if(f.browser.msie){s.elements.tooltip.find('.qtip-wrapper [nodeName="arc"]').each(function(){f(this).attr("fillcolor",s.options.style.border.color)})}}}return f.fn.qtip.log.error.call(s,1,f.fn.qtip.constants.EVENT_STYLE_UPDATED,"updateStyle")},updateContent:function(A,y){var z,x,w;if(!s.status.rendered){return f.fn.qtip.log.error.call(s,2,f.fn.qtip.constants.TOOLTIP_NOT_RENDERED,"updateContent")}else{if(!A){return f.fn.qtip.log.error.call(s,2,f.fn.qtip.constants.NO_CONTENT_PROVIDED,"updateContent")}}z=s.beforeContentUpdate.call(s,A);if(typeof z=="string"){A=z}else{if(z===false){return}}if(f.browser.msie){s.elements.contentWrapper.children().css({zoom:"normal"})}if(A.jquery&&A.length>0){A.clone(true).appendTo(s.elements.content).show()}else{s.elements.content.html(A)}x=s.elements.content.find("img[complete=false]");if(x.length>0){w=0;x.each(function(C){f('').load(function(){if(++w==x.length){B()}})})}else{B()}function B(){s.updateWidth();if(y!==false){if(s.options.position.type!=="static"){s.updatePosition(s.elements.tooltip.is(":visible"),true)}if(s.options.style.tip.corner!==false){n.call(s)}}}s.onContentUpdate.call(s);return f.fn.qtip.log.error.call(s,1,f.fn.qtip.constants.EVENT_CONTENT_UPDATED,"loadContent")},loadContent:function(w,z,A){var y;if(!s.status.rendered){return f.fn.qtip.log.error.call(s,2,f.fn.qtip.constants.TOOLTIP_NOT_RENDERED,"loadContent")}y=s.beforeContentLoad.call(s);if(y===false){return s}if(A=="post"){f.post(w,z,x)}else{f.get(w,z,x)}function x(B){s.onContentLoad.call(s);f.fn.qtip.log.error.call(s,1,f.fn.qtip.constants.EVENT_CONTENT_LOADED,"loadContent");s.updateContent(B)}return s},updateTitle:function(w){if(!s.status.rendered){return f.fn.qtip.log.error.call(s,2,f.fn.qtip.constants.TOOLTIP_NOT_RENDERED,"updateTitle")}else{if(!w){return f.fn.qtip.log.error.call(s,2,f.fn.qtip.constants.NO_CONTENT_PROVIDED,"updateTitle")}}returned=s.beforeTitleUpdate.call(s);if(returned===false){return s}if(s.elements.button){s.elements.button=s.elements.button.clone(true)}s.elements.title.html(w);if(s.elements.button){s.elements.title.prepend(s.elements.button)}s.onTitleUpdate.call(s);return f.fn.qtip.log.error.call(s,1,f.fn.qtip.constants.EVENT_TITLE_UPDATED,"updateTitle")},focus:function(A){var y,x,w,z;if(!s.status.rendered){return f.fn.qtip.log.error.call(s,2,f.fn.qtip.constants.TOOLTIP_NOT_RENDERED,"focus")}else{if(s.options.position.type=="static"){return f.fn.qtip.log.error.call(s,1,f.fn.qtip.constants.CANNOT_FOCUS_STATIC,"focus")}}y=parseInt(s.elements.tooltip.css("z-index"));x=6000+f("div.qtip[qtip]").length-1;if(!s.status.focused&&y!==x){z=s.beforeFocus.call(s,A);if(z===false){return s}f("div.qtip[qtip]").not(s.elements.tooltip).each(function(){if(f(this).qtip("api").status.rendered===true){w=parseInt(f(this).css("z-index"));if(typeof w=="number"&&w>-1){f(this).css({zIndex:parseInt(f(this).css("z-index"))-1})}f(this).qtip("api").status.focused=false}});s.elements.tooltip.css({zIndex:x});s.status.focused=true;s.onFocus.call(s,A);f.fn.qtip.log.error.call(s,1,f.fn.qtip.constants.EVENT_FOCUSED,"focus")}return s},disable:function(w){if(!s.status.rendered){return f.fn.qtip.log.error.call(s,2,f.fn.qtip.constants.TOOLTIP_NOT_RENDERED,"disable")}if(w){if(!s.status.disabled){s.status.disabled=true;f.fn.qtip.log.error.call(s,1,f.fn.qtip.constants.EVENT_DISABLED,"disable")}else{f.fn.qtip.log.error.call(s,1,f.fn.qtip.constants.TOOLTIP_ALREADY_DISABLED,"disable")}}else{if(s.status.disabled){s.status.disabled=false;f.fn.qtip.log.error.call(s,1,f.fn.qtip.constants.EVENT_ENABLED,"disable")}else{f.fn.qtip.log.error.call(s,1,f.fn.qtip.constants.TOOLTIP_ALREADY_ENABLED,"disable")}}return s},destroy:function(){var w,x,y;x=s.beforeDestroy.call(s);if(x===false){return s}if(s.status.rendered){s.options.show.when.target.unbind("mousemove.qtip",s.updatePosition);s.options.show.when.target.unbind("mouseout.qtip",s.hide);s.options.show.when.target.unbind(s.options.show.when.event+".qtip");s.options.hide.when.target.unbind(s.options.hide.when.event+".qtip");s.elements.tooltip.unbind(s.options.hide.when.event+".qtip");s.elements.tooltip.unbind("mouseover.qtip",s.focus);s.elements.tooltip.remove()}else{s.options.show.when.target.unbind(s.options.show.when.event+".qtip-create")}if(typeof s.elements.target.data("qtip")=="object"){y=s.elements.target.data("qtip").interfaces;if(typeof y=="object"&&y.length>0){for(w=0;w0){s.elements.target.data("qtip").current=y.length-1}else{s.elements.target.removeData("qtip")}s.onDestroy.call(s);f.fn.qtip.log.error.call(s,1,f.fn.qtip.constants.EVENT_DESTROYED,"destroy");return s.elements.target},getPosition:function(){var w,x;if(!s.status.rendered){return f.fn.qtip.log.error.call(s,2,f.fn.qtip.constants.TOOLTIP_NOT_RENDERED,"getPosition")}w=(s.elements.tooltip.css("display")!=="none")?false:true;if(w){s.elements.tooltip.css({visiblity:"hidden"}).show()}x=s.elements.tooltip.offset();if(w){s.elements.tooltip.css({visiblity:"visible"}).hide()}return x},getDimensions:function(){var w,x;if(!s.status.rendered){return f.fn.qtip.log.error.call(s,2,f.fn.qtip.constants.TOOLTIP_NOT_RENDERED,"getDimensions")}w=(!s.elements.tooltip.is(":visible"))?true:false;if(w){s.elements.tooltip.css({visiblity:"hidden"}).show()}x={height:s.elements.tooltip.outerHeight(),width:s.elements.tooltip.outerWidth()};if(w){s.elements.tooltip.css({visiblity:"visible"}).hide()}return x}})}function p(){var s,w,u,t,v,y,x;s=this;s.beforeRender.call(s);s.status.rendered=true;s.elements.tooltip='';s.elements.tooltip=f(s.elements.tooltip);s.elements.tooltip.appendTo(s.options.position.container);s.elements.tooltip.data("qtip",{current:0,interfaces:[s]});s.elements.wrapper=s.elements.tooltip.children("div:first");s.elements.contentWrapper=s.elements.wrapper.children("div:first").css({background:s.options.style.background});s.elements.content=s.elements.contentWrapper.children("div:first").css(q(s.options.style));if(f.browser.msie){s.elements.wrapper.add(s.elements.content).css({zoom:1})}if(s.options.hide.when.event=="unfocus"){s.elements.tooltip.attr("unfocus",true)}if(typeof s.options.style.width.value=="number"){s.updateWidth()}if(f("").get(0).getContext||f.browser.msie){if(s.options.style.border.radius>0){m.call(s)}else{s.elements.contentWrapper.css({border:s.options.style.border.width+"px solid "+s.options.style.border.color})}if(s.options.style.tip.corner!==false){e.call(s)}}else{s.elements.contentWrapper.css({border:s.options.style.border.width+"px solid "+s.options.style.border.color});s.options.style.border.radius=0;s.options.style.tip.corner=false;f.fn.qtip.log.error.call(s,2,f.fn.qtip.constants.CANVAS_VML_NOT_SUPPORTED,"render")}if((typeof s.options.content.text=="string"&&s.options.content.text.length>0)||(s.options.content.text.jquery&&s.options.content.text.length>0)){u=s.options.content.text}else{if(typeof s.elements.target.attr("title")=="string"&&s.elements.target.attr("title").length>0){u=s.elements.target.attr("title").replace("\\n","
    ");s.elements.target.attr("title","")}else{if(typeof s.elements.target.attr("alt")=="string"&&s.elements.target.attr("alt").length>0){u=s.elements.target.attr("alt").replace("\\n","
    ");s.elements.target.attr("alt","")}else{u=" ";f.fn.qtip.log.error.call(s,1,f.fn.qtip.constants.NO_VALID_CONTENT,"render")}}}if(s.options.content.title.text!==false){j.call(s)}s.updateContent(u);l.call(s);if(s.options.show.ready===true){s.show()}if(s.options.content.url!==false){t=s.options.content.url;v=s.options.content.data;y=s.options.content.method||"get";s.loadContent(t,v,y)}s.onRender.call(s);f.fn.qtip.log.error.call(s,1,f.fn.qtip.constants.EVENT_RENDERED,"render")}function m(){var F,z,t,B,x,E,u,G,D,y,w,C,A,s,v;F=this;F.elements.wrapper.find(".qtip-borderBottom, .qtip-borderTop").remove();t=F.options.style.border.width;B=F.options.style.border.radius;x=F.options.style.border.color||F.options.style.tip.color;E=g(B);u={};for(z in E){u[z]='
    ';if(f("").get(0).getContext){u[z]+=''}else{if(f.browser.msie){G=B*2+3;u[z]+=''}}u[z]+="
    "}D=F.getDimensions().width-(Math.max(t,B)*2);y='
    ';w='
    '+u.topLeft+u.topRight+y;F.elements.wrapper.prepend(w);C='
    '+u.bottomLeft+u.bottomRight+y;F.elements.wrapper.append(C);if(f("").get(0).getContext){F.elements.wrapper.find("canvas").each(function(){A=E[f(this).parent("[rel]:first").attr("rel")];r.call(F,f(this),A,B,x)})}else{if(f.browser.msie){F.elements.tooltip.append('')}}s=Math.max(B,(B+(t-B)));v=Math.max(t-B,0);F.elements.contentWrapper.css({border:"0px solid "+x,borderWidth:v+"px "+s+"px"})}function r(u,w,s,t){var v=u.get(0).getContext("2d");v.fillStyle=t;v.beginPath();v.arc(w[0],w[1],s,0,Math.PI*2,false);v.fill()}function e(v){var t,s,x,u,w;t=this;if(t.elements.tip!==null){t.elements.tip.remove()}s=t.options.style.tip.color||t.options.style.border.color;if(t.options.style.tip.corner===false){return}else{if(!v){v=t.options.style.tip.corner}}x=b(v,t.options.style.tip.size.width,t.options.style.tip.size.height);t.elements.tip='
    ';if(f("").get(0).getContext){t.elements.tip+=''}else{if(f.browser.msie){u=t.options.style.tip.size.width+","+t.options.style.tip.size.height;w="m"+x[0][0]+","+x[0][1];w+=" l"+x[1][0]+","+x[1][1];w+=" "+x[2][0]+","+x[2][1];w+=" xe";t.elements.tip+='';t.elements.tip+='';t.elements.contentWrapper.css("position","relative")}}t.elements.tooltip.prepend(t.elements.tip+"
    ");t.elements.tip=t.elements.tooltip.find("."+t.options.style.classes.tip).eq(0);if(f("").get(0).getContext){h.call(t,t.elements.tip.find("canvas:first"),x,s)}if(v.search(/top/)!==-1&&f.browser.msie&&parseInt(f.browser.version.charAt(0))===6){t.elements.tip.css({marginTop:-4})}n.call(t,v)}function h(t,v,s){var u=t.get(0).getContext("2d");u.fillStyle=s;u.beginPath();u.moveTo(v[0][0],v[0][1]);u.lineTo(v[1][0],v[1][1]);u.lineTo(v[2][0],v[2][1]);u.fill()}function n(u){var t,w,s,x,v;t=this;if(t.options.style.tip.corner===false||!t.elements.tip){return}if(!u){u=t.elements.tip.attr("rel")}w=positionAdjust=(f.browser.msie)?1:0;t.elements.tip.css(u.match(/left|right|top|bottom/)[0],0);if(u.search(/top|bottom/)!==-1){if(f.browser.msie){if(parseInt(f.browser.version.charAt(0))===6){positionAdjust=(u.search(/top/)!==-1)?-3:1}else{positionAdjust=(u.search(/top/)!==-1)?1:2}}if(u.search(/Middle/)!==-1){t.elements.tip.css({left:"50%",marginLeft:-(t.options.style.tip.size.width/2)})}else{if(u.search(/Left/)!==-1){t.elements.tip.css({left:t.options.style.border.radius-w})}else{if(u.search(/Right/)!==-1){t.elements.tip.css({right:t.options.style.border.radius+w})}}}if(u.search(/top/)!==-1){t.elements.tip.css({top:-positionAdjust})}else{t.elements.tip.css({bottom:positionAdjust})}}else{if(u.search(/left|right/)!==-1){if(f.browser.msie){positionAdjust=(parseInt(f.browser.version.charAt(0))===6)?1:((u.search(/left/)!==-1)?1:2)}if(u.search(/Middle/)!==-1){t.elements.tip.css({top:"50%",marginTop:-(t.options.style.tip.size.height/2)})}else{if(u.search(/Top/)!==-1){t.elements.tip.css({top:t.options.style.border.radius-w})}else{if(u.search(/Bottom/)!==-1){t.elements.tip.css({bottom:t.options.style.border.radius+w})}}}if(u.search(/left/)!==-1){t.elements.tip.css({left:-positionAdjust})}else{t.elements.tip.css({right:positionAdjust})}}}s="padding-"+u.match(/left|right|top|bottom/)[0];x=t.options.style.tip.size[(s.search(/left|right/)!==-1)?"width":"height"];t.elements.tooltip.css("padding",0);t.elements.tooltip.css(s,x);if(f.browser.msie&&parseInt(f.browser.version.charAt(0))==6){v=parseInt(t.elements.tip.css("margin-top"))||0;v+=parseInt(t.elements.content.css("margin-top"))||0;t.elements.tip.css({marginTop:v})}}function j(){var s=this;if(s.elements.title!==null){s.elements.title.remove()}s.elements.title=f('
    ').css(q(s.options.style.title,true)).css({zoom:(f.browser.msie)?1:0}).prependTo(s.elements.contentWrapper);if(s.options.content.title.text){s.updateTitle.call(s,s.options.content.title.text)}if(s.options.content.title.button!==false&&typeof s.options.content.title.button=="string"){s.elements.button=f('').css(q(s.options.style.button,true)).html(s.options.content.title.button).prependTo(s.elements.title).click(function(t){if(!s.status.disabled){s.hide(t)}})}}function l(){var t,v,u,s;t=this;v=t.options.show.when.target;u=t.options.hide.when.target;if(t.options.hide.fixed){u=u.add(t.elements.tooltip)}if(t.options.hide.when.event=="inactive"){s=["click","dblclick","mousedown","mouseup","mousemove","mouseout","mouseenter","mouseleave","mouseover"];function y(z){if(t.status.disabled===true){return}clearTimeout(t.timers.inactive);t.timers.inactive=setTimeout(function(){f(s).each(function(){u.unbind(this+".qtip-inactive");t.elements.content.unbind(this+".qtip-inactive")});t.hide(z)},t.options.hide.delay)}}else{if(t.options.hide.fixed===true){t.elements.tooltip.bind("mouseover.qtip",function(){if(t.status.disabled===true){return}clearTimeout(t.timers.hide)})}}function x(z){if(t.status.disabled===true){return}if(t.options.hide.when.event=="inactive"){f(s).each(function(){u.bind(this+".qtip-inactive",y);t.elements.content.bind(this+".qtip-inactive",y)});y()}clearTimeout(t.timers.show);clearTimeout(t.timers.hide);t.timers.show=setTimeout(function(){t.show(z)},t.options.show.delay)}function w(z){if(t.status.disabled===true){return}if(t.options.hide.fixed===true&&t.options.hide.when.event.search(/mouse(out|leave)/i)!==-1&&f(z.relatedTarget).parents("div.qtip[qtip]").length>0){z.stopPropagation();z.preventDefault();clearTimeout(t.timers.hide);return false}clearTimeout(t.timers.show);clearTimeout(t.timers.hide);t.elements.tooltip.stop(true,true);t.timers.hide=setTimeout(function(){t.hide(z)},t.options.hide.delay)}if((t.options.show.when.target.add(t.options.hide.when.target).length===1&&t.options.show.when.event==t.options.hide.when.event&&t.options.hide.when.event!=="inactive")||t.options.hide.when.event=="unfocus"){t.cache.toggle=0;v.bind(t.options.show.when.event+".qtip",function(z){if(t.cache.toggle==0){x(z)}else{w(z)}})}else{v.bind(t.options.show.when.event+".qtip",x);if(t.options.hide.when.event!=="inactive"){u.bind(t.options.hide.when.event+".qtip",w)}}if(t.options.position.type.search(/(fixed|absolute)/)!==-1){t.elements.tooltip.bind("mouseover.qtip",t.focus)}if(t.options.position.target==="mouse"&&t.options.position.type!=="static"){v.bind("mousemove.qtip",function(z){t.cache.mouse={x:z.pageX,y:z.pageY};if(t.status.disabled===false&&t.options.position.adjust.mouse===true&&t.options.position.type!=="static"&&t.elements.tooltip.css("display")!=="none"){t.updatePosition(z)}})}}function o(u,v,A){var z,s,x,y,t,w;z=this;if(A.corner=="center"){return v.position}s=f.extend({},u);y={x:false,y:false};t={left:(s.left=f.fn.qtip.cache.screen.width+f.fn.qtip.cache.screen.scroll.left),top:(s.top=f.fn.qtip.cache.screen.height+f.fn.qtip.cache.screen.scroll.top)};x={left:(t.left&&(A.corner.search(/right/i)!=-1||(A.corner.search(/right/i)==-1&&!t.right))),right:(t.right&&(A.corner.search(/left/i)!=-1||(A.corner.search(/left/i)==-1&&!t.left))),top:(t.top&&A.corner.search(/top/i)==-1),bottom:(t.bottom&&A.corner.search(/bottom/i)==-1)};if(x.left){if(z.options.position.target!=="mouse"){s.left=v.position.left+v.dimensions.width}else{s.left=z.cache.mouse.x}y.x="Left"}else{if(x.right){if(z.options.position.target!=="mouse"){s.left=v.position.left-A.dimensions.width}else{s.left=z.cache.mouse.x-A.dimensions.width}y.x="Right"}}if(x.top){if(z.options.position.target!=="mouse"){s.top=v.position.top+v.dimensions.height}else{s.top=z.cache.mouse.y}y.y="top"}else{if(x.bottom){if(z.options.position.target!=="mouse"){s.top=v.position.top-A.dimensions.height}else{s.top=z.cache.mouse.y-A.dimensions.height}y.y="bottom"}}if(s.left<0){s.left=u.left;y.x=false}if(s.top<0){s.top=u.top;y.y=false}if(z.options.style.tip.corner!==false){s.corner=new String(A.corner);if(y.x!==false){s.corner=s.corner.replace(/Left|Right|Middle/,y.x)}if(y.y!==false){s.corner=s.corner.replace(/top|bottom/,y.y)}if(s.corner!==z.elements.tip.attr("rel")){e.call(z,s.corner)}}return s}function q(u,t){var v,s;v=f.extend(true,{},u);for(s in v){if(t===true&&s.search(/(tip|classes)/i)!==-1){delete v[s]}else{if(!t&&s.search(/(width|border|tip|title|classes|user)/i)!==-1){delete v[s]}}}return v}function c(s){if(typeof s.tip!=="object"){s.tip={corner:s.tip}}if(typeof s.tip.size!=="object"){s.tip.size={width:s.tip.size,height:s.tip.size}}if(typeof s.border!=="object"){s.border={width:s.border}}if(typeof s.width!=="object"){s.width={value:s.width}}if(typeof s.width.max=="string"){s.width.max=parseInt(s.width.max.replace(/([0-9]+)/i,"$1"))}if(typeof s.width.min=="string"){s.width.min=parseInt(s.width.min.replace(/([0-9]+)/i,"$1"))}if(typeof s.tip.size.x=="number"){s.tip.size.width=s.tip.size.x;delete s.tip.size.x}if(typeof s.tip.size.y=="number"){s.tip.size.height=s.tip.size.y;delete s.tip.size.y}return s}function a(){var s,t,u,x,v,w;s=this;u=[true,{}];for(t=0;t0){v.tip.size.width+=1}if(v.tip.size.height%2>0){v.tip.size.height+=1}if(v.tip.corner===true){v.tip.corner=(s.options.position.corner.tooltip==="center")?false:s.options.position.corner.tooltip}return v}function b(v,u,t){var s={bottomRight:[[0,0],[u,t],[u,0]],bottomLeft:[[0,0],[u,0],[0,t]],topRight:[[0,t],[u,0],[u,t]],topLeft:[[0,0],[0,t],[u,t]],topMiddle:[[0,t],[u/2,0],[u,t]],bottomMiddle:[[0,0],[u,0],[u/2,t]],rightMiddle:[[0,0],[u,t/2],[0,t]],leftMiddle:[[u,0],[u,t],[0,t/2]]};s.leftTop=s.bottomRight;s.rightTop=s.bottomLeft;s.leftBottom=s.topRight;s.rightBottom=s.topLeft;return s[v]}function g(s){var t;if(f("").get(0).getContext){t={topLeft:[s,s],topRight:[0,s],bottomLeft:[s,0],bottomRight:[0,0]}}else{if(f.browser.msie){t={topLeft:[-90,90,0],topRight:[-90,90,-s],bottomLeft:[90,270,0],bottomRight:[90,270,-s]}}}return t}function k(){var s,t,u;s=this;u=s.getDimensions();t='';d.extend(x.prototype,{_scroll:function(){var b=this.qtip.elements.overlay;b&&(b[0].style.top=d(a).scrollTop()+"px")},init:function(c){var e=c.tooltip;d("select, object").length<1&&(this.bgiframe=c.elements.bgiframe=d(Ba).appendTo(e),c._bind(e,"tooltipmove",this.adjustBGIFrame,this._ns,this)),this.redrawContainer=d("
    ",{id:S+"-rcontainer"}).appendTo(b.body),c.elements.overlay&&c.elements.overlay.addClass("qtipmodal-ie6fix")&&(c._bind(a,["scroll","resize"],this._scroll,this._ns,this),c._bind(e,["tooltipshow"],this._scroll,this._ns,this)),this.redraw()},adjustBGIFrame:function(){var a,b,c=this.qtip.tooltip,d={height:c.outerHeight(E),width:c.outerWidth(E)},e=this.qtip.plugins.tip,f=this.qtip.elements.tip;b=parseInt(c.css("borderLeftWidth"),10)||0,b={left:-b,top:-b},e&&f&&(a="x"===e.corner.precedance?[I,L]:[J,K],b[a[1]]-=f[a[0]]()),this.bgiframe.css(b).css(d)},redraw:function(){if(this.qtip.rendered<1||this.drawing)return this;var a,b,c,d,e=this.qtip.tooltip,f=this.qtip.options.style,g=this.qtip.options.position.container;return this.qtip.drawing=1,f.height&&e.css(J,f.height),f.width?e.css(I,f.width):(e.css(I,"").appendTo(this.redrawContainer),b=e.width(),1>b%2&&(b+=1),c=e.css("maxWidth")||"",d=e.css("minWidth")||"",a=(c+d).indexOf("%")>-1?g.width()/100:0,c=(c.indexOf("%")>-1?a:1*parseInt(c,10))||b,d=(d.indexOf("%")>-1?a:1*parseInt(d,10))||0,b=c+d?Math.min(Math.max(b,d),c):b,e.css(I,Math.round(b)).appendTo(g)),this.drawing=0,this},destroy:function(){this.bgiframe&&this.bgiframe.remove(),this.qtip._unbind([a,this.qtip.tooltip],this._ns)}}),Aa=R.ie6=function(a){return 6===da.ie?new x(a):E},Aa.initialize="render",B.ie6={"^content|style$":function(){this.redraw()}}})}(window,document); +//# sourceMappingURL=jquery.qtip.min.map \ No newline at end of file diff --git a/webapp/src/main/webapp/js/menupage/menumanagement_edit.js b/webapp/src/main/webapp/js/menupage/menumanagement_edit.js index e5266bdde..aef6bd54c 100644 --- a/webapp/src/main/webapp/js/menupage/menumanagement_edit.js +++ b/webapp/src/main/webapp/js/menupage/menumanagement_edit.js @@ -77,16 +77,16 @@ var menuManagementEdit = { $('input:checkbox[name=allSelected]').click(function(){ if ( this.checked ) { // if checked, select all the checkboxes - $('input:checkbox[name=classInClassGroup]').attr('checked','checked'); + $('input:checkbox[name=classInClassGroup]').prop('checked','checked'); } else { // if not checked, deselect all the checkboxes - $('input:checkbox[name=classInClassGroup]').removeAttr('checked'); + $('input:checkbox[name=classInClassGroup]').prop('checked', null); } }); $('input:checkbox[name=classInClassGroup]').click(function(){ - $('input:checkbox[name=allSelected]').removeAttr('checked'); + $('input:checkbox[name=allSelected]').prop('checked', null); }); }, validateMenuItemForm: function() { diff --git a/webapp/src/main/webapp/js/menupage/pageManagementUtils.js b/webapp/src/main/webapp/js/menupage/pageManagementUtils.js index 456092bbb..cebf38899 100644 --- a/webapp/src/main/webapp/js/menupage/pageManagementUtils.js +++ b/webapp/src/main/webapp/js/menupage/pageManagementUtils.js @@ -138,8 +138,8 @@ var pageManagementUtils = { }, initDisplay: function(){ //right side components - this.contentTypeSelectOptions.eq(0).attr('selected', 'selected'); - $('select#selectClassGroup option').eq(0).attr('selected', 'selected'); + this.contentTypeSelectOptions.eq(0).prop('selected', 'selected'); + $('select#selectClassGroup option').eq(0).prop('selected', 'selected'); //Why would you want to hide this? This hides everything // $("section#pageDetails").hide(); @@ -152,11 +152,11 @@ var pageManagementUtils = { //left side components //These depend on whether or not this is an existing item or not if(this.isAdd()) { - this.defaultTemplateRadio.attr('checked',true); + this.defaultTemplateRadio.prop('checked',true); //disable save button this.disablePageSave(); if(!this.isAddMenuItem()) { - this.isMenuCheckbox.attr('checked',false); + this.isMenuCheckbox.prop('checked',false); this.menuSection.hide(); } } @@ -243,7 +243,7 @@ var pageManagementUtils = { pageManagementUtils.classGroupSection.hide(); pageManagementUtils.fixedHTMLSection.hide(); pageManagementUtils.sparqlQuerySection.hide(); - pageManagementUtils.contentTypeSelectOptions.eq(0).attr('selected', 'selected'); + pageManagementUtils.contentTypeSelectOptions.eq(0).prop('selected', 'selected'); pageManagementUtils.contentTypeSelect.focus(); pageManagementUtils.adjustSaveButtonHeight(); pageManagementUtils.checkSelfContainedRadio(); @@ -287,7 +287,7 @@ var pageManagementUtils = { pageManagementUtils.sparqlQuerySection.hide(); pageManagementUtils.searchIndividualsSection.hide(); //Reset main content type drop-down - pageManagementUtils.contentTypeSelectOptions.eq(0).attr('selected', 'selected'); + pageManagementUtils.contentTypeSelectOptions.eq(0).prop('selected', 'selected'); if ( pageManagementUtils.leftSideDiv.css("height") != undefined ) { pageManagementUtils.leftSideDiv.css("height",""); if ( pageManagementUtils.leftSideDiv.height() < pageManagementUtils.rightSideDiv.height() ) { @@ -442,7 +442,7 @@ var pageManagementUtils = { }); $el.find("textarea").val(""); //resetting class group section as well so selection is reset if type changes - $el.find("select option:eq(0)").attr("selected", "selected"); + $el.find("select option:eq(0)").prop("selected", "selected"); }, checkTemplateForMultipleContent:function(contentTypeSelected) { @@ -454,7 +454,7 @@ var pageManagementUtils = { //alert the user that they should be picking custom template instead alert(pageManagementUtils.multipleContentWithDefaultTemplateError); //pick custom template - $('input:radio[name=selectedTemplate][value="custom"]').attr("checked", true); + $('input:radio[name=selectedTemplate][value="custom"]').prop("checked", true); pageManagementUtils.handleSelectCustomTemplate(); } @@ -698,7 +698,7 @@ var pageManagementUtils = { //doing this in clear inputs instead which will be triggered //every time content type is changed AS well as on more content button after //original content is cloned and stored - //$('select#selectClassGroup option').eq(0).attr('selected', 'selected'); + //$('select#selectClassGroup option').eq(0).prop('selected', 'selected'); pageManagementUtils.classesForClassGroup.addClass('hidden'); }, chooseClassGroup: function() { @@ -761,19 +761,19 @@ var pageManagementUtils = { $('input:checkbox[name=allSelected]').click(function(){ if ( this.checked ) { // if checked, select all the checkboxes for this particular section - $(this).closest("ul").find('input:checkbox[name=classInClassGroup]').attr('checked','checked'); - //$('input:checkbox[name=classInClassGroup]').attr('checked','checked'); + $(this).closest("ul").find('input:checkbox[name=classInClassGroup]').prop('checked','checked'); + //$('input:checkbox[name=classInClassGroup]').prop('checked','checked'); } else { // if not checked, deselect all the checkboxes - $(this).closest("ul").find('input:checkbox[name=classInClassGroup]').removeAttr('checked'); + $(this).closest("ul").find('input:checkbox[name=classInClassGroup]').prop('checked', null); - // $('input:checkbox[name=classInClassGroup]').removeAttr('checked'); + // $('input:checkbox[name=classInClassGroup]').prop('checked', null); } }); $('input:checkbox[name=classInClassGroup]').click(function(){ - $(this).closest("ul").find('input:checkbox[name=allSelected]').removeAttr('checked'); + $(this).closest("ul").find('input:checkbox[name=allSelected]').prop('checked', null); }); }, //This is SPECIFIC to VIVO so should be moved there updateInternalClassMessage:function(classGroupName) { //User has changed content type diff --git a/webapp/src/main/webapp/js/menupage/processClassGroupDataGetterContent.js b/webapp/src/main/webapp/js/menupage/processClassGroupDataGetterContent.js index 065e9583e..467ff6b99 100644 --- a/webapp/src/main/webapp/js/menupage/processClassGroupDataGetterContent.js +++ b/webapp/src/main/webapp/js/menupage/processClassGroupDataGetterContent.js @@ -95,19 +95,19 @@ var processClassGroupDataGetterContent = { pageContentSection.find('input:checkbox[name=allSelected]').click(function(){ if ( this.checked ) { // if checked, select all the checkboxes for this particular section - $(this).closest("ul").find('input:checkbox[name=classInClassGroup]').attr('checked','checked'); - //$('input:checkbox[name=classInClassGroup]').attr('checked','checked'); + $(this).closest("ul").find('input:checkbox[name=classInClassGroup]').prop('checked','checked'); + //$('input:checkbox[name=classInClassGroup]').prop('checked','checked'); } else { // if not checked, deselect all the checkboxes - $(this).closest("ul").find('input:checkbox[name=classInClassGroup]').removeAttr('checked'); + $(this).closest("ul").find('input:checkbox[name=classInClassGroup]').prop('checked', null); - // $('input:checkbox[name=classInClassGroup]').removeAttr('checked'); + // $('input:checkbox[name=classInClassGroup]').prop('checked', null); } }); pageContentSection.find('input:checkbox[name=classInClassGroup]').click(function(){ - $(this).closest("ul").find('input:checkbox[name=allSelected]').removeAttr('checked'); + $(this).closest("ul").find('input:checkbox[name=allSelected]').prop('checked', null); }); }, bindEventHandlers:function(pageContentSection) { diff --git a/webapp/src/main/webapp/js/menupage/processIndividualsForClassesDataGetterContent.js b/webapp/src/main/webapp/js/menupage/processIndividualsForClassesDataGetterContent.js index a50423657..c79f63a2f 100644 --- a/webapp/src/main/webapp/js/menupage/processIndividualsForClassesDataGetterContent.js +++ b/webapp/src/main/webapp/js/menupage/processIndividualsForClassesDataGetterContent.js @@ -32,10 +32,10 @@ var processIndividualsForClassesDataGetterContent = { var numberSelected = classesSelected.length; var i; //Uncheck all since default is checked - pageContentSection.find("input[name='classInClassGroup']").removeAttr("checked"); + pageContentSection.find("input[name='classInClassGroup']").prop("checked", null); for(i = 0; i < numberSelected; i++) { var classSelected = classesSelected[i]; - pageContentSection.find("input[name='classInClassGroup'][value='" + classSelected + "']").attr("checked", "checked"); + pageContentSection.find("input[name='classInClassGroup'][value='" + classSelected + "']").prop("checked", "checked"); } //If number of classes selected is not equal to total number of classes, uncheck all @@ -45,7 +45,7 @@ var processIndividualsForClassesDataGetterContent = { if(resultsClasses != null) { var numberClasses = resultsClasses.length; if(numberClasses != numberSelected) { - pageContentSection.find("input[name='allSelected']").removeAttr("checked"); + pageContentSection.find("input[name='allSelected']").prop("checked", null); } } } diff --git a/webapp/src/main/webapp/js/searchDownload.js b/webapp/src/main/webapp/js/searchDownload.js index 889bf9c2f..d449cb045 100644 --- a/webapp/src/main/webapp/js/searchDownload.js +++ b/webapp/src/main/webapp/js/searchDownload.js @@ -2,49 +2,39 @@ $(document).ready(function(){ // This function creates and styles the "qTip" tooltip that displays the resource uri and the rdf link when the user clicks the uri/rdf icon. - - $('img#downloadIcon').each(function() - { - $(this).qtip( + + $('head').append(''); + + $('img#downloadIcon').qtip( { + prerender: true, // We need this for the .click() event listener on 'a.close' content: { - prerender: true, // We need this for the .click() event listener on 'a.close' text: '
    ' +'

    ' +'

    ' +'
    ' +'
    close
    ' - +'
    Download the results from this search
    ' - +'
    download results in XML format
    ' - +'
    download results in CSV format
    ' + +'

    ' + +'

    download results in XML format

    ' + +'

    download results in CSV format

    ' +'
    ' - }, position: { - corner: { - target: 'bottomLeft', - tooltip: 'topLeft' - } + my: 'top left', + at: 'bottom left' }, show: { - when: {event: 'click'} + event: 'click' }, hide: { - fixed: true, // Make it fixed so it can be hovered over and interacted with - when: { - target: $('a.close'), - event: 'click' - } + event: 'click' }, style: { - padding: '1em', - width: 500, - backgroundColor: '#f1f2ee' + classes: 'downloadTip', + width: 500 } }); - }); - $( "#slider-vertical" ).slider({ orientation: "vertical", range: "min", diff --git a/webapp/src/main/webapp/js/vitro.js b/webapp/src/main/webapp/js/vitro.js index e57e85ce2..5625840f3 100755 --- a/webapp/src/main/webapp/js/vitro.js +++ b/webapp/src/main/webapp/js/vitro.js @@ -54,7 +54,7 @@ function encodeUrl(pstrString) { */ function addRows(ele, data, cellFuncs, rowFunc) { var frag = document.createDocumentFragment(); - if (DWRUtil._isArray(data)) { + if (dwr.util._isArray(data)) { for (var i = 0; i < data.length; i++) { frag.appendChild( makeRow( data[i], cellFuncs, rowFunc) ); } @@ -77,9 +77,9 @@ function makeRow( row, cellFuncs, rowFunc){ var func = cellFuncs[j] var td var reply = func(row) - if (DWRUtil._isHTMLElement(reply, "td")) { + if (dwr.util._isHTMLElement(reply, "td")) { td = reply; - } else if (DWRUtil._isHTMLElement(reply, "a")) { + } else if (dwr.util._isHTMLElement(reply, "a")) { td = document.createElement("td"); td.appendChild( reply ); } else { @@ -91,7 +91,7 @@ function makeRow( row, cellFuncs, rowFunc){ return tr; } -/** added this from the DWRUtil.js */ +/** added this from the dwr.util.js */ isDate = function(data) { return (data && data.toUTCString) ? true : false; }; @@ -147,8 +147,8 @@ function date2iso8601( jdate ){ function setDateValue(ele, date, date2StrFunc){ if( date == null || ! isDate(date)) { return; } if( date2StrFunc == null ) { date2StrFunc = date2iso8601; } - ele = $(ele); - DWRUtil.setValue(ele, date2StrFunc( date )); + ele = dwr.util.byId(ele); + dwr.util.setValue(ele, date2StrFunc( date )); } /* removes all children and if it can, removes options */ @@ -160,7 +160,7 @@ function clear(ele){ } function hideElementById(eleId){ - var ele = $(eleId); + var ele = dwr.util.byId(eleId); if( ele != null ){ ele.style.display="none"; } } @@ -168,7 +168,7 @@ function makeEntLinkElement(entityURI, text){ link = document.createElement("a"); link.href = "entityEdit?uri=" + encodeUrl(entityURI); link.innerHTML = text; - //DWRUtil.setValue(link, "test" + text); + //dwr.util.setValue(link, "test" + text); return link; } @@ -375,7 +375,7 @@ function adjustIFrameSize(id) { } function toggleDisabled(ele){ - ele = $(ele); + ele = dwr.util.byId(ele); if(ele == null) {return;} ele.disabled = !ele.disabled ; } @@ -403,16 +403,16 @@ and returns a value for use in the select option value. textFun is a function that takes one parameter, an object, and returns a value for use in the select option text. -This is based on the code in DWRUtil but lacks the error checking. +This is based on the code in dwr.util but lacks the error checking. */ addOptions = function(ele, data, valueFunc, textFunc) { var orig = ele; - ele = $(ele); + ele = dwr.util.byId(ele); if (ele == null) { - DWRUtil.debug("addOptions() can't find an element with id: " + orig + "."); + dwr.util.debug("addOptions() can't find an element with id: " + orig + "."); return; } - var useOptions = DWRUtil._isHTMLElement(ele, "select"); + var useOptions = dwr.util._isHTMLElement(ele, "select"); if (data == null) return; var text; diff --git a/webapp/src/main/webapp/templates/edit/specific/ents_edit_head.jsp b/webapp/src/main/webapp/templates/edit/specific/ents_edit_head.jsp index 31bf303fb..2f226cac1 100644 --- a/webapp/src/main/webapp/templates/edit/specific/ents_edit_head.jsp +++ b/webapp/src/main/webapp/templates/edit/specific/ents_edit_head.jsp @@ -12,19 +12,23 @@ String context = request.getContextPath(); if (!(Boolean)request.getAttribute("dwrDisabled")) { %> + + - - diff --git a/webapp/src/main/webapp/templates/freemarker/body/accounts/userAccounts-associateProfilePanel.ftl b/webapp/src/main/webapp/templates/freemarker/body/accounts/userAccounts-associateProfilePanel.ftl index d768e6355..074fe546b 100644 --- a/webapp/src/main/webapp/templates/freemarker/body/accounts/userAccounts-associateProfilePanel.ftl +++ b/webapp/src/main/webapp/templates/freemarker/body/accounts/userAccounts-associateProfilePanel.ftl @@ -5,7 +5,7 @@ <#assign strings = i18n() /> ${stylesheets.add('', - '')} + '')}
    @@ -85,7 +85,8 @@ var associateProfileFieldsData = { }; -${scripts.add('', - '', +${scripts.add('', + '', + '', '')} diff --git a/webapp/src/main/webapp/templates/freemarker/body/accounts/userAccounts-edit.ftl b/webapp/src/main/webapp/templates/freemarker/body/accounts/userAccounts-edit.ftl index 82a6fd225..fab709078 100644 --- a/webapp/src/main/webapp/templates/freemarker/body/accounts/userAccounts-edit.ftl +++ b/webapp/src/main/webapp/templates/freemarker/body/accounts/userAccounts-edit.ftl @@ -89,7 +89,8 @@ ${stylesheets.add('')} ${stylesheets.add('')} -${scripts.add('', - '', +${scripts.add('', + '', + '', '', '')} diff --git a/webapp/src/main/webapp/templates/freemarker/body/accounts/userAccounts-myAccount.ftl b/webapp/src/main/webapp/templates/freemarker/body/accounts/userAccounts-myAccount.ftl index f116f2b3d..3802c7d39 100644 --- a/webapp/src/main/webapp/templates/freemarker/body/accounts/userAccounts-myAccount.ftl +++ b/webapp/src/main/webapp/templates/freemarker/body/accounts/userAccounts-myAccount.ftl @@ -87,5 +87,5 @@ ${stylesheets.add('')} ${stylesheets.add('')} -${scripts.add('', +${scripts.add('', '')} \ No newline at end of file diff --git a/webapp/src/main/webapp/templates/freemarker/body/accounts/userAccounts-myProxiesPanel.ftl b/webapp/src/main/webapp/templates/freemarker/body/accounts/userAccounts-myProxiesPanel.ftl index 7ab9f1b19..d36aeb428 100644 --- a/webapp/src/main/webapp/templates/freemarker/body/accounts/userAccounts-myProxiesPanel.ftl +++ b/webapp/src/main/webapp/templates/freemarker/body/accounts/userAccounts-myProxiesPanel.ftl @@ -61,9 +61,9 @@ var i18nStrings = { ${stylesheets.add('', - '')} + '')} ${scripts.add('', '', '', - '')} \ No newline at end of file + '')} \ No newline at end of file diff --git a/webapp/src/main/webapp/templates/freemarker/body/admin/searchIndex.ftl b/webapp/src/main/webapp/templates/freemarker/body/admin/searchIndex.ftl index d93233bbc..71ac5907d 100644 --- a/webapp/src/main/webapp/templates/freemarker/body/admin/searchIndex.ftl +++ b/webapp/src/main/webapp/templates/freemarker/body/admin/searchIndex.ftl @@ -20,4 +20,4 @@ ${stylesheets.add('')} ${scripts.add('')} -${scripts.add('')} +${scripts.add('')} diff --git a/webapp/src/main/webapp/templates/freemarker/body/contactForm/contactForm-form.ftl b/webapp/src/main/webapp/templates/freemarker/body/contactForm/contactForm-form.ftl index 186e383dc..d6fc209c0 100644 --- a/webapp/src/main/webapp/templates/freemarker/body/contactForm/contactForm-form.ftl +++ b/webapp/src/main/webapp/templates/freemarker/body/contactForm/contactForm-form.ftl @@ -52,7 +52,7 @@ ${stylesheets.add('')} ${scripts.add('', '', - '')} + '')} ', +${scripts.add('', + '', '', '')} diff --git a/webapp/src/main/webapp/templates/freemarker/body/imageUpload/imageUpload-newImage.ftl b/webapp/src/main/webapp/templates/freemarker/body/imageUpload/imageUpload-newImage.ftl index 970eca691..35658839e 100644 --- a/webapp/src/main/webapp/templates/freemarker/body/imageUpload/imageUpload-newImage.ftl +++ b/webapp/src/main/webapp/templates/freemarker/body/imageUpload/imageUpload-newImage.ftl @@ -2,7 +2,8 @@ <#-- Upload a replacement main image for an Individual. --> -${scripts.add('', +${scripts.add('', + '', '')} ${stylesheets.add('')} @@ -37,5 +38,5 @@ ${stylesheets.add(' - i18n_confirmDelete = ${i18n.confirm_delete} + i18n_confirmDelete = "${i18n.confirm_delete}"; diff --git a/webapp/src/main/webapp/templates/freemarker/body/imageUpload/imageUpload-replaceImage.ftl b/webapp/src/main/webapp/templates/freemarker/body/imageUpload/imageUpload-replaceImage.ftl index d4adf239e..fd1378bf3 100644 --- a/webapp/src/main/webapp/templates/freemarker/body/imageUpload/imageUpload-replaceImage.ftl +++ b/webapp/src/main/webapp/templates/freemarker/body/imageUpload/imageUpload-replaceImage.ftl @@ -2,7 +2,8 @@ <#-- Upload a replacement main image for an Individual. --> -${scripts.add('', +${scripts.add('', + '', '')} ${stylesheets.add('')} @@ -38,5 +39,5 @@ ${stylesheets.add(' - i18n_confirmDelete = "${i18n.confirm_delete}" + i18n_confirmDelete = "${i18n.confirm_delete}"; diff --git a/webapp/src/main/webapp/templates/freemarker/body/individual/individual-menu.ftl b/webapp/src/main/webapp/templates/freemarker/body/individual/individual-menu.ftl index 40a901d1b..217e91d91 100644 --- a/webapp/src/main/webapp/templates/freemarker/body/individual/individual-menu.ftl +++ b/webapp/src/main/webapp/templates/freemarker/body/individual/individual-menu.ftl @@ -43,7 +43,7 @@ ${stylesheets.add('', '')} - ${headScripts.add('')} + ${headScripts.add('')} <#assign positionPredicate = "${namespaces.display}menuPosition" /> diff --git a/webapp/src/main/webapp/templates/freemarker/body/individual/individual-vitro.ftl b/webapp/src/main/webapp/templates/freemarker/body/individual/individual-vitro.ftl index 13c7373d9..c421460bf 100644 --- a/webapp/src/main/webapp/templates/freemarker/body/individual/individual-vitro.ftl +++ b/webapp/src/main/webapp/templates/freemarker/body/individual/individual-vitro.ftl @@ -98,9 +98,10 @@ -${stylesheets.add('')} +${stylesheets.add('', + '')} -${headScripts.add('', +${headScripts.add('', '')} ${scripts.add('', @@ -108,5 +109,5 @@ ${scripts.add('')} diff --git a/webapp/src/main/webapp/templates/freemarker/body/individual/manageLabelsForIndividual.ftl b/webapp/src/main/webapp/templates/freemarker/body/individual/manageLabelsForIndividual.ftl index ec0b78a2a..24fe1f2ce 100644 --- a/webapp/src/main/webapp/templates/freemarker/body/individual/manageLabelsForIndividual.ftl +++ b/webapp/src/main/webapp/templates/freemarker/body/individual/manageLabelsForIndividual.ftl @@ -102,9 +102,9 @@ var i18nStrings = { }; -${stylesheets.add('')} +${stylesheets.add('')} ${scripts.add('', - '', + '', '')} diff --git a/webapp/src/main/webapp/templates/freemarker/body/individual/viewLabelsForIndividual.ftl b/webapp/src/main/webapp/templates/freemarker/body/individual/viewLabelsForIndividual.ftl index 4a55168f2..abdce527e 100644 --- a/webapp/src/main/webapp/templates/freemarker/body/individual/viewLabelsForIndividual.ftl +++ b/webapp/src/main/webapp/templates/freemarker/body/individual/viewLabelsForIndividual.ftl @@ -38,8 +38,8 @@ -${stylesheets.add('')} +${stylesheets.add('')} ${scripts.add('', - '')} + '')} diff --git a/webapp/src/main/webapp/templates/freemarker/body/manageproxies/manageProxies-list.ftl b/webapp/src/main/webapp/templates/freemarker/body/manageproxies/manageProxies-list.ftl index 8405b3096..c1752bae4 100644 --- a/webapp/src/main/webapp/templates/freemarker/body/manageproxies/manageProxies-list.ftl +++ b/webapp/src/main/webapp/templates/freemarker/body/manageproxies/manageProxies-list.ftl @@ -196,10 +196,11 @@ var i18nStrings = { ${stylesheets.add('')} ${stylesheets.add('')} ${stylesheets.add('')} -${stylesheets.add('')} +${stylesheets.add('')} -${scripts.add('', - '')} +${scripts.add('', + '', + '')} ${scripts.add('', '', '')} diff --git a/webapp/src/main/webapp/templates/freemarker/body/pagemanagement/pageList.ftl b/webapp/src/main/webapp/templates/freemarker/body/pagemanagement/pageList.ftl index d149f60cb..9a7a44e80 100644 --- a/webapp/src/main/webapp/templates/freemarker/body/pagemanagement/pageList.ftl +++ b/webapp/src/main/webapp/templates/freemarker/body/pagemanagement/pageList.ftl @@ -78,10 +78,10 @@ confirmPageDeletion: '${i18n().confirm_page_deletion}' }; -${stylesheets.add('', +${stylesheets.add('', '')} -${scripts.add('')} +${scripts.add('')} ${scripts.add('')} ${scripts.add('')} ${scripts.add('')} diff --git a/webapp/src/main/webapp/templates/freemarker/body/search/search-pagedResults.ftl b/webapp/src/main/webapp/templates/freemarker/body/search/search-pagedResults.ftl index 0c8ed5ed2..baa2dc6a4 100644 --- a/webapp/src/main/webapp/templates/freemarker/body/search/search-pagedResults.ftl +++ b/webapp/src/main/webapp/templates/freemarker/body/search/search-pagedResults.ftl @@ -20,7 +20,7 @@ var urlsBase = '${urls.base}'; - Download Results + ${i18n().download_results} <#-- --> @@ -42,7 +42,7 @@ <#if classLinks?has_content>
    <#if classGroupName?has_content> -

    ${i18n().limit} ${classGroupName} to

    +

    ${i18n().limit} ${classGroupName} ${i18n().to}

    <#else>

    ${i18n().limit_to}

    @@ -67,7 +67,7 @@ <#-- Paging controls --> <#if (pagingLinks?size > 0)>
    - Pages: + ${i18n().pages}: <#if prevPage??> <#list pagingLinks as link> <#if link.url??> @@ -109,10 +109,11 @@
    ${stylesheets.add('', - '')} + '', + '')} ${headScripts.add('', - '', + '', '' )} diff --git a/webapp/src/main/webapp/templates/freemarker/body/siteAdmin/siteAdmin-classHierarchy.ftl b/webapp/src/main/webapp/templates/freemarker/body/siteAdmin/siteAdmin-classHierarchy.ftl index 123322d67..6c2c80340 100644 --- a/webapp/src/main/webapp/templates/freemarker/body/siteAdmin/siteAdmin-classHierarchy.ftl +++ b/webapp/src/main/webapp/templates/freemarker/body/siteAdmin/siteAdmin-classHierarchy.ftl @@ -59,6 +59,6 @@ $(document).ready(function() { ${stylesheets.add('')} -${scripts.add('', +${scripts.add('', '')} diff --git a/webapp/src/main/webapp/templates/freemarker/body/siteAdmin/siteAdmin-dataInput.ftl b/webapp/src/main/webapp/templates/freemarker/body/siteAdmin/siteAdmin-dataInput.ftl index c79a7c90b..d53727723 100644 --- a/webapp/src/main/webapp/templates/freemarker/body/siteAdmin/siteAdmin-dataInput.ftl +++ b/webapp/src/main/webapp/templates/freemarker/body/siteAdmin/siteAdmin-dataInput.ftl @@ -23,7 +23,7 @@
    -${stylesheets.add('')} +${stylesheets.add('')} -${scripts.add('', +${scripts.add('', '')} \ No newline at end of file diff --git a/webapp/src/main/webapp/templates/freemarker/body/siteAdmin/siteAdmin-fauxPropertiesList.ftl b/webapp/src/main/webapp/templates/freemarker/body/siteAdmin/siteAdmin-fauxPropertiesList.ftl index 3f3260eb4..f3618ba02 100644 --- a/webapp/src/main/webapp/templates/freemarker/body/siteAdmin/siteAdmin-fauxPropertiesList.ftl +++ b/webapp/src/main/webapp/templates/freemarker/body/siteAdmin/siteAdmin-fauxPropertiesList.ftl @@ -103,9 +103,7 @@ @@ -118,6 +116,6 @@ $(document).ready(function() { ${stylesheets.add('')} -${scripts.add('', +${scripts.add('', '')} diff --git a/webapp/src/main/webapp/templates/freemarker/body/siteAdmin/siteAdmin-objectPropHierarchy.ftl b/webapp/src/main/webapp/templates/freemarker/body/siteAdmin/siteAdmin-objectPropHierarchy.ftl index b0f60fa25..457eb8f42 100644 --- a/webapp/src/main/webapp/templates/freemarker/body/siteAdmin/siteAdmin-objectPropHierarchy.ftl +++ b/webapp/src/main/webapp/templates/freemarker/body/siteAdmin/siteAdmin-objectPropHierarchy.ftl @@ -73,6 +73,6 @@ $(document).ready(function() { ${stylesheets.add('')} -${scripts.add('', +${scripts.add('', '')} diff --git a/webapp/src/main/webapp/templates/freemarker/edit/forms/autoCompleteObjectPropForm.ftl b/webapp/src/main/webapp/templates/freemarker/edit/forms/autoCompleteObjectPropForm.ftl index 457a02d31..0d5040d33 100644 --- a/webapp/src/main/webapp/templates/freemarker/edit/forms/autoCompleteObjectPropForm.ftl +++ b/webapp/src/main/webapp/templates/freemarker/edit/forms/autoCompleteObjectPropForm.ftl @@ -134,12 +134,12 @@ Also multiple types parameter set to true only if more than one type returned--> edu.cornell.mannlib.vitro.webapp.edit.n3editing.configuration.generators.AddAttendeeRoleToPersonGenerator --> -${stylesheets.add('')} +${stylesheets.add('')} ${stylesheets.add('')} ${stylesheets.add('')} - ${scripts.add('', + ${scripts.add('', '', '', '')} diff --git a/webapp/src/main/webapp/templates/freemarker/edit/forms/pageManagement.ftl b/webapp/src/main/webapp/templates/freemarker/edit/forms/pageManagement.ftl index c63a0fa6f..86e7ac3c0 100644 --- a/webapp/src/main/webapp/templates/freemarker/edit/forms/pageManagement.ftl +++ b/webapp/src/main/webapp/templates/freemarker/edit/forms/pageManagement.ftl @@ -196,7 +196,7 @@ ${stylesheets.add('')} ${stylesheets.add('')} -${scripts.add('')} +${scripts.add('')} ${scripts.add('')} ${scripts.add('')} ${scripts.add('')} diff --git a/webapp/src/main/webapp/templates/freemarker/page/partials/developer.ftl b/webapp/src/main/webapp/templates/freemarker/page/partials/developer.ftl index efc60beee..54ea51f45 100644 --- a/webapp/src/main/webapp/templates/freemarker/page/partials/developer.ftl +++ b/webapp/src/main/webapp/templates/freemarker/page/partials/developer.ftl @@ -3,7 +3,7 @@
    ${scripts.add('')} -${scripts.add('')} +${scripts.add('')} diff --git a/webapp/src/main/webapp/templates/freemarker/page/partials/headScripts.ftl b/webapp/src/main/webapp/templates/freemarker/page/partials/headScripts.ftl index 61c7df455..bd45f204e 100644 --- a/webapp/src/main/webapp/templates/freemarker/page/partials/headScripts.ftl +++ b/webapp/src/main/webapp/templates/freemarker/page/partials/headScripts.ftl @@ -6,7 +6,8 @@ var i18nStrings = { allCapitalized: '${i18n().all_capitalized}', }; - + + <#-- script for enabling new HTML5 semantic markup in IE browsers -->