All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.docx4j.openpackaging.URIHelper Maven / Gradle / Ivy

Go to download

docx4j is a library which helps you to work with the Office Open XML file format as used in docx documents, pptx presentations, and xlsx spreadsheets.

There is a newer version: 6.1.2
Show newest version
/*
 * Copyright (c) 2006, Wygwam
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without modification, 
 * are permitted provided that the following conditions are met: 
 * 
 * - Redistributions of source code must retain the above copyright notice, 
 * this list of conditions and the following disclaimer.
 * - Redistributions in binary form must reproduce the above copyright notice, 
 * this list of conditions and the following disclaimer in the documentation and/or 
 * other materials provided with the distribution.
 * - Neither the name of Wygwam nor the names of its contributors may be 
 * used to endorse or promote products derived from this software without 
 * specific prior written permission. 
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY 
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 
 * IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

package org.docx4j.openpackaging;

import java.net.URI;
import java.net.URISyntaxException;

import org.docx4j.openpackaging.exceptions.InvalidFormatException;
import org.docx4j.openpackaging.parts.PartName;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;



/**
 * Helper for part and pack URI.
 * 
 * @author Julien Chable, CDubet
 * @version 0.3
 */
public final class URIHelper {

	private static Logger log = LoggerFactory.getLogger(URIHelper.class);	
	
	/**
	 * Package root URI.
	 */
	private static URI packageRootUri;

	/**
	 * Extension name of a relationship part.
	 */
	public static final String RELATIONSHIP_PART_EXTENSION_NAME;

	/**
	 * Segment name of a relationship part.
	 */
	public static final String RELATIONSHIP_PART_SEGMENT_NAME;

	/**
	 * Segment name of the package properties folder.
	 */
	public static final String PACKAGE_PROPERTIES_SEGMENT_NAME;

	/**
	 * Core package properties art name.
	 */
	public static final String PACKAGE_CORE_PROPERTIES_NAME;

	/**
	 * Forward slash URI separator.
	 */
	public static final char FORWARD_SLASH_CHAR;

	/**
	 * Forward slash URI separator.
	 */
	public static final String FORWARD_SLASH_STRING;

	/**
	 * Package relationships part URI
	 */
	public static final URI PACKAGE_RELATIONSHIPS_ROOT_URI;

	/**
	 * Package relationships part name.
	 */
	public static final PartName PACKAGE_RELATIONSHIPS_ROOT_PART_NAME;

	/**
	 * Core properties part URI.
	 */
	public static final URI CORE_PROPERTIES_URI;

	/**
	 * Core properties partname.
	 */
	public static final PartName CORE_PROPERTIES_PART_NAME;

	/**
	 * Root package URI.
	 */
	public static final URI PACKAGE_ROOT_URI;

	/**
	 * Root package part name.
	 */
	public static final PartName PACKAGE_ROOT_PART_NAME;

