/******************************************************************************* * Copyright (c) 2007, 2010 Association for Decentralized Information Management * in Industry THTH ry. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * VTT Technical Research Centre of Finland - initial API and implementation *******************************************************************************/ package org.simantics.db.common.utils; import java.nio.CharBuffer; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Comparator; import java.util.Formatter; import java.util.HashSet; import java.util.List; import java.util.Locale; import java.util.Set; import java.util.TreeSet; import org.simantics.databoard.Bindings; import org.simantics.databoard.binding.mutable.Variant; import org.simantics.databoard.util.binary.RandomAccessBinary; import org.simantics.db.ReadGraph; import org.simantics.db.Resource; import org.simantics.db.Statement; import org.simantics.db.exception.BindingException; import org.simantics.db.exception.DatabaseException; import org.simantics.db.exception.ServiceException; import org.simantics.db.exception.ValidationException; import org.simantics.layer0.Layer0; import org.simantics.utils.datastructures.MapList; public final class NameUtils { public static Set findReservedNames(ReadGraph g, String proposition, Resource container, Resource consistRelation, Resource propertyToList, Set result, MapList reservation) throws DatabaseException { if (proposition == null) throw new NullPointerException("null proposition"); if (propertyToList == null) throw new NullPointerException("null property to list"); if (result == null) result = new HashSet(); if (reservation != null) { String possibleURI = g.getPossibleURI(container); if(possibleURI != null) { result.addAll(reservation.getValuesUnsafe(possibleURI)); } } for (Resource r : g.getObjects(container, consistRelation)) { String name = g.getPossibleRelatedValue(r, propertyToList); if (name != null && name.startsWith(proposition)) result.add(Versions.getBaseName(name)); } return result; } public static Set findReservedNames(ReadGraph g, String proposition, Resource container, Resource consistRelation, Resource propertyToList, Set result) throws DatabaseException { return findReservedNames(g, proposition, container, consistRelation, propertyToList, result, null); } public static Set findReservedNames(ReadGraph g, String proposition, Resource container, Resource consistRelation, Set result) throws DatabaseException { Layer0 L0 = Layer0.getInstance(g); return findReservedNames(g, proposition, container, consistRelation, L0.HasName, result); } public static Set findReservedNames(ReadGraph g, String proposition, Resource container, Resource consistRelation) throws DatabaseException { Set result = new HashSet(); findReservedNames(g, proposition, container, consistRelation, result); return result; } public static String findFreshName(ReadGraph g, String proposition, Resource container) throws DatabaseException { Layer0 b = Layer0.getInstance(g); return findFreshName(g, proposition, container, b.ConsistsOf); } public static String findFreshLabel(ReadGraph g, String proposition, Resource container) throws DatabaseException { Layer0 L0 = Layer0.getInstance(g); return _findFreshName(g, proposition, container, L0.ConsistsOf, L0.HasLabel, " "); } public static String findFreshEscapedName(ReadGraph g, String proposition, Resource container) throws DatabaseException { Layer0 b = Layer0.getInstance(g); return findFreshEscapedName(g, proposition, container, b.ConsistsOf); } public static final Comparator STRING_CHARBUFFER_COMPARATOR = new Comparator(){ @Override public int compare(Object o1, Object o2) { String s1 = null; String s2 = null; if (o1 instanceof String) s1 = (String) o1; if (o2 instanceof String) s2 = (String) o2; if (s1 != null && s2 != null) return s1.compareTo((String) o2); if (s1 == null && s2 == null) return 0; if (s1 == null && (o1 instanceof CharBuffer)) return -compare(s2, (CharBuffer) o1); if (s2 == null && (o2 instanceof CharBuffer)) return compare(s1, (CharBuffer) o2); return 0; } int compare(String s, CharBuffer buf) { int len1 = s.length(); int len2 = buf.position(); int n = Math.min(len1, len2); int k = 0; while (k < n) { char c1 = s.charAt(k); char c2 = buf.get(k); if (c1 != c2) { return c1 - c2; } k++; } return len1 - len2; } }; public static String findFreshName(ReadGraph g, String proposition, Resource container, Resource consistRelation) throws DatabaseException { return _findFreshName(g, proposition, container, consistRelation, " "); } public static String findFreshName(ReadGraph g, String proposition, Resource container, Resource consistRelation, String nameFormat) throws DatabaseException { return findFreshName(g, proposition, container, consistRelation, Layer0.getInstance(g).HasName, nameFormat); } public static String findFreshName(ReadGraph g, String proposition, Resource container, Resource consistRelation, Resource nameProperty, String nameFormat) throws DatabaseException { Set reservedNames = new TreeSet(STRING_CHARBUFFER_COMPARATOR); findReservedNames(g, proposition, container, consistRelation, nameProperty, reservedNames); return findFreshNameFormatted(proposition, reservedNames, nameFormat); } public static String findFreshEscapedName(ReadGraph g, String proposition, Resource container, Resource consistRelation) throws DatabaseException { return _findFreshName(g, proposition, container, consistRelation, "_"); } /** * Find a fresh name using the specified name proposition among an * externally specified set of names using suffix numbers. * * @param g * @param proposition * @param reservedNames * @param numberSeparator * @return * @throws DatabaseException */ public static String findFreshName(ReadGraph g, String proposition, Set reservedNames, String numberSeparator) throws DatabaseException { // Trying to optimize away unnecessary allocation of new String instances // when looking for fresh names for objects. if (!reservedNames.contains(proposition)) return proposition; // Need to make sure that the set is using the correct comparator. Set used = new TreeSet(STRING_CHARBUFFER_COMPARATOR); used.addAll(reservedNames); return findFreshNameNumbered(proposition, used, numberSeparator); } public static String findFreshInstanceName(ReadGraph g, Resource type, Resource container) throws DatabaseException { String typeName = g.getPossibleRelatedValue(type, Layer0.getInstance(g).HasName); if (typeName == null) typeName = "Entity"; return findFreshName(g, typeName, container); } public static String findFreshInstanceName(ReadGraph g, Resource type, Resource container, Resource relation) throws DatabaseException { String typeName = g.getPossibleRelatedValue(type, Layer0.getInstance(g).HasName); if (typeName == null) typeName = "Entity"; return findFreshName(g, typeName, container, relation); } private static String _findFreshName(ReadGraph g, String proposition, Resource container, Resource consistRelation, String numberSeparator) throws DatabaseException { return _findFreshName(g, proposition, container, consistRelation, Layer0.getInstance(g).HasName, numberSeparator); } private static String _findFreshName(ReadGraph g, String proposition, Resource container, Resource consistRelation, Resource nameProperty, String numberSeparator) throws DatabaseException { return findFreshName(g, proposition, container, consistRelation, nameProperty, numberSeparator, null); } public static String findFreshName(ReadGraph g, String proposition, Resource container, Resource consistRelation, Resource nameProperty, String numberSeparator, MapList reservation) throws DatabaseException { Set reservedNames = new TreeSet(STRING_CHARBUFFER_COMPARATOR); findReservedNames(g, proposition, container, consistRelation, nameProperty, reservedNames, reservation); return findFreshNameNumbered(proposition, reservedNames, numberSeparator); } public static String findFreshNameNumbered(String proposition, Set reservedNames, String numberSeparator) { // Trying to optimize away unnecessary allocation of new String instances // when looking for fresh names for objects. if (!reservedNames.contains(proposition)) return proposition; CharBuffer cb = CharBuffer.allocate(proposition.length() + 10); cb.append(proposition); cb.append(numberSeparator); cb.mark(); int i = reservedNames.size() + 1; while (true) { cb.reset(); cb.append(String.valueOf(i)); if (!reservedNames.contains(cb)) { // Construct a String from the CharBuffer cb.limit(cb.position()); cb.rewind(); return cb.toString(); } ++i; } } public static String findFreshNameFormatted(String proposition, Set reservedNames, String nameFormat) { if (!reservedNames.contains(proposition)) return proposition; // Trying to optimize away unnecessary allocation of new String instances // when looking for fresh names for objects. CharBuffer cb = CharBuffer.allocate(proposition.length() + 10); cb.mark(); @SuppressWarnings("resource") Formatter formatter = new Formatter(cb, Locale.US); int i = reservedNames.size() + 1; for (;; ++i) { cb.reset(); formatter.format(nameFormat, proposition, i); if (!reservedNames.contains(cb)) { // Construct a String from the CharBuffer cb.limit(cb.position()); cb.rewind(); String result = cb.toString(); return result; } } } public static String getSafeName(ReadGraph graph, Resource resource, boolean id) throws ValidationException, ServiceException { if(id && resource != null) { return getSafeName(graph, resource) + ":$" + resource.getResourceId(); } else { return getSafeName(graph, resource); } } public static String getSafeName(ReadGraph graph, Collection rs) throws ValidationException, ServiceException { StringBuilder b = new StringBuilder(); b.append("{"); for(Resource r : rs) { b.append(getSafeName(graph, r)); b.append(","); } b.append("}"); return b.toString(); } public static String getSafeLabel(ReadGraph graph, Resource resource) throws ValidationException, ServiceException { return getSafeNameInternal(graph, resource, true); } public static String getSafeName(ReadGraph graph, Resource resource) throws ValidationException, ServiceException { return getSafeNameInternal(graph, resource, false); } private static Object truncate(Object array) { if(array instanceof byte[]) { byte[] bytes = (byte[])array; if(bytes.length > 100) return Arrays.copyOfRange(bytes, 0, 100); } return array; } public static String getURIOrSafeNameInternal(ReadGraph graph, Resource resource) throws ValidationException, ServiceException { return getURIOrSafeNameInternal(graph, resource, false); } public static String getURIOrSafeNameInternal(ReadGraph graph, Resource resource, boolean tryLabel) throws ValidationException, ServiceException { if (resource == null) return "null"; String uri = graph.getPossibleURI(resource); if(uri != null) return uri; else return getSafeNameInternal(graph, resource, tryLabel); } public static long getPossibleValueSize(ReadGraph graph, Resource resource) { try { if(graph.hasValue(resource)) { RandomAccessBinary rab = graph.getRandomAccessBinary(resource); return rab.length(); } }catch (Exception e) { } return 0; } private static String getSafeNameInternal(ReadGraph graph, Resource resource, boolean tryLabel) throws ValidationException, ServiceException { if(resource == null) return "null"; Layer0 b = Layer0.getInstance(graph); if (graph.isInstanceOf(resource, b.Variant)) { try { Variant v = graph.getPossibleValue(resource, Bindings.VARIANT); return v.toString(); } catch (BindingException e) { } } List names = new ArrayList(1); if (tryLabel) { for(Resource nameResource : graph.getObjects(resource, b.HasLabel)) { if(graph.isInstanceOf(nameResource, b.Literal)) { Object value = graph.getPossibleValue(nameResource); if(value != null) { String s = Literals.literalToString(value); if (!s.isEmpty()) names.add(s); } } } } if (names.isEmpty()) { for(Resource nameResource : graph.getObjects(resource, b.HasName)) { Object value = graph.getPossibleValue(nameResource); if(value != null) { names.add(Literals.literalToString(value)); } } } if(!names.isEmpty()) { if(names.size() == 1) return names.get(0); else { StringBuilder bb = new StringBuilder(); bb.append('['); for(int i=0;i0) bb.append(", "); bb.append(names.get(i)); } bb.append(']'); return bb.toString(); } } StringBuilder bb = new StringBuilder(); long valueSize = getPossibleValueSize(graph, resource); //System.err.println("valueSize=" + valueSize); if(valueSize > 0) { if(valueSize < 1e6) { Object val = graph.getPossibleValue(resource); if(val != null) { val = truncate(val); if(val instanceof double[]) bb.append(Arrays.toString((double[])val)); else if(val instanceof float[]) bb.append(Arrays.toString((float[])val)); else if(val instanceof int[]) bb.append(Arrays.toString((int[])val)); else if(val instanceof boolean[]) bb.append(Arrays.toString((boolean[])val)); else if(val instanceof long[]) bb.append(Arrays.toString((long[])val)); else if(val instanceof byte[]) bb.append(Arrays.toString((byte[])val)); else if(val instanceof String[]) bb.append(Arrays.toString((String[])val)); else bb.append(val); } else { bb.append('$').append(resource.getResourceId()); } } else { bb.append('$').append(resource.getResourceId()); bb.append("[" + valueSize + " bytes of value]"); } } else { bb.append('$').append(resource.getResourceId()); } boolean ok = false; for(Resource r : graph.getObjects(resource, b.InstanceOf)) { if(!r.equals(resource)) bb.append(" : (" + getSafeName(graph, r) + ")"); ok = true; } if(!ok) { for(Resource r : graph.getObjects(resource, b.Inherits)) { bb.append(" c) throws DatabaseException { return toString(g, c, false); } /** * Convert a collection of resources into a String in the same manner as * {@link Arrays#toString(Object[])} does but by consulting * {@link #getSafeName(ReadGraph, Resource)} for each resource. * * @param g database accessor * @param c the collection of resources to translate to named strings * @param ids true to print the ID of each resource after its * name separated by ':' * @return string representation of the resource collection * @throws DatabaseException */ public static String toString(ReadGraph g, Collection c, boolean ids) throws DatabaseException { StringBuilder sb = new StringBuilder(); sb.append('['); boolean first = true; for (Resource r : c) { if (!first) sb.append(", "); first = false; sb.append(getSafeName(g, r, ids)); } return sb.append(']').toString(); } /** * @param graph * @param r * @return * @throws DatabaseException */ public static String resourcePath(ReadGraph graph, Resource r) throws DatabaseException { return resourcePath0(graph, r, new StringBuilder(80)).toString(); } /** * @param graph * @param r * @return * @throws DatabaseException */ private static StringBuilder resourcePath0(ReadGraph graph, Resource r, StringBuilder result) throws DatabaseException { String uri = graph.getPossibleURI(r); if (uri != null) return result.append(uri); Resource owner = null; try { owner = CommonDBUtils.getPossibleOwner(graph, r); if (owner == null) return result.append(NameUtils.getSafeName(graph, r)); } catch (DatabaseException e) { // Owner resource not found by CommonDBUtils.getPossibleOwner. return result.append(NameUtils.getSafeName(graph, r)); } // See if there's a statement between r and owner. Layer0 L0 = Layer0.getInstance(graph); for (Statement stm : graph.getStatements(owner, L0.IsWeaklyRelatedTo)) { if (stm.getObject().equals(r)) { Resource predicate = stm.getPredicate(); if (predicate.equals(L0.ConsistsOf)) { return resourcePath0(graph, owner, result) .append(" / ") .append(NameUtils.getSafeName(graph, r)); } else if (graph.isSubrelationOf(predicate, L0.HasProperty)) { return resourcePath0(graph, owner, result) .append(" #(") .append(NameUtils.getSafeName(graph, predicate, true)) .append(") ") .append(NameUtils.getSafeName(graph, r)); } else { return resourcePath0(graph, owner, result) .append(" -(") .append(NameUtils.getSafeName(graph, predicate, true)) .append(")-> ") .append(NameUtils.getSafeName(graph, r)); } } } return resourcePath0(graph, owner, result) .append(" ... ") .append(NameUtils.getSafeName(graph, r)); } }