001/** 002 * Copyright 2015 DuraSpace, Inc. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.fcrepo.kernel.impl.rdf.converters; 017 018import com.google.common.base.Converter; 019import com.google.common.base.Splitter; 020import com.hp.hpl.jena.datatypes.BaseDatatype; 021import com.hp.hpl.jena.datatypes.RDFDatatype; 022import com.hp.hpl.jena.rdf.model.Literal; 023import com.hp.hpl.jena.rdf.model.RDFNode; 024import com.hp.hpl.jena.rdf.model.Resource; 025import org.fcrepo.kernel.models.FedoraResource; 026import org.fcrepo.kernel.exception.RepositoryRuntimeException; 027import org.slf4j.Logger; 028 029import javax.jcr.Node; 030import javax.jcr.RepositoryException; 031import javax.jcr.Session; 032import javax.jcr.Value; 033import javax.jcr.ValueFactory; 034 035import java.util.Iterator; 036 037import static com.hp.hpl.jena.rdf.model.ResourceFactory.createLangLiteral; 038import static com.hp.hpl.jena.rdf.model.ResourceFactory.createPlainLiteral; 039import static com.hp.hpl.jena.rdf.model.ResourceFactory.createResource; 040import static com.hp.hpl.jena.rdf.model.ResourceFactory.createTypedLiteral; 041import static javax.jcr.PropertyType.BOOLEAN; 042import static javax.jcr.PropertyType.DATE; 043import static javax.jcr.PropertyType.DECIMAL; 044import static javax.jcr.PropertyType.DOUBLE; 045import static javax.jcr.PropertyType.LONG; 046import static javax.jcr.PropertyType.PATH; 047import static javax.jcr.PropertyType.REFERENCE; 048import static javax.jcr.PropertyType.STRING; 049import static javax.jcr.PropertyType.UNDEFINED; 050import static javax.jcr.PropertyType.URI; 051import static javax.jcr.PropertyType.WEAKREFERENCE; 052import static org.fcrepo.kernel.impl.identifiers.NodeResourceConverter.nodeToResource; 053import static org.slf4j.LoggerFactory.getLogger; 054 055/** 056 * @author cabeer 057 * @since 10/8/14 058 */ 059public class ValueConverter extends Converter<Value, RDFNode> { 060 061 private static final Logger LOGGER = getLogger(ValueConverter.class); 062 063 private final Session session; 064 private final Converter<Node, Resource> graphSubjects; 065 066 /** 067 * Convert values between JCR values and RDF objects with the given session and subjects 068 * @param session the session 069 * @param graphSubjects the graph subjects 070 */ 071 public ValueConverter(final Session session, 072 final Converter<Resource, FedoraResource> graphSubjects) { 073 this.session = session; 074 this.graphSubjects = nodeToResource(graphSubjects); 075 } 076 077 @Override 078 protected RDFNode doForward(final Value value) { 079 try { 080 switch (value.getType()) { 081 case BOOLEAN: 082 return literal2node(value.getBoolean()); 083 case DATE: 084 return literal2node(value.getDate()); 085 case DECIMAL: 086 return literal2node(value.getDecimal()); 087 case DOUBLE: 088 return literal2node(value.getDouble()); 089 case LONG: 090 return literal2node(value.getLong()); 091 case URI: 092 return createResource(value.getString()); 093 case REFERENCE: 094 case WEAKREFERENCE: 095 case PATH: 096 return traverseLink(value); 097 default: 098 return stringliteral2node(value.getString()); 099 } 100 } catch (final RepositoryException e) { 101 throw new RepositoryRuntimeException(e); 102 } 103 } 104 105 @Override 106 protected Value doBackward(final RDFNode resource) { 107 108 try { 109 110 final ValueFactory valueFactory = session.getValueFactory(); 111 112 if (resource.isAnon()) { 113 // a non-URI resource (e.g. a blank node) 114 return valueFactory.createValue(resource.toString(), UNDEFINED); 115 } 116 117 final RdfLiteralJcrValueBuilder rdfLiteralJcrValueBuilder = new RdfLiteralJcrValueBuilder(); 118 119 if (resource.isURIResource()) { 120 rdfLiteralJcrValueBuilder.value(resource.asResource().getURI()).datatype("URI"); 121 } else { 122 123 final Literal literal = resource.asLiteral(); 124 final RDFDatatype dataType = literal.getDatatype(); 125 126 rdfLiteralJcrValueBuilder.value(literal.getString()).datatype(dataType).lang(literal.getLanguage()); 127 } 128 129 return valueFactory.createValue(rdfLiteralJcrValueBuilder.toString(), STRING); 130 } catch (final RepositoryException e) { 131 throw new RepositoryRuntimeException(e); 132 } 133 } 134 135 private static Literal literal2node(final Object literal) { 136 final Literal result = createTypedLiteral(literal); 137 LOGGER.trace("Converting {} into {}", literal, result); 138 return result; 139 } 140 141 142 private static RDFNode stringliteral2node(final String literal) { 143 final RdfLiteralJcrValueBuilder rdfLiteralJcrValueBuilder = new RdfLiteralJcrValueBuilder(literal); 144 145 if (rdfLiteralJcrValueBuilder.hasLang()) { 146 return createLangLiteral(rdfLiteralJcrValueBuilder.value(), rdfLiteralJcrValueBuilder.lang()); 147 } else if (rdfLiteralJcrValueBuilder.isResource()) { 148 return createResource(rdfLiteralJcrValueBuilder.value()); 149 } else if (rdfLiteralJcrValueBuilder.hasDatatypeUri()) { 150 return createTypedLiteral(rdfLiteralJcrValueBuilder.value(), rdfLiteralJcrValueBuilder.datatype()); 151 } else { 152 return createPlainLiteral(literal); 153 } 154 } 155 156 private RDFNode traverseLink(final Value v) 157 throws RepositoryException { 158 final javax.jcr.Node refNode; 159 if (v.getType() == PATH) { 160 refNode = session.getNode(v.getString()); 161 } else { 162 refNode = session.getNodeByIdentifier(v.getString()); 163 } 164 return getGraphSubject(refNode); 165 } 166 167 private RDFNode getGraphSubject(final javax.jcr.Node n) { 168 return graphSubjects.convert(n); 169 } 170 171 protected static class RdfLiteralJcrValueBuilder { 172 private static final String FIELD_DELIMITER = "\30^^\30"; 173 public static final Splitter JCR_VALUE_SPLITTER = Splitter.on(FIELD_DELIMITER); 174 175 private String value; 176 private String datatypeUri; 177 private String lang; 178 179 RdfLiteralJcrValueBuilder() { 180 181 } 182 183 public RdfLiteralJcrValueBuilder(final String literal) { 184 this(); 185 186 final Iterator<String> tokenizer = JCR_VALUE_SPLITTER.split(literal).iterator(); 187 188 value = tokenizer.next(); 189 190 if (tokenizer.hasNext()) { 191 datatypeUri = tokenizer.next(); 192 } 193 194 if (tokenizer.hasNext()) { 195 lang = tokenizer.next(); 196 } 197 } 198 199 @Override 200 public String toString() { 201 final StringBuilder b = new StringBuilder(); 202 203 b.append(value); 204 205 if (hasDatatypeUri()) { 206 b.append(FIELD_DELIMITER); 207 b.append(datatypeUri); 208 } else if (hasLang()) { 209 // if it has a language, but not a datatype, add a placeholder. 210 b.append(FIELD_DELIMITER); 211 } 212 213 if (hasLang()) { 214 b.append(FIELD_DELIMITER); 215 b.append(lang); 216 } 217 218 return b.toString(); 219 220 } 221 222 public String value() { 223 return value; 224 } 225 226 public RDFDatatype datatype() { 227 if (hasDatatypeUri()) { 228 return new BaseDatatype(datatypeUri); 229 } 230 return null; 231 } 232 233 public String lang() { 234 return lang; 235 } 236 237 public RdfLiteralJcrValueBuilder value(final String value) { 238 this.value = value; 239 return this; 240 } 241 242 public RdfLiteralJcrValueBuilder datatype(final String datatypeUri) { 243 this.datatypeUri = datatypeUri; 244 return this; 245 } 246 247 public RdfLiteralJcrValueBuilder datatype(final RDFDatatype datatypeUri) { 248 if (datatypeUri != null && !datatypeUri.getURI().isEmpty()) { 249 this.datatypeUri = datatypeUri.getURI(); 250 } 251 return this; 252 } 253 254 public RdfLiteralJcrValueBuilder lang(final String lang) { 255 this.lang = lang; 256 return this; 257 } 258 259 public boolean hasLang() { 260 return lang != null && !lang.isEmpty(); 261 } 262 263 public boolean hasDatatypeUri() { 264 return datatypeUri != null && !datatypeUri.isEmpty(); 265 } 266 267 public boolean isResource() { 268 return hasDatatypeUri() && datatypeUri.equals("URI"); 269 } 270 } 271}