	/* Static initialization */
	static {
		RELATIONSHIP_PART_SEGMENT_NAME = "_rels";
		RELATIONSHIP_PART_EXTENSION_NAME = ".rels";
		FORWARD_SLASH_CHAR = '/';
		FORWARD_SLASH_STRING = "/";
		PACKAGE_PROPERTIES_SEGMENT_NAME = "docProps";
		PACKAGE_CORE_PROPERTIES_NAME = "core.xml";

		// Make URI
		URI uriPACKAGE_ROOT_URI = null;
		URI uriPACKAGE_RELATIONSHIPS_ROOT_URI = null;
		URI uriPACKAGE_PROPERTIES_URI = null;
		try {
			uriPACKAGE_ROOT_URI = new URI("/");
			uriPACKAGE_RELATIONSHIPS_ROOT_URI = new URI(FORWARD_SLASH_CHAR
					+ RELATIONSHIP_PART_SEGMENT_NAME + FORWARD_SLASH_CHAR
					+ RELATIONSHIP_PART_EXTENSION_NAME);
			packageRootUri = new URI("/");
			uriPACKAGE_PROPERTIES_URI = new URI(FORWARD_SLASH_CHAR
					+ PACKAGE_PROPERTIES_SEGMENT_NAME + FORWARD_SLASH_CHAR
					+ PACKAGE_CORE_PROPERTIES_NAME);
		} catch (URISyntaxException e) {
			// Should never happen in production as all data are fixed
		}
		PACKAGE_ROOT_URI = uriPACKAGE_ROOT_URI;
		PACKAGE_RELATIONSHIPS_ROOT_URI = uriPACKAGE_RELATIONSHIPS_ROOT_URI;
		CORE_PROPERTIES_URI = uriPACKAGE_PROPERTIES_URI;

		// Make part name from previous URI
		PartName tmpPACKAGE_ROOT_PART_NAME = null;
		PartName tmpPACKAGE_RELATIONSHIPS_ROOT_PART_NAME = null;
		PartName tmpCORE_PROPERTIES_URI = null;
		try {
			tmpPACKAGE_RELATIONSHIPS_ROOT_PART_NAME = createPartName(PACKAGE_RELATIONSHIPS_ROOT_URI);
			tmpCORE_PROPERTIES_URI = createPartName(CORE_PROPERTIES_URI);
			tmpPACKAGE_ROOT_PART_NAME = new PartName(PACKAGE_ROOT_URI,
					false);
		} catch (InvalidFormatException e) {
			// Should never happen in production as all data are fixed
		}
		PACKAGE_RELATIONSHIPS_ROOT_PART_NAME = tmpPACKAGE_RELATIONSHIPS_ROOT_PART_NAME;
		CORE_PROPERTIES_PART_NAME = tmpCORE_PROPERTIES_URI;
		PACKAGE_ROOT_PART_NAME = tmpPACKAGE_ROOT_PART_NAME;
	}

	/**
	 * Gets the URI for the package root.
	 * 
	 * @return URI of the package root.
	 */
	public static URI getPackageRootUri() {
		return packageRootUri;
	}


	/**
	 * Know if the specified URI is a relationship part name.
	 * 
	 * @param partUri
	 *            URI to check.
	 * @return true if the URI false.
	 */
	private static boolean isRelationshipPartURI(URI partUri) {
		if (partUri == null)
			throw new NullPointerException("partUri");

		return partUri.getPath().matches(
				".*" + RELATIONSHIP_PART_SEGMENT_NAME + ".*"
						+ RELATIONSHIP_PART_EXTENSION_NAME + "$");
	}

	/**
	 * Get file name from the specified URI.
	 */
	public static String getFilename(URI uri) {
		if (uri != null) {
			String path = uri.getPath();
			int len = path.length();
			int num2 = len;
			while (--num2 >= 0) {
				char ch1 = path.charAt(num2);
				if (ch1 == URIHelper.FORWARD_SLASH_CHAR)
					return path.substring(num2 + 1, len);
			}
		}
		return "";
	}

	/**
	 * Get the file name without the trailing extension.
	 */
	public static String getFilenameWithoutExtension(URI uri) {
		String filename = getFilename(uri);
		int dotIndex = filename.lastIndexOf(".");
		if (dotIndex == -1)
			return filename;
		return filename.substring(0, dotIndex);
	}

	/**
	 * Get the directory path from the specified URI.
	 */
	public static URI getPath(URI uri) {
		if (uri != null) {
			String path = uri.getPath();
			int len = path.length();
			int num2 = len;
			while (--num2 >= 0) {
				char ch1 = path.charAt(num2);
				if (ch1 == URIHelper.FORWARD_SLASH_CHAR) {
					try {
						return new URI(path.substring(0, num2));
					} catch (URISyntaxException e) {
						return null;
					}
				}
			}
		}
		return null;
	}

