1 /******************************************************************************* 2 * Copyright (c) 2000, 2017 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 *******************************************************************************/ 14 package org.eclipse.pde.internal.build; 15 16 import java.io.*; 17 import java.net.MalformedURLException; 18 import java.net.URL; 19 import java.util.*; 20 import java.util.Map.Entry; 21 import java.util.zip.ZipFile; 22 import org.eclipse.core.runtime.*; 23 import org.eclipse.equinox.p2.publisher.eclipse.FeatureEntry; 24 import org.eclipse.osgi.service.resolver.BundleDescription; 25 import org.eclipse.osgi.service.resolver.VersionRange; 26 import org.eclipse.osgi.util.ManifestElement; 27 import org.eclipse.osgi.util.NLS; 28 import org.eclipse.pde.internal.build.ant.AntScript; 29 import org.eclipse.pde.internal.build.site.BuildTimeFeature; 30 import org.eclipse.pde.internal.build.site.BuildTimeSite; 31 import org.osgi.framework.BundleException; 32 import org.osgi.framework.Version; 33 34 /** 35 * General utility class. 36 */ 37 public final class Utils implements IPDEBuildConstants, IBuildPropertiesConstants, IXMLConstants { 38 static class ArrayEnumeration implements Enumeration<Object> { 39 private final Object[] array; 40 int cur = 0; 41 ArrayEnumeration(Object[] array)42 public ArrayEnumeration(Object[] array) { 43 this.array = new Object[array.length]; 44 System.arraycopy(array, 0, this.array, 0, this.array.length); 45 } 46 47 @Override hasMoreElements()48 public boolean hasMoreElements() { 49 return cur < array.length; 50 } 51 52 @Override nextElement()53 public Object nextElement() { 54 return array[cur++]; 55 } 56 } 57 58 // The 64 characters that are legal in a version qualifier, in lexicographical order. 59 private static final String BASE_64_ENCODING = "-0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz"; //$NON-NLS-1$ 60 61 // regex expressions and keys for parsing feature root properties 62 private static final String REGEX_ROOT_CONFIG = "^root((\\.[\\w-\\*]+){3})$"; //$NON-NLS-1$ 63 private static final String REGEX_ROOT_CONFIG_FOLDER = "^root((\\.[\\w-\\*]+){3})?\\.folder\\.(.*)$"; //$NON-NLS-1$ 64 private static final String REGEX_ROOT_CONFIG_PERMISSIONS = "^root((\\.[\\w-\\*]+){3})?\\.permissions\\.(.*)$"; //$NON-NLS-1$ 65 private static final String REGEX_ROOT_CONFIG_LINK = "^root((\\.[\\w-\\*]+){3})?\\.link$"; //$NON-NLS-1$ 66 public static final String ROOT_PERMISSIONS = "!!ROOT.PERMISSIONS!!"; //$NON-NLS-1$ 67 public static final String ROOT_LINK = "!!ROOT.LINK!!"; //$NON-NLS-1$ 68 public static final String ROOT_COMMON = "!!COMMON!!"; //$NON-NLS-1$ 69 70 /** 71 * returns a value 1 - 64 for valid qualifier characters. Returns 0 for non-valid characters 72 */ qualifierCharValue(char c)73 public static int qualifierCharValue(char c) { 74 int index = BASE_64_ENCODING.indexOf(c); 75 // The "+ 1" is very intentional. For a blank (or anything else that 76 // is not a legal character), we want to return 0. For legal 77 // characters, we want to return one greater than their position, so 78 // that a blank is correctly distinguished from '-'. 79 return index + 1; 80 } 81 82 // Integer to character conversion in our base-64 encoding scheme. If the 83 // input is out of range, an illegal character will be returned. base64Character(int number)84 public static char base64Character(int number) { 85 if (number < 0 || number > 63) { 86 return ' '; 87 } 88 return BASE_64_ENCODING.charAt(number); 89 } 90 createVersionRange(String versionId)91 public static VersionRange createVersionRange(String versionId) { 92 VersionRange range = null; 93 if (versionId == null || versionId.length() == 0 || GENERIC_VERSION_NUMBER.equals(versionId)) 94 range = VersionRange.emptyRange; 95 else { 96 int qualifierIdx = versionId.indexOf(IBuildPropertiesConstants.PROPERTY_QUALIFIER); 97 if (qualifierIdx != -1) { 98 String newVersion = versionId.substring(0, qualifierIdx); 99 if (newVersion.endsWith(".")) //$NON-NLS-1$ 100 newVersion = newVersion.substring(0, newVersion.length() - 1); 101 102 Version lower = new Version(newVersion); 103 Version upper = null; 104 String newQualifier = incrementQualifier(lower.getQualifier()); 105 if (newQualifier == null) 106 upper = new Version(lower.getMajor(), lower.getMinor(), lower.getMicro() + 1); 107 else 108 upper = new Version(lower.getMajor(), lower.getMinor(), lower.getMicro(), newQualifier); 109 range = new VersionRange(lower, true, upper, false); 110 } else { 111 range = new VersionRange(new Version(versionId), true, new Version(versionId), true); 112 } 113 } 114 return range; 115 } 116 createVersionRange(FeatureEntry entry)117 public static VersionRange createVersionRange(FeatureEntry entry) { 118 String versionSpec = entry.getVersion(); 119 if (versionSpec == null) 120 return VersionRange.emptyRange; 121 122 Version version = new Version(versionSpec); 123 if (version.equals(Version.emptyVersion)) 124 return VersionRange.emptyRange; 125 126 String match = entry.getMatch(); 127 if (!entry.isRequires() || match == null) { 128 return createVersionRange(versionSpec); 129 } 130 131 if (match.equals("perfect")) //$NON-NLS-1$ 132 return new VersionRange(version, true, version, true); 133 if (match.equals("equivalent")) { //$NON-NLS-1$ 134 Version upper = new Version(version.getMajor(), version.getMinor() + 1, 0); 135 return new VersionRange(version, true, upper, false); 136 } 137 if (match.equals("compatible")) { //$NON-NLS-1$ 138 Version upper = new Version(version.getMajor() + 1, 0, 0); 139 return new VersionRange(version, true, upper, false); 140 } 141 if (match.equals("greaterOrEqual")) //$NON-NLS-1$ 142 return new VersionRange(version, true, new VersionRange(null).getMaximum(), true); 143 144 return VersionRange.emptyRange; 145 } 146 incrementQualifier(String qualifier)147 private static String incrementQualifier(String qualifier) { 148 int idx = qualifier.length() - 1; 149 150 for (; idx >= 0; idx--) { 151 //finding last non-'z' character 152 if (qualifier.charAt(idx) != 'z') 153 break; 154 } 155 156 if (idx >= 0) { 157 // qualifierCharValue returns 1 - 64, this is an implicit +1 over 158 // the characters returned by base64Character 159 int c = Utils.qualifierCharValue(qualifier.charAt(idx)); 160 String newQualifier = qualifier.substring(0, idx); 161 newQualifier += Utils.base64Character(c); 162 return newQualifier; 163 } 164 165 return null; 166 } 167 168 /** 169 * Convert a list of tokens into an array. The list separator has to be 170 * specified. 171 */ getArrayFromString(String list, String separator)172 public static String[] getArrayFromString(String list, String separator) { 173 if (list == null || list.trim().equals("")) //$NON-NLS-1$ 174 return new String[0]; 175 List<String> result = new ArrayList<>(); 176 for (StringTokenizer tokens = new StringTokenizer(list, separator); tokens.hasMoreTokens();) { 177 String token = tokens.nextToken().trim(); 178 if (!token.equals("")) //$NON-NLS-1$ 179 result.add(token); 180 } 181 return result.toArray(new String[result.size()]); 182 } 183 184 /** 185 * Convert a list of tokens into an array. The list separator has to be 186 * specified. The spcecificity of this method is that it returns an empty 187 * element when to same separators are following each others. For example 188 * the string a,,b returns the following array [a, ,b] 189 * 190 */ getArrayFromStringWithBlank(String list, String separator)191 public static String[] getArrayFromStringWithBlank(String list, String separator) { 192 if (list == null || list.trim().length() == 0) 193 return new String[0]; 194 List<String> result = new ArrayList<>(); 195 boolean previousWasSeparator = true; 196 for (StringTokenizer tokens = new StringTokenizer(list, separator, true); tokens.hasMoreTokens();) { 197 String token = tokens.nextToken().trim(); 198 if (token.equals(separator)) { 199 if (previousWasSeparator) 200 result.add(""); //$NON-NLS-1$ 201 previousWasSeparator = true; 202 } else { 203 result.add(token); 204 previousWasSeparator = false; 205 } 206 } 207 return result.toArray(new String[result.size()]); 208 } 209 210 /** 211 * Return a string array constructed from the given list of comma-separated 212 * tokens. 213 * 214 * @param list 215 * the list to convert 216 * @return the array of strings 217 */ getArrayFromString(String list)218 public static String[] getArrayFromString(String list) { 219 return getArrayFromString(list, ","); //$NON-NLS-1$ 220 } 221 222 /** 223 * Converts an array of strings into an array of URLs. 224 * 225 * @param target 226 * @return URL[] 227 * @throws CoreException 228 */ asURL(String[] target)229 public static URL[] asURL(String[] target) throws CoreException { 230 if (target == null) 231 return null; 232 try { 233 URL[] result = new URL[target.length]; 234 for (int i = 0; i < target.length; i++) 235 result[i] = new URL(target[i]); 236 return result; 237 } catch (MalformedURLException e) { 238 throw new CoreException(new Status(IStatus.ERROR, PI_PDEBUILD, EXCEPTION_MALFORMED_URL, e.getMessage(), e)); 239 } 240 } 241 asURL(Collection<File> target)242 public static URL[] asURL(Collection<File> target) throws CoreException { 243 if (target == null) 244 return null; 245 try { 246 URL[] result = new URL[target.size()]; 247 int i = 0; 248 for (Iterator<File> iter = target.iterator(); iter.hasNext();) { 249 result[i++] = iter.next().toURL(); 250 } 251 return result; 252 } catch (MalformedURLException e) { 253 throw new CoreException(new Status(IStatus.ERROR, PI_PDEBUILD, EXCEPTION_MALFORMED_URL, e.getMessage(), e)); 254 } 255 } 256 asFile(String[] target)257 public static File[] asFile(String[] target) { 258 if (target == null) 259 return new File[0]; 260 File[] result = new File[target.length]; 261 for (int i = 0; i < result.length; i++) { 262 result[i] = new File(target[i]); 263 } 264 return result; 265 } 266 asFile(URL[] target)267 public static File[] asFile(URL[] target) { 268 if (target == null) 269 return new File[0]; 270 File[] result = new File[target.length]; 271 for (int i = 0; i < result.length; i++) { 272 result[i] = new File(target[i].getFile()); 273 } 274 return result; 275 } 276 asFile(Collection<?> collection)277 public static File[] asFile(Collection<?> collection) { 278 if (collection.size() == 0) 279 return new File[0]; 280 Object first = collection.iterator().next(); 281 if (first instanceof String) 282 return asFile(collection.toArray(new String[collection.size()])); 283 else if (first instanceof URL) 284 return asFile(collection.toArray(new URL[collection.size()])); 285 else if (first instanceof File) 286 return collection.toArray(new File[collection.size()]); 287 throw new IllegalArgumentException(); 288 } 289 290 /** 291 * Return a string which is a concatination of each member of the given 292 * collection, separated by the given separator. 293 * 294 * @param collection 295 * the collection to concatinate 296 * @param separator 297 * the separator to use 298 * @return String 299 */ getStringFromCollection(Collection<?> collection, String separator)300 public static String getStringFromCollection(Collection<?> collection, String separator) { 301 StringBuffer result = new StringBuffer(); 302 boolean first = true; 303 for (Iterator<?> i = collection.iterator(); i.hasNext();) { 304 if (first) 305 first = false; 306 else 307 result.append(separator); 308 result.append(i.next()); 309 } 310 return result.toString(); 311 } 312 313 /** 314 * Return a string which is a concatination of each member of the given 315 * array, separated by the given separator. 316 * 317 * @param values 318 * the array to concatinate 319 * @param separator 320 * the separator to use 321 * @return String 322 */ getStringFromArray(String[] values, String separator)323 public static String getStringFromArray(String[] values, String separator) { 324 StringBuffer result = new StringBuffer(); 325 for (int i = 0; i < values.length; i++) { 326 if (values[i] != null) { 327 if (i > 0) 328 result.append(separator); 329 result.append(values[i]); 330 } 331 } 332 return result.toString(); 333 } 334 335 /** 336 * Return a path which is equivalent to the given location relative to the 337 * specified base path. 338 * 339 * @param location 340 * the location to convert 341 * @param base 342 * the base path 343 * @return IPath 344 */ makeRelative(IPath location, IPath base)345 public static IPath makeRelative(IPath location, IPath base) { 346 //can't make relative if the devices don't match 347 if (location.getDevice() == null) { 348 if (base.getDevice() != null) 349 return location; 350 } else { 351 if (!location.getDevice().equalsIgnoreCase(base.getDevice())) 352 return location; 353 } 354 int baseCount = base.segmentCount(); 355 int count = base.matchingFirstSegments(location); 356 String temp = ""; //$NON-NLS-1$ 357 for (int j = 0; j < baseCount - count; j++) 358 temp += "../"; //$NON-NLS-1$ 359 return new Path(temp).append(location.removeFirstSegments(count)); 360 } 361 362 /** 363 * Transfers all available bytes from the given input stream to the given 364 * output stream. Regardless of failure, this method closes both streams. 365 * 366 * @param source 367 * @param destination 368 * @throws IOException 369 */ transferStreams(InputStream source, OutputStream destination)370 public static void transferStreams(InputStream source, OutputStream destination) throws IOException { 371 source = new BufferedInputStream(source); 372 destination = new BufferedOutputStream(destination); 373 try { 374 byte[] buffer = new byte[8192]; 375 while (true) { 376 int bytesRead = -1; 377 if ((bytesRead = source.read(buffer)) == -1) 378 break; 379 destination.write(buffer, 0, bytesRead); 380 } 381 } finally { 382 try { 383 source.close(); 384 } catch (IOException e) { 385 // ignore 386 } 387 try { 388 destination.close(); 389 } catch (IOException e) { 390 // ignore 391 } 392 } 393 } 394 copyFile(String src, String dest)395 static public void copyFile(String src, String dest) throws IOException { 396 File source = new File(src); 397 if (!source.exists()) 398 return; 399 File destination = new File(dest); 400 File destDir = destination.getParentFile(); 401 if ((!destDir.exists() && !destDir.mkdirs()) || destDir.isFile()) 402 return; //we will fail trying to create the file, TODO log warning/error 403 404 copy(source, destination); 405 } 406 copy(File source, File destination)407 public static void copy(File source, File destination) throws IOException { 408 org.eclipse.pde.internal.publishing.Utils.copy(source, destination); 409 } 410 writeBuffer(StringBuffer buffer, File outputFile)411 public static void writeBuffer(StringBuffer buffer, File outputFile) throws IOException { 412 FileOutputStream stream = null; 413 try { 414 outputFile.getParentFile().mkdirs(); 415 stream = new FileOutputStream(outputFile); 416 stream.write(buffer.toString().getBytes()); 417 } finally { 418 close(stream); 419 } 420 } 421 writeProperties(Properties properites, File outputFile, String comment)422 public static void writeProperties(Properties properites, File outputFile, String comment) throws IOException { 423 outputFile.getParentFile().mkdirs(); 424 OutputStream buildFile = new BufferedOutputStream(new FileOutputStream(outputFile)); 425 try { 426 properites.store(buildFile, comment); 427 } finally { 428 close(buildFile); 429 } 430 } 431 getPluginEntry(BuildTimeFeature feature, String pluginId, boolean raw)432 public static FeatureEntry[] getPluginEntry(BuildTimeFeature feature, String pluginId, boolean raw) { 433 FeatureEntry[] plugins; 434 if (raw) 435 plugins = feature.getRawPluginEntries(); 436 else 437 plugins = feature.getPluginEntries(); 438 List<FeatureEntry> foundEntries = new ArrayList<>(5); 439 440 for (int i = 0; i < plugins.length; i++) { 441 if (plugins[i].getId().equals(pluginId)) 442 foundEntries.add(plugins[i]); 443 } 444 return foundEntries.toArray(new FeatureEntry[foundEntries.size()]); 445 446 } 447 448 // Return a collection of File, the result can be null findFiles(File from, String foldername, final String filename)449 public static Collection<File> findFiles(File from, String foldername, final String filename) { 450 // if from is a file which name match filename, then simply return the 451 // file 452 File root = from; 453 if (root.isFile() && root.getName().equals(filename)) { 454 Collection<File> coll = new ArrayList<>(1); 455 coll.add(root); 456 return coll; 457 } 458 459 Collection<File> collectedElements = new ArrayList<>(10); 460 461 File[] featureDirectoryContent = new File(from, foldername).listFiles(); 462 if (featureDirectoryContent == null) 463 return null; 464 465 for (int i = 0; i < featureDirectoryContent.length; i++) { 466 if (featureDirectoryContent[i].isDirectory()) { 467 File[] featureFiles = featureDirectoryContent[i].listFiles(new FilenameFilter() { 468 @Override 469 public boolean accept(File dir, String name) { 470 return name.equals(filename); 471 } 472 }); 473 if (featureFiles.length != 0) 474 collectedElements.add(featureFiles[0]); 475 } 476 } 477 return collectedElements; 478 } 479 isIn(FeatureEntry[] array, FeatureEntry element)480 public static boolean isIn(FeatureEntry[] array, FeatureEntry element) { 481 for (int i = 0; i < array.length; i++) { 482 if (array[i].getId().equals(element.getId()) && array[i].getVersion().equals(element.getVersion())) 483 return true; 484 } 485 return false; 486 } 487 copyFiles(String fromDir, String toDir)488 public static Collection<String> copyFiles(String fromDir, String toDir) throws CoreException { 489 File templateLocation = new File(fromDir); 490 Collection<String> copiedFiles = new ArrayList<>(); 491 if (templateLocation.exists()) { 492 File[] files = templateLocation.listFiles(); 493 if (files != null) { 494 for (int i = 0; i < files.length; i++) { 495 if (files[i].isDirectory()) { 496 File subDir = new File(toDir, files[i].getName()); 497 if (!subDir.exists()) 498 subDir.mkdirs(); 499 Collection<String> subFiles = copyFiles(fromDir + '/' + files[i].getName(), toDir + '/' + files[i].getName()); 500 for (Iterator<String> iter = subFiles.iterator(); iter.hasNext();) { 501 String sub = iter.next(); 502 copiedFiles.add(files[i].getName() + '/' + sub); 503 } 504 continue; 505 } 506 507 FileInputStream inputStream = null; 508 FileOutputStream outputStream = null; 509 510 try { 511 inputStream = new FileInputStream(files[i]); 512 } catch (FileNotFoundException e) { 513 String message = NLS.bind(Messages.exception_missingFile, files[i].getAbsolutePath()); 514 throw new CoreException(new Status(IStatus.ERROR, PI_PDEBUILD, EXCEPTION_READING_FILE, message, e)); 515 } 516 517 String fileToCopy = toDir + '/' + files[i].getName(); 518 try { 519 outputStream = new FileOutputStream(fileToCopy); 520 } catch (FileNotFoundException e) { 521 try { 522 inputStream.close(); 523 } catch (IOException e1) { 524 // Ignored 525 } 526 String message = NLS.bind(Messages.exception_missingFile, fileToCopy); 527 throw new CoreException(new Status(IStatus.ERROR, PI_PDEBUILD, EXCEPTION_READING_FILE, message, e)); 528 } 529 530 try { 531 Utils.transferStreams(inputStream, outputStream); 532 copiedFiles.add(files[i].getName()); 533 } catch (IOException e) { 534 String message = NLS.bind(Messages.exception_writingFile, fileToCopy); 535 throw new CoreException(new Status(IStatus.ERROR, PI_PDEBUILD, EXCEPTION_WRITING_FILE, message, e)); 536 } 537 } 538 } 539 } 540 return copiedFiles; 541 } 542 extractPlugins(List<BundleDescription> initialList, Set<BundleDescription> toExtract)543 public static List<BundleDescription> extractPlugins(List<BundleDescription> initialList, Set<BundleDescription> toExtract) { 544 //TODO This algorithm needs to be improved 545 if (initialList.size() == toExtract.size()) 546 return initialList; 547 List<BundleDescription> result = new ArrayList<>(toExtract.size()); 548 for (Iterator<BundleDescription> iter = initialList.iterator(); iter.hasNext();) { 549 BundleDescription element = iter.next(); 550 if (toExtract.contains(element)) { 551 result.add(element); 552 if (result.size() == toExtract.size()) 553 break; 554 } 555 } 556 return result; 557 } 558 isStringIn(String[] searched, String toSearch)559 public static int isStringIn(String[] searched, String toSearch) { 560 if (searched == null || toSearch == null) 561 return -1; 562 for (int i = 0; i < searched.length; i++) { 563 if (toSearch.startsWith(searched[i])) 564 return i; 565 } 566 return -1; 567 } 568 getOldExecutableRootOverrides()569 static public Properties getOldExecutableRootOverrides() { 570 Properties overrides = new Properties(); 571 overrides.put("root.win32.win32.x86_64", "file:bin/win32/win32/x86_64/launcher.exe"); //$NON-NLS-1$ //$NON-NLS-2$ 572 return overrides; 573 } 574 575 /** 576 * Process root file properties. 577 * Resulting map is from config string to a property map. The format of the property map is: 578 * 1) folder -> fileset to copy. folder can be "" (the root) or an actual folder 579 * 2) ROOT_PERMISSIONS + rights -> fileset to set rights for 580 * 3) ROOT_LINK -> comma separated list: (target, link)* 581 * 582 * Properties that are common across all configs are available under the ROOT_COMMON key. 583 * They are also optionally merged into each individual config. 584 585 * @param properties - build.properties for a feature 586 * @param mergeCommon - whether or not to merge the common properties into each config 587 * @return Map 588 */ processRootProperties(Properties properties, boolean mergeCommon)589 static public Map<String, Map<String, String>> processRootProperties(Properties properties, boolean mergeCommon) { 590 Map<String, Map<String, String>> map = new HashMap<>(); 591 Map<String, String> common = new HashMap<>(); 592 for (Enumeration<Object> keys = properties.keys(); keys.hasMoreElements();) { 593 String entry = (String) keys.nextElement(); 594 String config = null; 595 String entryKey = null; 596 597 if (entry.equals(ROOT) || entry.matches(REGEX_ROOT_CONFIG)) { 598 config = entry.length() > 4 ? entry.substring(5) : ""; //$NON-NLS-1$ 599 entryKey = ""; //$NON-NLS-1$ 600 } else if (entry.matches(REGEX_ROOT_CONFIG_FOLDER)) { 601 int folderIdx = entry.indexOf(FOLDER_INFIX); 602 config = (folderIdx > 5) ? entry.substring(5, folderIdx) : ""; //$NON-NLS-1$ 603 entryKey = entry.substring(folderIdx + 8); 604 } else if (entry.matches(REGEX_ROOT_CONFIG_PERMISSIONS)) { 605 int permissionIdx = entry.indexOf(PERMISSIONS_INFIX); 606 config = (permissionIdx > 5) ? entry.substring(5, permissionIdx) : ""; //$NON-NLS-1$ 607 entryKey = ROOT_PERMISSIONS + entry.substring(permissionIdx + 13); 608 } else if (entry.matches(REGEX_ROOT_CONFIG_LINK)) { 609 int linkIdx = entry.indexOf(LINK_SUFFIX); 610 config = (linkIdx > 5) ? entry.substring(5, linkIdx) : ""; //$NON-NLS-1$ 611 entryKey = ROOT_LINK; 612 } 613 614 if (config != null) { 615 Map<String, String> submap = (config.length() == 0) ? common : map.get(config); 616 if (submap == null) { 617 submap = new HashMap<>(); 618 map.put(config, submap); 619 } 620 if (submap.containsKey(entryKey)) { 621 String existing = submap.get(entryKey); 622 submap.put(entryKey, existing + "," + properties.getProperty(entry)); //$NON-NLS-1$ 623 } else { 624 submap.put(entryKey, (String) properties.get(entry)); 625 } 626 } 627 } 628 629 //merge the common properties into each of the configs 630 if (common.size() > 0 && mergeCommon) { 631 for (Iterator<String> iterator = map.keySet().iterator(); iterator.hasNext();) { 632 String key = iterator.next(); 633 Map<String, String> submap = map.get(key); 634 for (Iterator<String> commonKeys = common.keySet().iterator(); commonKeys.hasNext();) { 635 String commonKey = commonKeys.next(); 636 if (submap.containsKey(commonKey)) { 637 String existing = submap.get(commonKey); 638 submap.put(commonKey, existing + "," + common.get(commonKey)); //$NON-NLS-1$ 639 } else { 640 submap.put(commonKey, common.get(commonKey)); 641 } 642 } 643 } 644 } 645 646 //and also add the common properties independently 647 if (mergeCommon || common.size() > 0) 648 map.put(ROOT_COMMON, common); 649 return map; 650 } 651 generatePermissions(Properties featureProperties, Config aConfig, String targetRootProperty, AntScript script)652 public static void generatePermissions(Properties featureProperties, Config aConfig, String targetRootProperty, AntScript script) { 653 if (featureProperties == null) 654 return; 655 String configInfix = aConfig.toString("."); //$NON-NLS-1$ 656 String configPath = aConfig.toStringReplacingAny(".", ANY_STRING); //$NON-NLS-1$ 657 String prefixPermissions = ROOT_PREFIX + configInfix + '.' + PERMISSIONS + '.'; 658 String prefixLinks = ROOT_PREFIX + configInfix + '.' + LINK; 659 String commonPermissions = ROOT_PREFIX + PERMISSIONS + '.'; 660 String commonLinks = ROOT_PREFIX + LINK; 661 for (Iterator<Entry<Object, Object>> iter = featureProperties.entrySet().iterator(); iter.hasNext();) { 662 Map.Entry<Object, Object> permission = iter.next(); 663 String instruction = (String) permission.getKey(); 664 String parameters = removeEndingSlashes((String) permission.getValue()); 665 if (instruction.startsWith(prefixPermissions)) { 666 generateChmodInstruction(script, getPropertyFormat(targetRootProperty) + '/' + configPath + '/' + getPropertyFormat(PROPERTY_COLLECTING_FOLDER), instruction.substring(prefixPermissions.length()), parameters); 667 continue; 668 } 669 if (instruction.startsWith(prefixLinks)) { 670 generateLinkInstruction(script, getPropertyFormat(targetRootProperty) + '/' + configPath + '/' + getPropertyFormat(PROPERTY_COLLECTING_FOLDER), parameters); 671 continue; 672 } 673 if (instruction.startsWith(commonPermissions)) { 674 generateChmodInstruction(script, getPropertyFormat(targetRootProperty) + '/' + configPath + '/' + getPropertyFormat(PROPERTY_COLLECTING_FOLDER), instruction.substring(commonPermissions.length()), parameters); 675 continue; 676 } 677 if (instruction.startsWith(commonLinks)) { 678 generateLinkInstruction(script, getPropertyFormat(targetRootProperty) + '/' + configPath + '/' + getPropertyFormat(PROPERTY_COLLECTING_FOLDER), parameters); 679 continue; 680 } 681 } 682 } 683 removeEndingSlashes(String value)684 public static String removeEndingSlashes(String value) { 685 String[] params = Utils.getArrayFromString(value, ","); //$NON-NLS-1$ 686 for (int i = 0; i < params.length; i++) { 687 if (params[i].endsWith("/")) //$NON-NLS-1$ 688 params[i] = params[i].substring(0, params[i].length() - 1); 689 } 690 return Utils.getStringFromArray(params, ","); //$NON-NLS-1$ 691 } 692 generateChmodInstruction(AntScript script, String dir, String rights, String files)693 private static void generateChmodInstruction(AntScript script, String dir, String rights, String files) { 694 if (rights.equals(EXECUTABLE)) { 695 rights = "755"; //$NON-NLS-1$ 696 } 697 script.printChmod(dir, rights, files); 698 } 699 generateLinkInstruction(AntScript script, String dir, String files)700 private static void generateLinkInstruction(AntScript script, String dir, String files) { 701 String[] links = Utils.getArrayFromString(files, ","); //$NON-NLS-1$ 702 List<String> arguments = new ArrayList<>(2); 703 for (int i = 0; i < links.length; i += 2) { 704 arguments.add("-sf"); //$NON-NLS-1$ 705 arguments.add(links[i]); 706 arguments.add(links[i + 1]); 707 script.printExecTask("ln", dir, arguments, "Linux,FreeBSD"); //$NON-NLS-1$ //$NON-NLS-2$ 708 arguments.clear(); 709 } 710 } 711 712 /** 713 * Return a string with the given property name in the format: 714 * <pre>${propertyName}</pre>. 715 * 716 * @param propertyName the name of the property 717 * @return String 718 */ getPropertyFormat(String propertyName)719 public static String getPropertyFormat(String propertyName) { 720 StringBuffer sb = new StringBuffer(); 721 sb.append(PROPERTY_ASSIGNMENT_PREFIX); 722 sb.append(propertyName); 723 sb.append(PROPERTY_ASSIGNMENT_SUFFIX); 724 return sb.toString(); 725 } 726 getMacroFormat(String propertyName)727 public static String getMacroFormat(String propertyName) { 728 StringBuffer sb = new StringBuffer(); 729 sb.append(MACRO_ASSIGNMENT_PREFIX); 730 sb.append(propertyName); 731 sb.append(PROPERTY_ASSIGNMENT_SUFFIX); 732 return sb.toString(); 733 } 734 isBinary(BundleDescription bundle)735 public static boolean isBinary(BundleDescription bundle) { 736 Properties bundleProperties = ((Properties) bundle.getUserObject()); 737 if (bundleProperties == null || bundleProperties.get(IS_COMPILED) == null) { 738 File props = new File(bundle.getLocation(), PROPERTIES_FILE); 739 return !(props.exists() && props.isFile()); 740 } 741 return (Boolean.FALSE == bundleProperties.get(IS_COMPILED)); 742 } 743 isSourceBundle(BundleDescription bundle)744 public static boolean isSourceBundle(BundleDescription bundle) { 745 Properties bundleProperties = (Properties) bundle.getUserObject(); 746 return (bundleProperties != null && bundleProperties.containsKey(ECLIPSE_SOURCE_BUNDLE)); 747 } 748 hasBundleShapeHeader(BundleDescription bundle)749 public static boolean hasBundleShapeHeader(BundleDescription bundle) { 750 Properties bundleProperties = (Properties) bundle.getUserObject(); 751 return (bundleProperties != null && bundleProperties.containsKey(ECLIPSE_BUNDLE_SHAPE)); 752 } 753 getSourceBundleHeader(BundleDescription bundle)754 public static String getSourceBundleHeader(BundleDescription bundle) { 755 Properties bundleProperties = (Properties) bundle.getUserObject(); 756 if (bundleProperties == null || !bundleProperties.containsKey(ECLIPSE_SOURCE_BUNDLE)) 757 return ""; //$NON-NLS-1$ 758 759 String header = bundleProperties.getProperty(ECLIPSE_SOURCE_BUNDLE); 760 return header; 761 } 762 763 /** 764 * Given a newly generated old-style source bundle for which there was a previously existing 765 * version in the target, return the location of the src folder in that earlier version 766 * @param bundle 767 * @return the old version's src folder, or null 768 */ getOldSourceLocation(BundleDescription bundle)769 public static File getOldSourceLocation(BundleDescription bundle) { 770 Properties props = (Properties) bundle.getUserObject(); 771 if (props == null || !props.containsKey(OLD_BUNDLE_LOCATION)) 772 return null; 773 774 String oldBundleLocation = props.getProperty(OLD_BUNDLE_LOCATION); 775 if (oldBundleLocation != null) { 776 File previousSrcRoot = new File(oldBundleLocation, "src"); //$NON-NLS-1$ 777 if (previousSrcRoot.exists()) 778 return previousSrcRoot; 779 } 780 781 return null; 782 } 783 parseSourceBundleEntry(BundleDescription bundle)784 public static Map<String, Map<String, String>> parseSourceBundleEntry(BundleDescription bundle) { 785 String header = getSourceBundleHeader(bundle); 786 if (header.length() == 0) 787 return Collections.emptyMap(); 788 789 HashMap<String, Map<String, String>> map = new HashMap<>(); 790 ManifestElement[] elements; 791 try { 792 elements = ManifestElement.parseHeader(ECLIPSE_SOURCE_BUNDLE, header); 793 } catch (BundleException e1) { 794 return Collections.emptyMap(); 795 } 796 for (int i = 0; i < elements.length; i++) { 797 String key = elements[i].getValue(); 798 HashMap<String, String> subMap = new HashMap<>(2); 799 map.put(key, subMap); 800 for (Enumeration<String> e = elements[i].getDirectiveKeys(); e != null && e.hasMoreElements();) { 801 String directive = e.nextElement(); 802 subMap.put(directive, elements[i].getDirective(directive)); 803 } 804 for (Enumeration<String> e = elements[i].getKeys(); e != null && e.hasMoreElements();) { 805 String attribute = e.nextElement(); 806 subMap.put(attribute, elements[i].getAttribute(attribute)); 807 } 808 } 809 return map; 810 } 811 812 public static final String EXTRA_ID = "id"; //$NON-NLS-1$ 813 public static final String EXTRA_VERSION = "version"; //$NON-NLS-1$ 814 public static final String EXTRA_UNPACK = "unpack"; //$NON-NLS-1$ 815 public static final String EXTRA_OPTIONAL = "optional"; //$NON-NLS-1$ 816 public static final String EXTRA_OS = "os"; //$NON-NLS-1$ 817 public static final String EXTRA_WS = "ws"; //$NON-NLS-1$ 818 public static final String EXTRA_ARCH = "arch"; //$NON-NLS-1$ 819 parseExtraBundlesString(String input, boolean onlyId)820 public static Map<String, Object> parseExtraBundlesString(String input, boolean onlyId) { 821 Map<String, Object> results = new HashMap<>(); 822 StringTokenizer tokenizer = null; 823 if (onlyId) 824 if (input.startsWith("plugin@")) //$NON-NLS-1$ 825 tokenizer = new StringTokenizer(input.substring(7), ";"); //$NON-NLS-1$ 826 else if (input.startsWith("exclude@") || input.startsWith("feature@")) //$NON-NLS-1$ //$NON-NLS-2$ 827 tokenizer = new StringTokenizer(input.substring(8), ";"); //$NON-NLS-1$ 828 else 829 tokenizer = new StringTokenizer(input, ";"); //$NON-NLS-1$ 830 else 831 tokenizer = new StringTokenizer(input, ";"); //$NON-NLS-1$ 832 833 results.put(EXTRA_ID, tokenizer.nextToken()); 834 results.put(EXTRA_VERSION, Version.emptyVersion); 835 results.put(EXTRA_UNPACK, Boolean.TRUE); 836 837 while (tokenizer.hasMoreTokens()) { 838 String token = tokenizer.nextToken(); 839 String value = null; 840 int idx = token.indexOf('='); 841 if (idx > 0 && idx < token.length() - 1) { 842 value = token.substring(idx + 1).trim(); 843 if (value.charAt(0) == '"' && value.charAt(value.length() - 1) == '"') 844 value = value.substring(1, value.length() - 1); 845 } 846 if (token.startsWith(EXTRA_VERSION)) 847 results.put(EXTRA_VERSION, new Version(value)); 848 else if (token.startsWith(EXTRA_UNPACK)) 849 results.put(EXTRA_UNPACK, Boolean.valueOf(value)); 850 else if (token.startsWith(EXTRA_OS)) 851 results.put(EXTRA_OS, value); 852 else if (token.startsWith(EXTRA_WS)) 853 results.put(EXTRA_WS, value); 854 else if (token.startsWith(EXTRA_ARCH)) 855 results.put(EXTRA_ARCH, value); 856 else if (token.startsWith(EXTRA_OPTIONAL)) 857 results.put(EXTRA_OPTIONAL, Boolean.valueOf(value)); 858 } 859 return results; 860 } 861 matchVersions(String version1, String version2)862 static public boolean matchVersions(String version1, String version2) { 863 if (version1 == null) 864 version1 = GENERIC_VERSION_NUMBER; 865 if (version2 == null) 866 version2 = GENERIC_VERSION_NUMBER; 867 868 if (version1.equals(version2) || version1.equals(GENERIC_VERSION_NUMBER) || version2.equals(GENERIC_VERSION_NUMBER)) 869 return true; 870 871 if (version1.endsWith(PROPERTY_QUALIFIER) || version2.endsWith(PROPERTY_QUALIFIER)) { 872 int idx = version1.indexOf(PROPERTY_QUALIFIER); 873 if (idx > -1) 874 version1 = version1.substring(0, idx); 875 idx = version2.indexOf(PROPERTY_QUALIFIER); 876 877 version1 = version1.substring(0, idx); 878 return (version1.length() > version2.length()) ? version1.startsWith(version2) : version2.startsWith(version1); 879 } 880 881 return false; 882 } 883 884 /** 885 * 886 * @param buf 887 * @param start 888 * @param target 889 * @return int 890 */ scan(StringBuffer buf, int start, String target)891 static public int scan(StringBuffer buf, int start, String target) { 892 return scan(buf, start, new String[] {target}); 893 } 894 895 /** 896 * 897 * @param buf 898 * @param start 899 * @param targets 900 * @return int 901 */ scan(StringBuffer buf, int start, String[] targets)902 static public int scan(StringBuffer buf, int start, String[] targets) { 903 for (int i = start; i < buf.length(); i++) { 904 for (int j = 0; j < targets.length; j++) { 905 if (i < buf.length() - targets[j].length()) { 906 String match = buf.substring(i, i + targets[j].length()); 907 if (targets[j].equals(match)) 908 return i; 909 } 910 } 911 } 912 return -1; 913 } 914 915 /** 916 * Return a buffer containing the contents of the file at the specified location. 917 * 918 * @param target the file 919 * @return StringBuffer 920 * @throws IOException 921 */ readFile(File target)922 static public StringBuffer readFile(File target) throws IOException { 923 return readFile(new FileInputStream(target)); 924 } 925 readFile(InputStream stream)926 static public StringBuffer readFile(InputStream stream) throws IOException { 927 StringBuffer result = new StringBuffer(); 928 char[] buf = new char[4096]; 929 int count; 930 try (InputStreamReader reader = new InputStreamReader(new BufferedInputStream(stream))) { 931 count = reader.read(buf, 0, buf.length); 932 while (count != -1) { 933 result.append(buf, 0, count); 934 count = reader.read(buf, 0, buf.length); 935 } 936 } 937 return result; 938 } 939 940 /** 941 * Custom build scripts should have their version number matching the 942 * version number defined by the feature/plugin/fragment descriptor. 943 * This is a best effort job so do not worry if the expected tags were 944 * not found and just return without modifying the file. 945 * 946 * @param buildFile 947 * @param propertyName 948 * @param version 949 * @throws IOException 950 * 951 */ updateVersion(File buildFile, String propertyName, String version)952 public static void updateVersion(File buildFile, String propertyName, String version) throws IOException { 953 StringBuffer buffer = readFile(buildFile); 954 int pos = scan(buffer, 0, propertyName); 955 if (pos == -1) 956 return; 957 pos = scan(buffer, pos, "value"); //$NON-NLS-1$ 958 if (pos == -1) 959 return; 960 int begin = scan(buffer, pos, "\""); //$NON-NLS-1$ 961 if (begin == -1) 962 return; 963 begin++; 964 int end = scan(buffer, begin, "\""); //$NON-NLS-1$ 965 if (end == -1) 966 return; 967 String currentVersion = buffer.substring(begin, end); 968 String newVersion = version; 969 if (currentVersion.equals(newVersion)) 970 return; 971 buffer.replace(begin, end, newVersion); 972 transferStreams(new ByteArrayInputStream(buffer.toString().getBytes()), new FileOutputStream(buildFile)); 973 } 974 getArrayEnumerator(Object[] array)975 public static Enumeration<Object> getArrayEnumerator(Object[] array) { 976 return new ArrayEnumeration(array); 977 } 978 close(Object obj)979 public static void close(Object obj) { 980 if (obj == null) 981 return; 982 try { 983 if (obj instanceof InputStream) 984 ((InputStream) obj).close(); 985 else if (obj instanceof ZipFile) 986 ((ZipFile) obj).close(); 987 else if (obj instanceof OutputStream) 988 ((OutputStream) obj).close(); 989 } catch (IOException e) { 990 //boo 991 } 992 } 993 guessUnpack(BundleDescription bundle, String[] classpath)994 public static boolean guessUnpack(BundleDescription bundle, String[] classpath) { 995 return org.eclipse.pde.internal.publishing.Utils.guessUnpack(bundle, classpath); 996 } 997 extract3Segments(String s)998 public static Version extract3Segments(String s) { 999 Version tmp = new Version(s); 1000 return new Version(tmp.getMajor(), tmp.getMinor(), tmp.getMicro()); 1001 } 1002 needsReplacement(String s)1003 private static boolean needsReplacement(String s) { 1004 if (s.equalsIgnoreCase(GENERIC_VERSION_NUMBER) || s.endsWith(PROPERTY_QUALIFIER)) 1005 return true; 1006 return false; 1007 } 1008 getEntryVersionMappings(FeatureEntry[] entries, BuildTimeSite site)1009 public static String getEntryVersionMappings(FeatureEntry[] entries, BuildTimeSite site) { 1010 return getEntryVersionMappings(entries, site, null); 1011 } 1012 getEntryVersionMappings(FeatureEntry[] entries, BuildTimeSite site, AssemblyInformation assembly)1013 public static String getEntryVersionMappings(FeatureEntry[] entries, BuildTimeSite site, AssemblyInformation assembly) { 1014 if (entries == null || site == null) 1015 return null; 1016 1017 StringBuffer result = new StringBuffer(); 1018 for (int i = 0; i < entries.length; i++) { 1019 String versionRequested = entries[i].getVersion(); 1020 if (versionRequested == null) 1021 versionRequested = GENERIC_VERSION_NUMBER; 1022 String id = entries[i].getId(); 1023 String newVersion = null; 1024 1025 if (!needsReplacement(versionRequested)) 1026 continue; 1027 1028 try { 1029 if (entries[i].isPlugin()) { 1030 BundleDescription model = null; 1031 if (assembly != null) 1032 model = assembly.getPlugin(entries[i].getId(), versionRequested); 1033 if (model == null) 1034 model = site.getRegistry().getResolvedBundle(id, versionRequested); 1035 if (model != null) 1036 newVersion = model.getVersion().toString(); 1037 } else { 1038 BuildTimeFeature feature = site.findFeature(id, versionRequested, false); 1039 if (feature != null) 1040 newVersion = feature.getVersion(); 1041 } 1042 } catch (CoreException e) { 1043 continue; 1044 } 1045 if (newVersion != null) { 1046 result.append(id); 1047 result.append(':'); 1048 result.append(extract3Segments(versionRequested)); 1049 result.append(','); 1050 result.append(newVersion); 1051 result.append(','); 1052 } 1053 } 1054 return result.toString(); 1055 } 1056 } 1057