1 /*******************************************************************************
2  * Copyright (c) 2008, 2018 IBM Corporation and others.
3  *
4  * This program and the accompanying materials
5  * are made available under the terms of the Eclipse Public License 2.0
6  * which accompanies this distribution, and is available at
7  * https://www.eclipse.org/legal/epl-2.0/
8  *
9  * SPDX-License-Identifier: EPL-2.0
10  *
11  * Contributors:
12  *     IBM Corporation - initial API and implementation
13  *     Karsten Thoms - Bug#535325
14  *******************************************************************************/
15 package org.eclipse.pde.internal.core.target;
16 
17 import java.io.File;
18 import java.util.ArrayList;
19 import java.util.Arrays;
20 import java.util.List;
21 import java.util.Objects;
22 import org.eclipse.core.runtime.CoreException;
23 import org.eclipse.core.runtime.IProgressMonitor;
24 import org.eclipse.core.runtime.IStatus;
25 import org.eclipse.core.runtime.Status;
26 import org.eclipse.core.runtime.SubMonitor;
27 import org.eclipse.osgi.util.NLS;
28 import org.eclipse.pde.core.target.ITargetDefinition;
29 import org.eclipse.pde.core.target.TargetBundle;
30 import org.eclipse.pde.core.target.TargetFeature;
31 import org.eclipse.pde.internal.build.IPDEBuildConstants;
32 import org.eclipse.pde.internal.core.PDECore;
33 
34 /**
35  * A directory of bundles.
36  *
37  * @since 3.5
38  */
39 public class DirectoryBundleContainer extends AbstractBundleContainer {
40 
41 	/**
42 	 * Constant describing the type of bundle container
43 	 */
44 	public static final String TYPE = "Directory"; //$NON-NLS-1$
45 
46 	/**
47 	 * Path to this container's directory in the local file system.
48 	 * The path may contain string substitution variables.
49 	 */
50 	private final String fPath;
51 
52 	/**
53 	 * Constructs a directory bundle container at the given location.
54 	 *
55 	 * @param path directory location in the local file system, may contain string substitution variables
56 	 */
DirectoryBundleContainer(String path)57 	public DirectoryBundleContainer(String path) {
58 		fPath = path;
59 	}
60 
61 	@Override
getLocation(boolean resolve)62 	public String getLocation(boolean resolve) throws CoreException {
63 		if (resolve) {
64 			return getDirectory().toString();
65 		}
66 		return fPath;
67 	}
68 
69 	@Override
getType()70 	public String getType() {
71 		return TYPE;
72 	}
73 
74 	@Override
resolveBundles(ITargetDefinition definition, IProgressMonitor monitor)75 	protected TargetBundle[] resolveBundles(ITargetDefinition definition, IProgressMonitor monitor) throws CoreException {
76 		File dir = getDirectory();
77 		if (dir.isDirectory()) {
78 			File site = getSite(dir);
79 			File[] files = site.listFiles();
80 			SubMonitor localMonitor = SubMonitor.convert(monitor, Messages.DirectoryBundleContainer_0, files.length);
81 			return Arrays.stream(files).parallel() //
82 					.map(file -> {
83 						localMonitor.split(1);
84 						try {
85 							return new TargetBundle(file);
86 						} catch (CoreException e) {
87 							// Ignore non-bundle files
88 							return null;
89 						}
90 					}).filter(Objects::nonNull) //
91 					.toArray(TargetBundle[]::new);
92 		}
93 		throw new CoreException(new Status(IStatus.ERROR, PDECore.PLUGIN_ID, NLS.bind(Messages.DirectoryBundleContainer_1, dir.toString())));
94 	}
95 
96 	@Override
resolveFeatures(ITargetDefinition definition, IProgressMonitor monitor)97 	protected TargetFeature[] resolveFeatures(ITargetDefinition definition, IProgressMonitor monitor) throws CoreException {
98 		File dir = getDirectory();
99 		if (!dir.isDirectory()) {
100 			throw new CoreException(new Status(IStatus.ERROR, PDECore.PLUGIN_ID,
101 					NLS.bind(Messages.DirectoryBundleContainer_1, dir.toString())));
102 		}
103 		File site = getFeatureSite(dir);
104 		File[] files = site.listFiles();
105 		SubMonitor localMonitor = SubMonitor.convert(monitor, Messages.DirectoryBundleContainer_0, files.length);
106 		List<TargetFeature> features = new ArrayList<>(files.length);
107 		Arrays.stream(files).parallel().forEach(file -> {
108 			try {
109 				TargetFeature rf = new TargetFeature(file);
110 				synchronized (features) {
111 					features.add(rf);
112 				}
113 			} catch (CoreException e) {
114 				// Ignore non-feature files
115 			}
116 			localMonitor.split(1);
117 		});
118 		return features.toArray(new TargetFeature[features.size()]);
119 	}
120 
121 	/**
122 	 * Returns the directory to search for bundles in.
123 	 *
124 	 * @return directory if unable to resolve variables in the path
125 	 */
getDirectory()126 	protected File getDirectory() throws CoreException {
127 		String path = resolveVariables(fPath);
128 		return new File(path);
129 	}
130 
131 	@Override
equals(Object o)132 	public boolean equals(Object o) {
133 		if (o instanceof DirectoryBundleContainer) {
134 			DirectoryBundleContainer dbc = (DirectoryBundleContainer) o;
135 			return fPath.equals(dbc.fPath);
136 		}
137 		return false;
138 	}
139 
140 	@Override
hashCode()141 	public int hashCode() {
142 		return fPath.hashCode();
143 	}
144 
145 	@Override
toString()146 	public String toString() {
147 		return new StringBuilder("Directory ").append(fPath).toString(); //$NON-NLS-1$
148 	}
149 
150 	/**
151 	 * Returns the directory to scan for bundles - a "plug-ins" sub directory if present.
152 	 *
153 	 * @param root the location the container specifies as a root directory
154 	 * @return the given directory or its plug-ins sub directory if present
155 	 */
getSite(File root)156 	private File getSite(File root) {
157 		File file = new File(root, IPDEBuildConstants.DEFAULT_PLUGIN_LOCATION);
158 		if (file.exists()) {
159 			return file;
160 		}
161 		return root;
162 	}
163 
164 	/**
165 	 * Returns the directory to scan for features - a "features" sub directory
166 	 * if present.
167 	 *
168 	 * @param root
169 	 *            the location the container specifies as a root directory
170 	 * @return the given directory or its plug-ins sub directory if present
171 	 */
getFeatureSite(File root)172 	private File getFeatureSite(File root) {
173 		File file = new File(root, IPDEBuildConstants.DEFAULT_FEATURE_LOCATION);
174 		if (file.exists()) {
175 			return file;
176 		}
177 		return root;
178 	}
179 
180 }
181