	/**
	 * Combine two URI.
	 * 
	 * @param prefix
	 * @param suffix
	 * @return
	 */
	public static URI combine(URI prefix, URI suffix) {
		URI retUri = null;
		try {
			retUri = new URI(combine(prefix.getPath(), suffix.getPath()));
		} catch (URISyntaxException e) {
			throw new IllegalArgumentException(
					"Prefix and suffix can't be combined !");
		}
		return retUri;
	}

	/**
	 * Combine a string URI with a prefix and a suffix.
	 */
	public static String combine(String prefix, String suffix) {
		if (!prefix.endsWith("" + FORWARD_SLASH_CHAR)
				&& !suffix.startsWith("" + FORWARD_SLASH_CHAR))
			return prefix + FORWARD_SLASH_CHAR + suffix;
		else if ((!prefix.endsWith("" + FORWARD_SLASH_CHAR)
				&& suffix.startsWith("" + FORWARD_SLASH_CHAR) || (prefix
				.endsWith("" + FORWARD_SLASH_CHAR) && !suffix.startsWith(""
				+ FORWARD_SLASH_CHAR))))
			return prefix + suffix;
		else
			return "";
	}

	/**
	 * Fully relativize the target part URI against the source part URI.
	 * 
	 * @param sourceURI
	 *            The source part URI.
	 * @param targetURI
	 *            The target part URI.
	 * @return A fully relativize part name URI ('word/media/image1.gif',
	 *         '/word/document.xml' => 'media/image1.gif') else
	 *         null.
	 */
	public static URI relativizeURI(URI sourceURI, URI targetURI) {
		StringBuilder retVal = new StringBuilder();
		String[] segmentsSource = sourceURI.getPath().split("/", -1);
		String[] segmentsTarget = targetURI.getPath().split("/", -1);

		// If the source URI is empty
		if (segmentsSource.length == 0) {
			throw new IllegalArgumentException(
					"Can't relativize an empty source URI !");
		}

		// If target URI is empty
		if (segmentsTarget.length == 0) {
			throw new IllegalArgumentException(
					"Can't relativize an empty target URI !");
		}
		
		// If the source is the root, then the relativized
		//  form must actually be an absolute URI
		if(sourceURI.toString().equals("/")) {
			return targetURI;
		}


		// Relativize the source URI against the target URI.
		// First up, figure out how many steps along we can go
		// and still have them be the same
		int segmentsTheSame = 0;
		for (int i = 0; i < segmentsSource.length && i < segmentsTarget.length; i++) {
			if (segmentsSource[i].equals(segmentsTarget[i])) {
				// Match so far, good
				segmentsTheSame++;
			} else {
				break;
			}
		}

		// If we didn't have a good match or at least except a first empty element
		if ((segmentsTheSame == 0 || segmentsTheSame == 1) && 
				segmentsSource[0].equals("") && segmentsTarget[0].equals("")) {
			for (int i = 0; i < segmentsSource.length - 2; i++) {
				retVal.append("../");
			}
			for (int i = 0; i < segmentsTarget.length; i++) {
				if (segmentsTarget[i].equals(""))
					continue;
				retVal.append(segmentsTarget[i]);
				if (i != segmentsTarget.length - 1)
					retVal.append("/");
			}

			try {
				return new URI(retVal.toString());
			} catch (Exception e) {
				System.err.println(e);
				return null;
			}
		}

		// Special case for where the two are the same
		if (segmentsTheSame == segmentsSource.length
				&& segmentsTheSame == segmentsTarget.length) {
			retVal.append("");
		} else {
			// Matched for so long, but no more

			// Do we need to go up a directory or two from
			// the source to get here?
			// (If it's all the way up, then don't bother!)
			if (segmentsTheSame == 1) {
				retVal.append("/");
			} else {
				for (int j = segmentsTheSame; j < segmentsSource.length - 1; j++) {
					retVal.append("../");
				}
			}

			// Now go from here on down
			for (int j = segmentsTheSame; j < segmentsTarget.length; j++) {
				if (retVal.length() > 0
						&& retVal.charAt(retVal.length() - 1) != '/') {
					retVal.append("/");
				}
				retVal.append(segmentsTarget[j]);
			}
		}

		try {
			return new URI(retVal.toString());
		} catch (Exception e) {
			System.err.println(e);
			return null;
		}
	}
	

