/* * Copyright (c) 2012 Data Harmonisation Panel * * All rights reserved. This program and the accompanying materials are made * available under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation, either version 3 of the License, * or (at your option) any later version. * * You should have received a copy of the GNU Lesser General Public License * along with this distribution. If not, see <http://www.gnu.org/licenses/>. * * Contributors: * HUMBOLDT EU Integrated Project #030962 * Data Harmonisation Panel <http://www.dhpanel.eu> */ package eu.esdihumboldt.hale.common.core.io; import java.io.UnsupportedEncodingException; import java.net.URI; import java.net.URLEncoder; import java.text.MessageFormat; import org.apache.commons.io.FilenameUtils; import de.fhg.igd.slf4jplus.ALogger; import de.fhg.igd.slf4jplus.ALoggerFactory; import eu.esdihumboldt.util.io.IOUtils; import eu.esdihumboldt.util.resource.Resources; /** * Updater class for a path. * * @author Patrick Lieb * @author Kai Schwierczek */ public class PathUpdate { private static final ALogger log = ALoggerFactory.getLogger(PathUpdate.class); private final URI oldLocation; private final URI newLocation; private String oldRaw; private String newRaw; /** * Create a path updater based on a pair of known old and new locations. * * @param oldLocation the old location of a file, may be null * @param newLocation the new location of the same file (though the file * name may be different), may be null */ public PathUpdate(URI oldLocation, URI newLocation) { super(); this.oldLocation = oldLocation; this.newLocation = newLocation; /* * analyze paths (w/o file name) of both URIs to find out which of the * later parts are equal, to determine which part of the old location * has to be replaced by which part of the new location for other files * that have been moved in a similar way to the analyzed file. */ if (oldLocation != null && newLocation != null && !oldLocation.equals(newLocation)) analysePaths(oldLocation, newLocation); } /** * Tries to find an existing readable URI.<br> * <ul> * <li>if the URI isn't absolute: * <ul> * <li>if a new location is available it is resolved against that</li> * <li>if an old location is available it is resolved against that</li> * </ul> * </li> * <li>if the URI is absolute: * <ul> * <li>if an old and a new location is available it is transformed in the * same way</li> * <li>the URI is used as is</li> * </ul> * </li> * </ul> * If none of the applicable cases results in a valid, existing URI and * tryFallback is true {@link #updatePathFallback(URI)} is returned, * otherwise <code>null</code> is returned. * * @param uri the URI in question * @param tryFallback whether to use {@link #updatePathFallback(URI)} in the * end or not * @param allowResource whether to allow resolving through {@link Resources} * @return a valid, existing URI or <code>null</code> */ public URI findLocation(URI uri, boolean tryFallback, boolean allowResource) { return findLocation(uri, tryFallback, allowResource, false); } /** * Tries to find an existing readable URI.<br> * <ul> * <li>if the URI isn't absolute: * <ul> * <li>if a new location is available it is resolved against that</li> * <li>if an old location is available it is resolved against that</li> * </ul> * </li> * <li>if the URI is absolute: * <ul> * <li>if an old and a new location is available it is transformed in the * same way</li> * <li>the URI is used as is</li> * </ul> * </li> * </ul> * If none of the applicable cases results in a valid, existing URI and * tryFallback is true {@link #updatePathFallback(URI)} is returned, * otherwise <code>null</code> is returned. * * @param uri the URI in question * @param tryFallback whether to use {@link #updatePathFallback(URI)} in the * end or not * @param allowResource whether to allow resolving through {@link Resources} * @param keepRelative If the URI is relative to the new location and * keepRelative is set, the URI is returned as is.<br> * Also, if the URI is relative to the old location and it is * possible to construct a relative path to the new location, * that is returned * @return a valid, existing URI or <code>null</code> */ public URI findLocation(URI uri, boolean tryFallback, boolean allowResource, boolean keepRelative) { if ("jdbc".equals(uri.getScheme())) { // not possible to update JDBC URLs or test the stream return uri; } if (!uri.isAbsolute()) { if (newLocation != null) { URI newAbsolute = newLocation.resolve(uri); if (HaleIO.testStream(newAbsolute, allowResource)) { if (keepRelative) { return uri; } else { return newAbsolute; } } else { // Check if the resource file name needs // to be URL-encoded first (for project archives // that were created w/ hale studio 3.2.0 and before) String resourcePath = FilenameUtils.getPath(uri.toString()); String resourceFileName = FilenameUtils.getName(uri.toString()); try { String encodedPath = resourcePath + URLEncoder.encode(resourceFileName, "UTF-8"); URI encodedUri = URI.create(encodedPath); newAbsolute = newLocation.resolve(encodedUri); if (HaleIO.testStream(newAbsolute, allowResource)) { if (keepRelative) { return encodedUri; } else { return newAbsolute; } } } catch (UnsupportedEncodingException e) { log.debug(MessageFormat.format("Could not URL-encode \"{0}\"", resourceFileName), e); } } } if (oldLocation != null) { URI oldAbsolute = oldLocation.resolve(uri); if (HaleIO.testStream(oldAbsolute, allowResource)) { if (keepRelative) return IOUtils.getRelativePath(oldAbsolute, newLocation); else return oldAbsolute; } } } else { if (oldLocation != null && newLocation != null) { URI changed = changePath(uri); if (HaleIO.testStream(changed, allowResource)) return changed; } if (HaleIO.testStream(uri, allowResource)) return uri; } if (tryFallback) return updatePathFallback(uri); else return null; } /** * Create an alternative path for the given location if it matches changes * from old to new location. If either old or new location is null, or the * given URI wasn't changed in the same way, this method has no effect. * * @param oldSource path where the file was saved to * @return the new URI */ public URI changePath(URI oldSource) { if (oldRaw != null) return URI.create(oldSource.toString().replace(oldRaw, newRaw)); else return oldSource; } // Analyzes the old and the new project path and tries to return the new one private void analysePaths(URI oldLocation, URI newLocation) { String o = oldLocation.toString(); String n = newLocation.toString(); // cut off file name. only look at the path to the files. int oindex = o.lastIndexOf('/'); o = (oindex >= 0) ? (o.substring(0, oindex)) : ""; int nindex = n.lastIndexOf('/'); n = (nindex >= 0) ? (n.substring(0, nindex)) : ""; int commonEndLength = 0; while (commonEndLength < o.length() && commonEndLength < n.length() && o.charAt(o.length() - commonEndLength - 1) == n .charAt(n.length() - commonEndLength - 1)) { commonEndLength++; } oldRaw = o.substring(0, o.length() - commonEndLength); newRaw = n.substring(0, n.length() - commonEndLength); } /** * @return the oldLocation, may be null */ public URI getOldLocation() { return oldLocation; } /** * @return the newLocation, may be null */ public URI getNewLocation() { return newLocation; } /** * Update the path to a resource if automatic update fails. The default * implementation returns <code>null</code>, which means the location is not * updated. * * @param oldLocation the old resource location * @return the replacement resource location or <code>null</code> */ protected URI updatePathFallback(URI oldLocation) { return null; } }