/* * JBoss, Home of Professional Open Source. * * See the LEGAL.txt file distributed with this work for information regarding copyright ownership and licensing. * * See the AUTHORS.txt file distributed with this work for a full listing of individual contributors. */ package org.teiid.designer.core.util; import java.util.Arrays; import org.eclipse.emf.common.util.URI; import org.teiid.core.designer.util.CoreArgCheck; /** * BasicUriHelper * * @since 8.0 */ public class BasicUriPathConverter implements UriPathConverter { private static final String SEGMENT_EMPTY = ""; //$NON-NLS-1$ private static final String SEGMENT_SELF = "."; //$NON-NLS-1$ private static final String SEGMENT_PARENT = ".."; //$NON-NLS-1$ private static final char SEGMENT_SEPARATOR = '/'; // ================================================================================== // C O N S T R U C T O R S // ================================================================================== /** * Construct an instance of BasicUriHelper. * */ public BasicUriPathConverter() { super(); } // ================================================================================== // P U B L I C M E T H O D S // ================================================================================== /** * @see UriHelper#makeAbsolutePath(String, String) */ @Override public String makeAbsolute(final String relativePath, final String basePath) { CoreArgCheck.isNotNull(relativePath); final URI relativeURI = URI.createURI(relativePath); // If relativePath is a workspace relative path of the form "/Project/.../File" // then return this path as the absolute path if (relativeURI.hasAbsolutePath()) { return relativePath; } CoreArgCheck.isNotNull(basePath); final URI baseURI = URI.createURI(basePath); // Use the URI class to make the relative path absolute return makeAbsolute(relativeURI,baseURI).toString(); } /** * @see UriHelper#makeAbsolutePath(URI, URI) */ @Override public URI makeAbsolute(final URI relativeURI, final URI baseURI) { CoreArgCheck.isNotNull(relativeURI); // If relativePath is a workspace relative path of the form "/Project/.../File" // then return this path as the absolute path if (relativeURI.hasAbsolutePath()) { return relativeURI; } // Use the URI class to make the relative path absolute CoreArgCheck.isNotNull(baseURI); if (baseURI.isRelative()) { String[] segments = mergePath(relativeURI,baseURI,true); StringBuffer sb = new StringBuffer(200); for (int i = 0; i != segments.length; ++i) { sb.append(SEGMENT_SEPARATOR); sb.append(segments[i]); } return URI.createURI(sb.toString()); } return relativeURI.resolve(baseURI); } /** * @see UriHelper#makeRelativePath(String, String) */ @Override public String makeRelative(final String absolutePath, final String basePath) { CoreArgCheck.isNotNull(absolutePath); final URI absoluteURI = URI.createURI(absolutePath); final URI baseURI = URI.createURI(basePath); // Use the URI class to make the absolute path relative return makeRelative(absoluteURI,baseURI).toString(); } /** * @see UriHelper#makeRelativeUri(URI, URI) */ @Override public URI makeRelative(final URI absoluteURI, final URI baseURI) { CoreArgCheck.isNotNull(absoluteURI); CoreArgCheck.isNotNull(baseURI); // Use the URI class to make the relative path absolute CoreArgCheck.isNotNull(baseURI); if (baseURI.isRelative()) { String[] segments = findRelativePath(absoluteURI,baseURI,true); StringBuffer sb = new StringBuffer(200); // Prepend a SEGMENT SEPARATOR to the resultant path String seg = segments[0]; if (segments.length > 1 && !SEGMENT_SELF.equals(seg) && !SEGMENT_PARENT.equals(seg) && !SEGMENT_EMPTY.equals(seg)) { sb.append(SEGMENT_SEPARATOR); } for (int i = 0; i != segments.length; ++i) { seg = segments[i]; if (i > 0) { sb.append(SEGMENT_SEPARATOR); } sb.append(seg); } return URI.createURI(sb.toString()); } // Use the URI class to make the absolute path relative return absoluteURI.deresolve(baseURI); } // ================================================================================== // P R I V A T E M E T H O D S // ================================================================================== // private void printUriInfo(final URI uri) { // System.out.println("URI = "+uri); //$NON-NLS-1$ // System.out.println(" isFile() "+uri.isFile()); //$NON-NLS-1$ // System.out.println(" isHierarchical() "+uri.isHierarchical()); //$NON-NLS-1$ // System.out.println(" isPrefix() "+uri.isPrefix()); //$NON-NLS-1$ // System.out.println(" isRelative() "+uri.isRelative()); //$NON-NLS-1$ // System.out.println(" hasAbsolutePath() "+uri.hasAbsolutePath()); //$NON-NLS-1$ // System.out.println(" hasAuthority() "+uri.hasAuthority()); //$NON-NLS-1$ // System.out.println(" hasDevice() "+uri.hasDevice()); //$NON-NLS-1$ // System.out.println(" hasPath() "+uri.hasPath()); //$NON-NLS-1$ // System.out.println(" hasQuery() "+uri.hasQuery()); //$NON-NLS-1$ // System.out.println(" hasRelativePath() "+uri.hasRelativePath()); //$NON-NLS-1$ // } // Merges a relative URI's path with the base non-relative path. If // base has no path, treat it as the root absolute path, unless this has // no path either. private String[] mergePath(final URI relativeURI, final URI baseURI, boolean preserveRootParents) { int segmentCount = relativeURI.segmentCount(); String[] segments = relativeURI.segments(); int baseSegmentCount = baseURI.segmentCount(); String[] stack = new String[baseSegmentCount + segmentCount]; int sp = 0; // use a stack to accumulate segments of base, except for the last // (i.e. skip trailing separator and anything following it), and of // relative path for (int i = 0; i < baseSegmentCount - 1; i++) { sp = accumulate(stack, sp, baseURI.segment(i), preserveRootParents); } for (int i = 0; i < segmentCount; i++) { sp = accumulate(stack, sp, segments[i], preserveRootParents); } // if the relative path is empty or ends in an empty segment, a parent // reference, or a self referenfce, add a trailing separator to a // non-empty path if (sp > 0 && (segmentCount == 0 || SEGMENT_EMPTY.equals(segments[segmentCount - 1]) || SEGMENT_PARENT.equals(segments[segmentCount - 1]) || SEGMENT_SELF.equals(segments[segmentCount - 1]))) { stack[sp++] = SEGMENT_EMPTY; } // return a correctly sized result String[] result = new String[sp]; System.arraycopy(stack, 0, result, 0, sp); return result; } // Adds a segment to a stack, skipping empty segments and self references, // and interpreting parent references. private static int accumulate(String[] stack, int sp, String segment, boolean preserveRootParents) { if (SEGMENT_PARENT.equals(segment)) { if (sp == 0) { // special care must be taken for a root's parent reference: it is // either ignored or the symbolic reference itself is pushed if (preserveRootParents) stack[sp++] = segment; } else { // unless we're already accumulating root parent references, // parent references simply pop the last segment descended if (SEGMENT_PARENT.equals(stack[sp - 1])) stack[sp++] = segment; else sp--; } } else if (!SEGMENT_EMPTY.equals(segment) && !SEGMENT_SELF.equals(segment)) { // skip empty segments and self references; push everything else stack[sp++] = segment; } return sp; } // Returns the shortest relative path between the the non-relative path of // the given base and this absolute path. If the base has no path, it is // treated as the root absolute path. private String[] findRelativePath(final URI absoluteURI, final URI baseURI, boolean preserveRootParents) { String[] segments = absoluteURI.segments(); // treat an empty base path as the root absolute path String[] startPath = collapseSegments(baseURI,preserveRootParents); String[] endPath = segments; // drop last segment from base, as in resolving int startCount = startPath.length > 0 ? startPath.length - 1 : 0; int endCount = endPath.length; // index of first segment that is different between endPath and startPath int diff = 0; // if endPath is shorter than startPath, the last segment of endPath may // not be compared: because startPath has been collapsed and had its // last segment removed, all preceeding segments can be considered non- // empty and followed by a separator, while the last segment of endPath // will either be non-empty and not followed by a separator, or just empty for (int count = startCount < endCount ? startCount : endCount - 1; diff < count && startPath[diff].equals(endPath[diff]); diff++) { } int upCount = startCount - diff; int downCount = endCount - diff; // a single separator, possibly preceeded by some parent reference // segments, is redundant if (downCount == 1 && SEGMENT_EMPTY.equals(endPath[endCount - 1])) { downCount = 0; } // an empty path needs to be replaced by a single "." if there is no // query, to distinguish it from a current document reference if (upCount + downCount == 0) { return new String[] { SEGMENT_SELF }; } // return a correctly sized result String[] result = new String[upCount + downCount]; Arrays.fill(result, 0, upCount, SEGMENT_PARENT); System.arraycopy(endPath, diff, result, upCount, downCount); return result; } // Collapses non-ending empty segments, parent references, and self // references in a non-relative path, returning the same path that would // be produced from the base hierarchical URI as part of a resolve. String[] collapseSegments(final URI uri, boolean preserveRootParents) { String[] segments = uri.segments(); if (!hasCollapsableSegments(uri,preserveRootParents)) return segments; // use a stack to accumulate segments int segmentCount = segments.length; String[] stack = new String[segmentCount]; int sp = 0; for (int i = 0; i < segmentCount; i++) { sp = accumulate(stack, sp, segments[i], preserveRootParents); } // if the path is non-empty and originally ended in an empty segment, a // parent reference, or a self reference, add a trailing separator if (sp > 0 && (SEGMENT_EMPTY.equals(segments[segmentCount - 1]) || SEGMENT_PARENT.equals(segments[segmentCount - 1]) || SEGMENT_SELF.equals(segments[segmentCount - 1]))) { stack[sp++] = SEGMENT_EMPTY; } // return a correctly sized result String[] result = new String[sp]; System.arraycopy(stack, 0, result, 0, sp); return result; } // Returns true if the non-relative path includes segments that would be // collapsed when resolving; false otherwise. If preserveRootParents is // true, collapsable segments include any empty segments, except for the // last segment, as well as and parent and self references. If // preserveRootsParents is false, parent references are not collapsable if // they are the first segment or preceeded only by other parent // references. private boolean hasCollapsableSegments(final URI uri, boolean preserveRootParents) { String[] segments = uri.segments(); for (int i = 0, len = segments.length; i < len; i++) { String segment = segments[i]; if ((i < len - 1 && SEGMENT_EMPTY.equals(segment)) || SEGMENT_SELF.equals(segment) || SEGMENT_PARENT.equals(segment) && (!preserveRootParents || (i != 0 && !SEGMENT_PARENT.equals(segments[i - 1])))) { return true; } } return false; } }