	/**
	 * Resolve a target uri against a source.
	 * 
	 * @param sourcePartUri
	 *            The source URI.
	 * @param targetUri
	 *            The target URI.
	 * @return The resolved URI.
	 */
	public static URI resolvePartUri(URI sourcePartUri, URI targetUri) {
//		log.info("source: " + sourcePartUri);
//		log.info("target: " + targetUri);
		URI uri;
		if (sourcePartUri == null || sourcePartUri.isAbsolute()) {
			throw new IllegalArgumentException("sourcePartUri");
		}
		if (targetUri == null) {
			log.error("targetUri was null");
			throw new IllegalArgumentException("targetUri");			
		} else if (targetUri.isAbsolute()) {			
			log.error("targetUri " + targetUri.toString() + " is absolute!");
			throw new IllegalArgumentException("targetUri");
		}

		uri = sourcePartUri.resolve(targetUri);
//		log.info("RESULT: " + uri);
		return uri;
	}

	/**
	 * Get URI from a string path.
	 */
	public static URI getURIFromPath(String path) {
		URI retUri = null;
		try {
			retUri = new URI(path);
		} catch (URISyntaxException e) {
			throw new IllegalArgumentException("path");
		}
		return retUri;
	}

	public static URI getSourcePartUriFromRelationshipPartUri(
			URI relationshipPartUri) {
		if (relationshipPartUri == null)
			throw new IllegalArgumentException(
					"The relationshipPart Uri was null !");

		if (!isRelationshipPartURI(relationshipPartUri))
			throw new IllegalArgumentException(
					"L'URI ne doit pas �tre celle d'une partie de type relation.");

		if (relationshipPartUri.compareTo(PACKAGE_RELATIONSHIPS_ROOT_URI) == 0)
			return PACKAGE_ROOT_URI;

		String filename = relationshipPartUri.getPath();
		String filenameWithoutExtension = getFilenameWithoutExtension(relationshipPartUri);
		filename = filename
				.substring(0, ((filename.length() - filenameWithoutExtension
						.length()) - RELATIONSHIP_PART_EXTENSION_NAME.length()));
		filename = filename.substring(0, filename.length()
				- RELATIONSHIP_PART_SEGMENT_NAME.length() - 1);
		filename = combine(filename, filenameWithoutExtension);
		return getURIFromPath(filename);
	}

	/**
	 * Create an OPC compliant part name by throwing an exception if the URI is
	 * not valid.
	 * 
	 * @param partUri
	 *            The part name URI to validate.
	 * @return A valid part name object, else null.
	 * @throws InvalidFormatException
	 *             Throws if the specified URI is not OPC compliant.
	 */
	public static PartName createPartName(URI partUri)
			throws InvalidFormatException {
		if (partUri == null)
			throw new IllegalArgumentException("partName");

		return new PartName(partUri, true);
	}

	/**
	 * Create an OPC compliant part name by throwing an exception if the
	 * specified name is not valid.
	 * 
	 * @param partName
	 *            The part name to validate.
	 * @return The correspondant part name if valid, else null.
	 * @throws InvalidFormatException
	 *             Throws if the specified part name is not OPC compliant.
	 * @see #createPartName(URI)
	 */
	public static PartName createPartName(String partName)
			throws InvalidFormatException {
		URI partNameURI;
		try {
			partNameURI = new URI(partName);
		} catch (URISyntaxException e) {
			throw new InvalidFormatException(e.getMessage());
		}
		return createPartName(partNameURI);
	}

