| /******************************************************************************* |
| * Copyright (c) 2006, 2013 IBM Corporation and others. |
| * |
| * This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License 2.0 |
| * which accompanies this distribution, and is available at |
| * https://www.eclipse.org/legal/epl-2.0/ |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| *******************************************************************************/ |
| |
| package org.eclipse.equinox.internal.transforms; |
| |
| import java.io.*; |
| import java.util.Enumeration; |
| import java.util.regex.Matcher; |
| import java.util.regex.Pattern; |
| import org.eclipse.equinox.internal.transforms.LazyInputStream.InputStreamProvider; |
| import org.eclipse.osgi.framework.log.FrameworkLogEntry; |
| import org.eclipse.osgi.internal.debug.Debug; |
| import org.eclipse.osgi.internal.framework.EquinoxContainer; |
| import org.eclipse.osgi.storage.BundleInfo.Generation; |
| import org.eclipse.osgi.storage.StorageUtil; |
| import org.eclipse.osgi.storage.bundlefile.*; |
| import org.osgi.framework.Bundle; |
| |
| /** |
| * This class is capable of providing transformed versions of entries contained within a base bundle file. |
| * For requests that transform bundle contents into local resources (such as file URLs) the transformed state of the bundle is written to the configuration area. |
| */ |
| public class TransformedBundleFile extends BundleFileWrapper { |
| |
| private final TransformerHook transformerHook; |
| private final BundleFile delegate; |
| private final Generation generation; |
| private final Debug debug; |
| |
| /** |
| * Create a wrapped bundle file. |
| * Requests into this file will be compared to the list of known transformers and transformer templates and if there's a match the transformed entity is returned instead of the original. |
| * @param transformerHook the transformer hook |
| * @param data the original data |
| * @param delegate the original file |
| */ |
| public TransformedBundleFile(TransformerHook transformerHook, Generation generation, BundleFile delegate) { |
| super(delegate); |
| this.transformerHook = transformerHook; |
| this.generation = generation; |
| this.delegate = delegate; |
| this.debug = generation.getBundleInfo().getStorage().getConfiguration().getDebug(); |
| } |
| |
| Generation getGeneration() { |
| return generation; |
| } |
| |
| public boolean equals(Object obj) { |
| return delegate.equals(obj); |
| } |
| |
| public BundleEntry getEntry(String path) { |
| |
| final BundleEntry original = delegate.getEntry(path); |
| if (generation.getRevision() == null || path == null || original == null) |
| return original; |
| |
| LazyInputStream stream = new LazyInputStream(new InputStreamProvider() { |
| |
| @Override |
| public InputStream getInputStream() throws IOException { |
| return original.getInputStream(); |
| } |
| }); |
| InputStream wrappedStream = getInputStream(stream, generation.getRevision().getBundle(), path); |
| if (wrappedStream == null) |
| return original; |
| return new TransformedBundleEntry(this, original, wrappedStream); |
| } |
| |
| /** |
| * Return the input stream that results from applying the given transformer |
| * URL to the provided input stream. |
| * |
| * @param inputStream |
| * the stream to transform |
| * @param bundle |
| * the resource representing the transformer |
| * @return the transformed stream |
| */ |
| protected InputStream getInputStream(InputStream inputStream, Bundle bundle, String path) { |
| String namespace = bundle.getSymbolicName(); |
| |
| String[] transformTypes = transformerHook.getTransformTypes(); |
| if (transformTypes.length == 0) |
| return null; |
| for (String transformType : transformTypes) { |
| StreamTransformer transformer = transformerHook.getTransformer(transformType); |
| if (transformer == null) |
| continue; |
| TransformTuple[] transformTuples = transformerHook.getTransformsFor(transformType); |
| if (transformTuples == null) |
| continue; |
| for (TransformTuple transformTuple : transformTuples) { |
| if (match(transformTuple.bundlePattern, namespace) && match(transformTuple.pathPattern, path)) { |
| try { |
| return transformer.getInputStream(inputStream, transformTuple.transformerUrl); |
| } catch (IOException e) { |
| generation.getBundleInfo().getStorage().getLogServices().log(EquinoxContainer.NAME, FrameworkLogEntry.ERROR, "Problem obtaining transformed stream from transformer : " //$NON-NLS-1$ |
| + transformer.getClass().getName(), e); |
| |
| } |
| } |
| } |
| } |
| |
| return null; |
| } |
| |
| /** |
| * Return whether the given string matches the given pattern. |
| * |
| * @param pattern |
| * @param string |
| * @return whether the given string matches the given pattern |
| */ |
| private boolean match(Pattern pattern, String string) { |
| Matcher matcher = pattern.matcher(string); |
| return matcher.matches(); |
| } |
| |
| /** |
| * This file is a copy of {@link ZipBundleFile#getFile(String, boolean)} |
| * with modifications. |
| */ |
| public File getFile(String path, boolean nativeCode) { |
| File originalFile = delegate.getFile(path, nativeCode); |
| |
| if (originalFile == null) |
| return null; |
| if (!hasTransforms(path)) |
| return originalFile; |
| try { |
| File nested = getExtractFile(path); |
| if (nested != null) { |
| if (nested.exists()) { |
| /* the entry is already cached */ |
| if (debug.DEBUG_GENERAL) |
| Debug.println("File already present: " + nested.getPath()); //$NON-NLS-1$ |
| if (nested.isDirectory()) |
| // must ensure the complete directory is extracted (bug |
| // 182585) |
| extractDirectory(path); |
| } else { |
| if (originalFile.isDirectory()) { |
| if (!nested.mkdirs()) { |
| throw new IOException("Unable to create directory: " + nested.getAbsolutePath()); //$NON-NLS-1$ |
| } |
| extractDirectory(path); |
| } else { |
| InputStream in = getEntry(path).getInputStream(); |
| if (in == null) |
| return null; |
| // if (in instanceof ) |
| /* the entry has not been cached */ |
| if (debug.DEBUG_GENERAL) |
| Debug.println("Creating file: " + nested.getPath()); //$NON-NLS-1$ |
| /* create the necessary directories */ |
| File dir = new File(nested.getParent()); |
| if (!dir.exists() && !dir.mkdirs()) { |
| throw new IOException("Unable to create directory: " + dir.getAbsolutePath()); //$NON-NLS-1$ |
| } |
| /* copy the entry to the cache */ |
| StorageUtil.readFile(in, nested); |
| if (nativeCode) { |
| generation.getBundleInfo().getStorage().setPermissions(nested); |
| } |
| } |
| } |
| |
| return nested; |
| } |
| } catch (IOException e) { |
| if (debug.DEBUG_GENERAL) |
| Debug.printStackTrace(e); |
| } |
| return null; |
| } |
| |
| /** |
| * Answers whether the resource at the given path or any of its children has |
| * a transform associated with it. |
| * |
| * @param path |
| * @return whether the resource at the given path or any of its children has |
| * a transform associated with it. |
| */ |
| private boolean hasTransforms(String path) { |
| if (!transformerHook.hasTransformers()) |
| return false; |
| return transformerHook.hasTransformsFor(generation.getRevision().getBundle()); |
| } |
| |
| /** |
| * Extracts a directory and all sub content to disk |
| * |
| * @param dirName |
| * the directory name to extract |
| * @return the File used to extract the content to. A value of |
| * <code>null</code> is returned if the directory to extract does |
| * not exist or if content extraction is not supported. |
| * |
| * This method is derived from ZipBundleFile#extractDirectory(String). |
| */ |
| protected synchronized File extractDirectory(String dirName) { |
| Enumeration<String> entries = delegate.getEntryPaths(dirName); |
| |
| while (entries.hasMoreElements()) { |
| String entryPath = entries.nextElement(); |
| if (entryPath.startsWith(dirName)) |
| getFile(entryPath, false); |
| } |
| return getExtractFile(dirName); |
| } |
| |
| protected File getExtractFile(String entryName) { |
| String path = ".tf"; /* put all these entries in this subdir *///$NON-NLS-1$ |
| String name = entryName.replace('/', File.separatorChar); |
| /* |
| * if name has a leading slash |
| */ |
| if ((name.length() > 1) && (name.charAt(0) == File.separatorChar)) |
| path = path.concat(name); |
| else |
| path = path + File.separator + name; |
| return generation.getExtractFile(path); |
| } |
| |
| public int hashCode() { |
| return delegate.hashCode(); |
| } |
| |
| public String toString() { |
| return delegate.toString(); |
| } |
| } |