Java tutorial
/* * 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.rya.indexing.smarturi; import java.io.UnsupportedEncodingException; import java.net.URISyntaxException; import java.net.URLDecoder; import java.util.ArrayList; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import org.apache.commons.lang3.StringUtils; import org.apache.http.NameValuePair; import org.apache.http.client.utils.URIBuilder; import org.apache.http.client.utils.URLEncodedUtils; import org.apache.http.message.BasicNameValuePair; import org.apache.rya.api.domain.RyaType; import org.apache.rya.api.domain.RyaURI; import org.apache.rya.api.resolver.RdfToRyaConversions; import org.apache.rya.api.resolver.RyaToRdfConversions; import org.apache.rya.indexing.entity.model.Entity; import org.apache.rya.indexing.entity.model.Property; import org.joda.time.DateTime; import org.joda.time.format.ISODateTimeFormat; import org.openrdf.model.URI; import org.openrdf.model.Value; import org.openrdf.model.impl.URIImpl; import org.openrdf.model.vocabulary.XMLSchema; import com.google.common.base.Charsets; import com.google.common.collect.HashBiMap; import com.google.common.collect.ImmutableMap; import com.google.common.primitives.Doubles; import com.google.common.primitives.Floats; import com.google.common.primitives.Ints; import com.google.common.primitives.Longs; /** * Interface for serializing and deserializing Smart URIs. */ public class SmartUriAdapter { private static final String ENTITY_TYPE_MAP_URN = "urn://entityTypeMap"; private static final URI RYA_TYPES_URI = new URIImpl("urn://ryaTypes"); /** * Private constructor to prevent instantiation. */ private SmartUriAdapter() { } private static URI createTypePropertiesUri( final ImmutableMap<RyaURI, ImmutableMap<RyaURI, Property>> typeProperties) throws SmartUriException { final List<NameValuePair> nameValuePairs = new ArrayList<>(); for (final Entry<RyaURI, ImmutableMap<RyaURI, Property>> typeProperty : typeProperties.entrySet()) { final RyaURI type = typeProperty.getKey(); final Map<RyaURI, Property> propertyMap = typeProperty.getValue(); final URI typeUri = createIndividualTypeWithPropertiesUri(type, propertyMap); final String keyString = type.getDataType().getLocalName(); final String valueString = typeUri.getLocalName(); nameValuePairs.add(new BasicNameValuePair(keyString, valueString)); } final URIBuilder uriBuilder = new URIBuilder(); uriBuilder.addParameters(nameValuePairs); String uriString; try { final java.net.URI uri = uriBuilder.build(); final String queryString = uri.getRawSchemeSpecificPart(); uriString = "urn:test" + queryString; } catch (final URISyntaxException e) { throw new SmartUriException("Unable to create type properties for the Smart URI", e); } return new URIImpl(uriString); } private static String getShortNameForType(final RyaURI type) throws SmartUriException { final String shortName = new URIImpl(type.getData()).getLocalName(); return shortName; } private static String addTypePrefixToUri(final String uriString, final String typePrefix) { final String localName = new URIImpl(uriString).getLocalName(); final String beginning = StringUtils.removeEnd(uriString, localName); final String formattedUriString = beginning + typePrefix + localName; return formattedUriString; } private static String removeTypePrefixFromUri(final String uriString, final String typePrefix) { final String localName = new URIImpl(uriString).getLocalName(); final String beginning = StringUtils.removeEnd(uriString, localName); final String replacement = localName.replaceFirst(typePrefix + ".", ""); final String formattedUriString = beginning + replacement; return formattedUriString; } private static Map<RyaURI, String> createTypeMap(final List<RyaURI> types) throws SmartUriException { final Map<RyaURI, String> map = new LinkedHashMap<>(); for (final RyaURI type : types) { final String shortName = getShortNameForType(type); map.put(type, shortName); } return map; } private static URI createTypeMapUri(final List<RyaURI> types) throws SmartUriException { final List<NameValuePair> nameValuePairs = new ArrayList<>(); for (final RyaURI type : types) { final String shortName = getShortNameForType(type); nameValuePairs.add(new BasicNameValuePair(type.getData(), shortName)); } final URIBuilder uriBuilder = new URIBuilder(); uriBuilder.addParameters(nameValuePairs); String uriString; try { final java.net.URI uri = uriBuilder.build(); final String queryString = uri.getRawSchemeSpecificPart(); uriString = ENTITY_TYPE_MAP_URN + queryString; } catch (final URISyntaxException e) { throw new SmartUriException("Unable to create type properties for the Smart URI", e); } return new URIImpl(uriString); } private static Map<RyaURI, String> convertUriToTypeMap(final URI typeMapUri) throws SmartUriException { final Map<RyaURI, String> map = new HashMap<>(); java.net.URI uri; try { final URIBuilder uriBuilder = new URIBuilder(typeMapUri.stringValue()); uri = uriBuilder.build(); } catch (final URISyntaxException e) { throw new SmartUriException("Unable to parse Rya type map in Smart URI", e); } final List<NameValuePair> params = URLEncodedUtils.parse(uri, Charsets.UTF_8.name()); for (final NameValuePair param : params) { final String name = param.getName(); final String value = param.getValue(); final RyaURI type = new RyaURI(name); map.put(type, value); } return map; } private static URI createIndividualTypeWithPropertiesUri(final RyaURI type, final Map<RyaURI, Property> map) throws SmartUriException { final List<NameValuePair> nameValuePairs = new ArrayList<>(); for (final Entry<RyaURI, Property> entry : map.entrySet()) { final RyaURI key = entry.getKey(); final Property property = entry.getValue(); final RyaType ryaType = property.getValue(); final String keyString = (new URIImpl(key.getData())).getLocalName(); final Value value = RyaToRdfConversions.convertValue(ryaType); final String valueString = value.stringValue(); nameValuePairs.add(new BasicNameValuePair(keyString, valueString)); } final URIBuilder uriBuilder = new URIBuilder(); uriBuilder.addParameters(nameValuePairs); String uriString; try { final java.net.URI uri = uriBuilder.build(); final String queryString = uri.getRawSchemeSpecificPart(); uriString = type.getData()/*new URIImpl(type.getData()).getLocalName()*/ + queryString; } catch (final URISyntaxException e) { throw new SmartUriException("Unable to create type URI with all its properties for the Smart URI", e); } return new URIImpl(uriString); } private static Entity convertMapToEntity(final RyaURI subject, final Map<RyaURI, Map<URI, Value>> map) { final Entity.Builder entityBuilder = Entity.builder(); entityBuilder.setSubject(subject); for (final Entry<RyaURI, Map<URI, Value>> typeEntry : map.entrySet()) { final RyaURI type = typeEntry.getKey(); final Map<URI, Value> subMap = typeEntry.getValue(); entityBuilder.setExplicitType(type); for (final Entry<URI, Value> entry : subMap.entrySet()) { final URI uri = entry.getKey(); final Value value = entry.getValue(); final RyaURI ryaUri = new RyaURI(uri.stringValue()); final RyaURI ryaName = new RyaURI(uri.stringValue()); final RyaType ryaType = new RyaType(value.stringValue()); final Property property = new Property(ryaName, ryaType); entityBuilder.setProperty(ryaUri, property); } } final Entity entity = entityBuilder.build(); return entity; } public static RyaURI findSubject(final URI uri) throws SmartUriException { final String uriString = uri.stringValue(); return findSubject(uriString); } public static RyaURI findSubject(final String uriString) throws SmartUriException { java.net.URI uri; try { uri = new java.net.URI(uriString); } catch (final URISyntaxException e) { throw new SmartUriException("Could not find subject in Smart URI", e); } final RyaURI subject; final String fullFragment = uri.getFragment(); if (fullFragment != null) { final int queryPosition = fullFragment.indexOf("?"); String partialFragment = null; if (queryPosition != -1) { partialFragment = fullFragment.substring(0, queryPosition); } final String subjectString = uri.getScheme() + ":" + uri.getSchemeSpecificPart() + "#" + partialFragment; subject = new RyaURI(subjectString); } else { final int queryPosition = uriString.indexOf("?"); String subjectString = null; if (queryPosition != -1) { subjectString = uriString.substring(0, queryPosition); } else { subjectString = uriString; } subject = new RyaURI(subjectString); } return subject; } /** * Serializes an {@link Entity} into a Smart {@link URI}. * @param entity the {@link Entity} to serialize into a Smart URI. * @return the Smart {@link URI}. * @throws SmartUriException */ public static URI serializeUriEntity(final Entity entity) throws SmartUriException { final Map<URI, Value> objectMap = new LinkedHashMap<>(); // Adds the entity's types to the Smart URI final List<RyaURI> typeIds = entity.getExplicitTypeIds(); final Map<RyaURI, String> ryaTypeMap = createTypeMap(typeIds); final URI ryaTypeMapUri = createTypeMapUri(typeIds); final RyaType valueRyaType = new RyaType(XMLSchema.ANYURI, ryaTypeMapUri.stringValue()); final Value typeValue = RyaToRdfConversions.convertValue(valueRyaType); objectMap.put(RYA_TYPES_URI, typeValue); final RyaURI subject = entity.getSubject(); final Map<RyaURI, ImmutableMap<RyaURI, Property>> typeMap = entity.getProperties(); for (final Entry<RyaURI, ImmutableMap<RyaURI, Property>> typeEntry : typeMap.entrySet()) { final RyaURI type = typeEntry.getKey(); String typeShortName = ryaTypeMap.get(type); typeShortName = typeShortName != null ? typeShortName + "." : ""; final ImmutableMap<RyaURI, Property> typeProperties = typeEntry.getValue(); for (final Entry<RyaURI, Property> properties : typeProperties.entrySet()) { final RyaURI key = properties.getKey(); final Property property = properties.getValue(); final String valueString = property.getValue().getData(); final RyaType ryaType = property.getValue(); //final RyaType ryaType = new RyaType(new URIImpl(key.getData()), valueString); final Value value = RyaToRdfConversions.convertValue(ryaType); String formattedKey = key.getData(); if (StringUtils.isNotBlank(typeShortName)) { formattedKey = addTypePrefixToUri(formattedKey, typeShortName); } final URI uri = new URIImpl(formattedKey); objectMap.put(uri, value); } } return serializeUri(subject, objectMap); } /** * Serializes a map into a URI. * @param subject the {@link RyaURI} subject of the Entity. Identifies the * thing that is being represented as an Entity. * @param map the {@link Map} of {@link URI}s to {@link Value}s. * @return the Smart {@link URI}. * @throws SmartUriException */ public static URI serializeUri(final RyaURI subject, final Map<URI, Value> map) throws SmartUriException { final String subjectData = subject.getData(); final int fragmentPosition = subjectData.indexOf("#"); String prefix = subjectData; String fragment = null; if (fragmentPosition > -1) { prefix = subjectData.substring(0, fragmentPosition); fragment = subjectData.substring(fragmentPosition + 1, subjectData.length()); } URIBuilder uriBuilder = null; try { if (fragmentPosition > -1) { uriBuilder = new URIBuilder(new java.net.URI("urn://" + fragment)); } else { uriBuilder = new URIBuilder(new java.net.URI(subjectData.replaceFirst(":", "://"))); } } catch (final URISyntaxException e) { throw new SmartUriException("Unable to serialize a Smart URI from the provided properties", e); } final List<NameValuePair> nameValuePairs = new ArrayList<>(); for (final Entry<URI, Value> entry : map.entrySet()) { final URI key = entry.getKey(); final Value value = entry.getValue(); nameValuePairs.add(new BasicNameValuePair(key.getLocalName(), value.stringValue())); } uriBuilder.setParameters(nameValuePairs); URI uri = null; try { if (fragmentPosition > -1) { final java.net.URI partialUri = uriBuilder.build(); final String uriString = StringUtils.removeStart(partialUri.getRawSchemeSpecificPart(), "//"); final URIBuilder fragmentUriBuilder = new URIBuilder(new java.net.URI(prefix)); fragmentUriBuilder.setFragment(uriString); final String fragmentUriString = fragmentUriBuilder.build().toString(); uri = new URIImpl(fragmentUriString); } else { final String uriString = uriBuilder.build().toString(); uri = new URIImpl(uriString); } } catch (final URISyntaxException e) { throw new SmartUriException("Smart URI could not serialize the property map.", e); } return uri; } /** * Deserializes a URI into a map of URI's to values. * @param uri the {@link URI}. * @return the {@link Map} of {@link URI}s to {@link Value}s. * @throws SmartUriException */ public static Map<URI, Value> deserializeUri(final URI uri) throws SmartUriException { final String uriString = uri.stringValue(); final int fragmentPosition = uriString.indexOf("#"); String prefix = uriString.substring(0, fragmentPosition + 1); if (fragmentPosition == -1) { prefix = uriString.split("\\?", 2)[0]; } final String fragment = uriString.substring(fragmentPosition + 1, uriString.length()); java.net.URI queryUri; URIBuilder uriBuilder = null; try { if (fragmentPosition > -1) { queryUri = new java.net.URI("urn://" + fragment); } else { queryUri = new java.net.URI(uriString); } uriBuilder = new URIBuilder(queryUri); } catch (final URISyntaxException e) { throw new SmartUriException("Unable to deserialize Smart URI", e); } final Map<URI, Value> map = new HashMap<>(); final RyaURI subject = findSubject(uri.stringValue()); final List<NameValuePair> parameters = uriBuilder.getQueryParams(); Map<RyaURI, String> entityTypeMap = new LinkedHashMap<>(); Map<String, RyaURI> invertedEntityTypeMap = new LinkedHashMap<>(); final Map<RyaURI, Map<URI, Value>> fullMap = new LinkedHashMap<>(); for (final NameValuePair pair : parameters) { final String keyString = pair.getName(); final String valueString = pair.getValue(); final URI keyUri = new URIImpl(prefix + keyString); final String decoded; try { decoded = URLDecoder.decode(valueString, Charsets.UTF_8.name()); } catch (final UnsupportedEncodingException e) { throw new SmartUriException("", e); } final URI type = TypeDeterminer.determineType(decoded); if (type == XMLSchema.ANYURI) { if (keyString.equals(RYA_TYPES_URI.getLocalName())) { entityTypeMap = convertUriToTypeMap(new URIImpl(decoded)); invertedEntityTypeMap = HashBiMap.create(entityTypeMap).inverse(); } } else { final int keyPrefixLocation = keyString.indexOf("."); final String keyPrefix = keyString.substring(0, keyPrefixLocation); final RyaURI keyCorrespondingType = invertedEntityTypeMap.get(keyPrefix); final String keyName = keyString.substring(keyPrefixLocation + 1, keyString.length()); final RyaType ryaType = new RyaType(type, valueString); final Value value = RyaToRdfConversions.convertValue(ryaType); final String formattedKeyUriString = removeTypePrefixFromUri(keyUri.stringValue(), keyPrefix); final URI formattedKeyUri = new URIImpl(formattedKeyUriString); map.put(formattedKeyUri, value); } } return map; } public static Entity deserializeUriEntity(final URI uri) throws SmartUriException { final String uriString = uri.stringValue(); final int fragmentPosition = uriString.indexOf("#"); String prefix = uriString.substring(0, fragmentPosition + 1); if (fragmentPosition == -1) { prefix = uriString.split("\\?", 2)[0]; } final String fragment = uriString.substring(fragmentPosition + 1, uriString.length()); java.net.URI queryUri; URIBuilder uriBuilder = null; try { if (fragmentPosition > -1) { queryUri = new java.net.URI("urn://" + fragment); } else { queryUri = new java.net.URI(uriString); } uriBuilder = new URIBuilder(queryUri); } catch (final URISyntaxException e) { throw new SmartUriException("Unable to deserialize Smart URI", e); } final RyaURI subject = findSubject(uri.stringValue()); final List<NameValuePair> parameters = uriBuilder.getQueryParams(); Map<RyaURI, String> entityTypeMap = new LinkedHashMap<>(); Map<String, RyaURI> invertedEntityTypeMap = new LinkedHashMap<>(); final Map<RyaURI, Map<URI, Value>> fullMap = new LinkedHashMap<>(); for (final NameValuePair pair : parameters) { final String keyString = pair.getName(); final String valueString = pair.getValue(); final URI keyUri = new URIImpl(prefix + keyString); final String decoded; try { decoded = URLDecoder.decode(valueString, Charsets.UTF_8.name()); } catch (final UnsupportedEncodingException e) { throw new SmartUriException("", e); } final URI type = TypeDeterminer.determineType(decoded); if (type == XMLSchema.ANYURI) { if (keyString.equals(RYA_TYPES_URI.getLocalName())) { entityTypeMap = convertUriToTypeMap(new URIImpl(decoded)); invertedEntityTypeMap = HashBiMap.create(entityTypeMap).inverse(); } } else { final int keyPrefixLocation = keyString.indexOf("."); final String keyPrefix = keyString.substring(0, keyPrefixLocation); final RyaURI keyCorrespondingType = invertedEntityTypeMap.get(keyPrefix); final String keyName = keyString.substring(keyPrefixLocation + 1, keyString.length()); final RyaType ryaType = new RyaType(type, valueString); final Value value = RyaToRdfConversions.convertValue(ryaType); final String formattedKeyUriString = removeTypePrefixFromUri(keyUri.stringValue(), keyPrefix); final URI formattedKeyUri = new URIImpl(formattedKeyUriString); final Map<URI, Value> map = fullMap.get(keyCorrespondingType); if (map == null) { final Map<URI, Value> subMap = new HashMap<>(); subMap.put(formattedKeyUri, value); fullMap.put(keyCorrespondingType, subMap); } else { map.put(formattedKeyUri, value); fullMap.put(keyCorrespondingType, map); } } } final Entity entity = convertMapToEntity(subject, fullMap); return entity; } private static final class TypeDeterminer { /** * Private constructor to prevent instantiation. */ private TypeDeterminer() { } private static URI determineType(final String data) { if (Ints.tryParse(data) != null) { return XMLSchema.INTEGER; } else if (Doubles.tryParse(data) != null) { return XMLSchema.DOUBLE; } else if (Floats.tryParse(data) != null) { return XMLSchema.FLOAT; } else if (isShort(data)) { return XMLSchema.SHORT; } else if (Longs.tryParse(data) != null) { return XMLSchema.LONG; } if (Boolean.parseBoolean(data)) { return XMLSchema.BOOLEAN; } else if (isByte(data)) { return XMLSchema.BYTE; } else if (isDate(data)) { return XMLSchema.DATETIME; } else if (isUri(data)) { return XMLSchema.ANYURI; } return XMLSchema.STRING; } private static boolean isDate(final String data) { try { DateTime.parse(data, ISODateTimeFormat.dateTimeParser()); return true; } catch (final IllegalArgumentException e) { // not a date return false; } } private static boolean isShort(final String data) { try { Short.parseShort(data); return true; } catch (final NumberFormatException e) { // not a short return false; } } private static boolean isByte(final String data) { try { Byte.parseByte(data); return true; } catch (final NumberFormatException e) { // not a byte return false; } } private static boolean isUri(final String data) { try { final String decoded = URLDecoder.decode(data, Charsets.UTF_8.name()); new URIImpl(decoded); return true; } catch (final IllegalArgumentException | UnsupportedEncodingException e) { // not a URI return false; } } } public static Map<URI, Value> entityToValueMap(final Entity entity) { final Map<URI, Value> map = new LinkedHashMap<>(); for (final Entry<RyaURI, ImmutableMap<RyaURI, Property>> entry : entity.getProperties().entrySet()) { for (final Entry<RyaURI, Property> property : entry.getValue().entrySet()) { final RyaURI propertyKey = property.getKey(); final URI uri = new URIImpl(propertyKey.getData()); final Property propertyValue = property.getValue(); final Value value = RyaToRdfConversions.convertValue(propertyValue.getValue()); map.put(uri, value); } } return map; } /** * Converts a {@link Map} of {@link URI}/{@link Value}s to a {@link Set} of * {@link Property}s. * @param map the {@link Map} of {@link URI}/{@link Value}. * @return the {@link Set} of {@link Property}s. */ public static Set<Property> mapToProperties(final Map<URI, Value> map) { final Set<Property> properties = new LinkedHashSet<>(); for (final Entry<URI, Value> entry : map.entrySet()) { final URI uri = entry.getKey(); final Value value = entry.getValue(); final RyaURI ryaUri = new RyaURI(uri.stringValue()); final RyaType ryaType = RdfToRyaConversions.convertValue(value); final Property property = new Property(ryaUri, ryaType); properties.add(property); } return properties; } public static Map<URI, Value> propertiesToMap(final Set<Property> properties) { final Map<URI, Value> map = new LinkedHashMap<>(); for (final Property property : properties) { final URI uri = new URIImpl(property.getName().getData()); final Value value = RyaToRdfConversions.convertValue(property.getValue()); map.put(uri, value); } return map; } }