	/**
	 * Validate a part URI by returning a boolean.
	 * ([M1.1],[M1.3],[M1.4],[M1.5],[M1.6])
	 * 
	 * (OPC Specifications 8.1.1 Part names) :
	 * 
	 * Part Name Syntax
	 * 
	 * The part name grammar is defined as follows:
	 * 
	 * part_name = 1*( "/" segment )
	 * 
	 * segment = 1*( pchar )
	 * 
	 * 
	 * (pchar is defined in RFC 3986)
	 * 
	 * @param partUri
	 *            The URI to validate.
	 * @return true if the URI is valid to the OPC Specifications, else
	 *         false
	 * 
	 * @see #createPartName(URI)
	 */
	public static boolean isValidPartName(URI partUri) {
		if (partUri == null)
			throw new IllegalArgumentException("partUri");

		try {
			createPartName(partUri);
			return true;
		} catch (Exception e) {
			return false;
		}
	}

	/**
	 * Decode a URI by converting all percent encoded character into a String
	 * character.
	 * 
	 * @param uri
	 *            The URI to decode.
	 * @return The specified URI in a String with converted percent encoded
	 *         characters.
	 */
	public static String decodeURI(URI uri) {
		StringBuffer retVal = new StringBuffer();
		String uriStr = uri.toASCIIString();
		char c;
		for (int i = 0; i < uriStr.length(); ++i) {
			c = uriStr.charAt(i);
			if (c == '%') {
				// We certainly found an encoded character, check for length
				// now ( '%' HEXDIGIT HEXDIGIT)
				if (((uriStr.length() - i) < 2)) {
					throw new IllegalArgumentException("The uri " + uriStr
							+ " contain invalid encoded character !");
				}

				// Decode the encoded character
				char decodedChar = (char) Integer.parseInt(uriStr.substring(
						i + 1, i + 3), 16);
				retVal.append(decodedChar);
				i += 2;
				continue;
			}
			retVal.append(c);
		}
		return retVal.toString();
	}

//	/**
//	 * Fully relativize the source part URI against the target part URI.
//	 * 
//	 * @param sourceURI
//	 *            The source part URI.
//	 * @param targetURI
//	 *            The target part URI.
//	 * @return A fully relativize part name URI ('word/media/image1.gif',
//	 *         '/word/document.xml' => 'media/image1.gif') else
//	 *         null.
//	 */
//	public static URI OLDrelativizeURI(URI sourceURI, URI targetURI) {
//		StringBuffer retVal = new StringBuffer();
//		String[] segmentsSource = sourceURI.getPath().split("/");
//		String[] segmentsTarget = targetURI.getPath().split("/");
//
//		// If the source URI is empty
//		if (segmentsSource.length == 0) {
//			return null;
//		}
//
//		// If target URI is empty
//		if (segmentsTarget.length == 0) {
//			if (sourceURI.getPath().startsWith(FORWARD_SLASH_STRING)) {
//				try {
//					return new URI(sourceURI.getPath().substring(1));
//				} catch (Exception e) {
//					return null;
//				}
//			} else
//				return sourceURI;
//		}
//
//		// Relativize the source URI against the target URI.
//		for (short i = 0, j = 0; i < segmentsSource.length
//				&& j < segmentsTarget.length; ++i, ++j) {
//			if (segmentsSource[i].equalsIgnoreCase(segmentsTarget[j])) {
//				if (i < segmentsSource.length - 1) {
//					continue;
//				} else {
//					// We add the last segment whatever it happens
//					retVal.append("/");
//					retVal.append(segmentsTarget[i]);
//					break;
//				}
//			} else {
//				for (; i < segmentsSource.length; ++i) {
//					retVal.append("/");
//					retVal.append(segmentsSource[i]);
//				}
//				break;
//			}
//		}
//		try {
//			PartName retPartName = new PartName(
//					retVal.toString(), true);
//			return new URI(retPartName.getURI().getPath().substring(1));
//		} catch (Exception e) {
//			return null;
//		}
//	}

	
	
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy