/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.jena.datatypes; import java.util.Objects ; import org.apache.jena.graph.impl.LiteralLabel ; /** * Base level implementation of datatype from which real implementations * can inherit. */ public class BaseDatatype implements RDFDatatype { /** The URI label for this data type */ protected String uri; /** * Constructor. * @param uri the URI label to use for this datatype */ public BaseDatatype(String uri) { this.uri = uri; } /** * Return the URI which is the label for this datatype */ @Override public String getURI() { return uri; } /** * Pair object used to encode both lexical form * and datatype for a typed literal with unknown * datatype. */ public static class TypedValue { public final String lexicalValue; public final String datatypeURI; public TypedValue(String lexicalValue, String datatypeURI) { this.lexicalValue = lexicalValue; this.datatypeURI = datatypeURI; } @Override public boolean equals(Object other) { if (other instanceof TypedValue) { return lexicalValue.equals(((TypedValue)other).lexicalValue) && datatypeURI.equals(((TypedValue)other).datatypeURI); } else { return false; } } @Override public int hashCode() { return lexicalValue.hashCode() ^ datatypeURI.hashCode(); } } /** * Convert a value of this datatype out * to lexical form. */ @Override public String unparse(Object value) { // Default implementation expects a parsed TypedValue but will // accept a pure lexical form if (value instanceof TypedValue) { return ((TypedValue)value).lexicalValue; } return value.toString(); } /** * Parse a lexical form of this datatype to a value * @throws DatatypeFormatException if the lexical form is not legal */ @Override public Object parse(String lexicalForm) throws DatatypeFormatException { return new TypedValue(lexicalForm, getURI()); } /** * Test whether the given string is a legal lexical form * of this datatype. */ @Override public boolean isValid(String lexicalForm) { try { parse(lexicalForm); return true; } catch (DatatypeFormatException e) { return false; } } /** * Test whether the given LiteralLabel is a valid instance * of this datatype. This takes into accound typing information * as well as lexical form - for example an xsd:string is * never considered valid as an xsd:integer (even if it is * lexically legal like "1"). */ @Override public boolean isValidLiteral(LiteralLabel lit) { // default is that only literals with the same type are valid return equals(lit.getDatatype()); } /** * Test whether the given object is a legal value form * of this datatype. */ @Override public boolean isValidValue(Object valueForm) { // Default to brute force return isValid(unparse(valueForm)); } /** * Compares two instances of values of the given datatype. * This default requires value and datatype equality. */ @Override public boolean isEqual(LiteralLabel litLabel1, LiteralLabel litLabel2) { return isEqualPlain(litLabel1, litLabel2) ; } /** The default for equality - same datatype, same value */ protected static boolean isEqualPlain(LiteralLabel litLabel1, LiteralLabel litLabel2) { return litLabel1.getDatatype() == litLabel2.getDatatype() && litLabel1.getValue().equals(litLabel2.getValue()); } /** * Equality for datatypes based solely on lexcial form, * i.e. there value space is equivalent to their lexical space. */ protected static boolean isEqualByTerm(LiteralLabel value1, LiteralLabel value2) { if ( value2 == null && value1 == null ) return true ; if ( value2 == null ) return false ; if ( value1 == null ) return false ; return Objects.equals(value1.getLexicalForm(), value2.getLexicalForm()) && Objects.equals(value1.getDatatype(), value2.getDatatype()) && Objects.equals(value1.language(), value2.language()) ; } /** Default implementation of getHashCode() delegates to the default from the literal label. */ @Override public int getHashCode( LiteralLabel lit ) { return lit.getDefaultHashcode(); } /** * Helper function to compare language tag values */ public boolean langTagCompatible(LiteralLabel value1, LiteralLabel value2) { if (value1.language() == null) { return (value2.language() == null || value2.language().equals("")); } else { return value1.language().equalsIgnoreCase(value2.language()); } } /** * Returns the java class which is used to represent value * instances of this datatype. */ @Override public Class<?> getJavaClass() { return null; } /** * Cannonicalise a java Object value to a normal form. * Primarily used in cases such as xsd:integer to reduce * the Java object representation to the narrowest of the Number * subclasses to ensure that indexing of typed literals works. */ @Override public Object cannonicalise( Object value ) { return value; } /** * Returns an object giving more details on the datatype. * This is type system dependent. In the case of XSD types * this will be an instance of * <code>org.apache.xerces.impl.xs.psvi.XSTypeDefinition</code>. */ @Override public Object extendedTypeDefinition() { return null; } /** * Normalization. If the value is narrower than the current data type * (e.g. value is xsd:date but the time is xsd:datetime) returns * the narrower type for the literal. * If the type is narrower than the value then it may normalize * the value (e.g. set the mask of an XSDDateTime) * Currently only used to narrow gener XSDDateTime objects * to the minimal XSD date/time type. * @param value the current object value * @param dt the currently set data type * @return a narrower version of the datatype based on the actual value range */ @Override public RDFDatatype normalizeSubType(Object value, RDFDatatype dt) { return this; // default is no narrowing } /** * Display format */ @Override public String toString() { return "Datatype[" + uri + (getJavaClass() == null ? "" : " -> " + getJavaClass()) + "]"; } }