blob: 811e5d90b1a781ac1113008b50d2185112ab1c99 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2018 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.pde.api.tools.generator.util;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileFilter;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.net.JarURLConnection;
import java.net.MalformedURLException;
import java.net.ProtocolException;
import java.net.SocketException;
import java.net.SocketTimeoutException;
import java.net.URL;
import java.net.URLConnection;
import java.net.UnknownHostException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.zip.CRC32;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.Path;
import org.eclipse.jdt.core.compiler.CharOperation;
public class Util {
private static final int DEFAULT_READING_SIZE = 8192;
private final static String UTF_8 = "UTF-8"; //$NON-NLS-1$
private static final byte[] CHARSET = new byte[] {
99, 104, 97, 114, 115, 101, 116, 61 };
private static final byte[] CLOSING_DOUBLE_QUOTE = new byte[] { 34 };
private static final byte[] CONTENT = new byte[] {
99, 111, 110, 116, 101, 110, 116, 61, 34 };
private static final byte[] CONTENT_TYPE = new byte[] {
34, 67, 111, 110, 116, 101, 110, 116, 45, 84, 121, 112, 101, 34 };
private static final CRC32 CRC32 = new CRC32();
public static final String LINE_SEPARATOR = System.getProperty("line.separator"); //$NON-NLS-1$
public static boolean contains(char[] name, char[][] names) {
for (char[] currentName : names) {
if (CharOperation.equals(currentName, name)) {
return true;
}
}
return false;
}
private static int getIndexOf(byte[] array, byte[] toBeFound, int start) {
if (array == null || toBeFound == null) {
return -1;
}
final int toBeFoundLength = toBeFound.length;
final int arrayLength = array.length;
if (arrayLength < toBeFoundLength) {
return -1;
}
loop: for (int i = start, max = arrayLength - toBeFoundLength + 1; i < max; i++) {
if (array[i] == toBeFound[0]) {
for (int j = 1; j < toBeFoundLength; j++) {
if (array[i + j] != toBeFound[j]) {
continue loop;
}
}
return i;
}
}
return -1;
}
/**
* Returns the given input stream's contents as a byte array. If a length is
* specified (ie. if length != -1), only length bytes are returned.
* Otherwise all bytes in the stream are returned. Note this doesn't close
* the stream.
*
* @throws IOException if a problem occured reading the stream.
*/
public static byte[] getInputStreamAsByteArray(InputStream stream, int length) throws IOException {
byte[] contents;
if (length == -1) {
contents = new byte[0];
int contentsLength = 0;
int amountRead = -1;
do {
int amountRequested = Math.max(stream.available(), DEFAULT_READING_SIZE); // read
// at
// least
// 8K
// resize contents if needed
if (contentsLength + amountRequested > contents.length) {
System.arraycopy(contents, 0, contents = new byte[contentsLength + amountRequested], 0, contentsLength);
}
// read as many bytes as possible
amountRead = stream.read(contents, contentsLength, amountRequested);
if (amountRead > 0) {
// remember length of contents
contentsLength += amountRead;
}
} while (amountRead != -1);
// resize contents if necessary
if (contentsLength < contents.length) {
System.arraycopy(contents, 0, contents = new byte[contentsLength], 0, contentsLength);
}
} else {
contents = new byte[length];
int len = 0;
int readSize = 0;
while ((readSize != -1) && (len != length)) {
// See PR 1FMS89U
// We record first the read size. In this case len is the actual
// read size.
len += readSize;
readSize = stream.read(contents, len, length - len);
}
}
return contents;
}
/**
* Returns the given input stream's contents as a character array. If a
* length is specified (i.e. if length != -1), this represents the number of
* bytes in the stream. Note this doesn't close the stream.
*
* @throws IOException if a problem occured reading the stream.
*/
public static char[] getInputStreamAsCharArray(InputStream stream, int length, String encoding) throws IOException {
BufferedReader reader = null;
try {
reader = encoding == null ? new BufferedReader(new InputStreamReader(stream)) : new BufferedReader(new InputStreamReader(stream, encoding));
} catch (UnsupportedEncodingException e) {
// encoding is not supported
reader = new BufferedReader(new InputStreamReader(stream));
}
char[] contents;
int totalRead = 0;
if (length == -1) {
contents = CharOperation.NO_CHAR;
} else {
// length is a good guess when the encoding produces less or the
// same amount of characters than the file length
contents = new char[length]; // best guess
}
while (true) {
int amountRequested;
if (totalRead < length) {
// until known length is met, reuse same array sized eagerly
amountRequested = length - totalRead;
} else {
// reading beyond known length
int current = reader.read();
if (current < 0) {
break;
}
amountRequested = Math.max(stream.available(), DEFAULT_READING_SIZE); // read
// at
// least
// 8K
// resize contents if needed
if (totalRead + 1 + amountRequested > contents.length) {
System.arraycopy(contents, 0, contents = new char[totalRead + 1 + amountRequested], 0, totalRead);
}
// add current character
contents[totalRead++] = (char) current; // coming from
// totalRead==length
}
// read as many chars as possible
int amountRead = reader.read(contents, totalRead, amountRequested);
if (amountRead < 0) {
break;
}
totalRead += amountRead;
}
// Do not keep first character for UTF-8 BOM encoding
int start = 0;
if (totalRead > 0 && UTF_8.equals(encoding)) {
if (contents[0] == 0xFEFF) { // if BOM char then skip
totalRead--;
start = 1;
}
}
// resize contents if necessary
if (totalRead < contents.length) {
System.arraycopy(contents, start, contents = new char[totalRead], 0, totalRead);
}
return contents;
}
public static String getInterfaces(char[][] superinterfacesNames) {
StringBuilder buffer = new StringBuilder();
for (int i = 0, max = superinterfacesNames.length; i < max; i++) {
if (i > 0) {
buffer.append(',');
}
buffer.append(superinterfacesNames[i]);
}
return String.valueOf(buffer);
}
public static ZipOutputStream getOutputStream(File file) throws FileNotFoundException {
return new ZipOutputStream(new BufferedOutputStream(new FileOutputStream(file)));
}
public static String getSimpleName(char[] name) {
return new String(getSimpleNameAsCharArray(name));
}
public static char[] getSimpleNameAsCharArray(char[] name) {
int index = CharOperation.lastIndexOf('.', name);
char[] subarray = CharOperation.subarray(name, index + 1, name.length);
subarray = CharOperation.replaceOnCopy(subarray, '$', '.');
return subarray;
}
public static void write(String rootDirName, String subDirName, String fileName, String contents) {
File rootDir = new File(rootDirName);
rootDir.mkdirs();
File subDir = new File(rootDir, subDirName);
subDir.mkdirs();
String fname = fileName;
if (fname.indexOf('/') != -1) {
fname = fname.replace('/', '_');
}
try (BufferedWriter writer = new BufferedWriter(new FileWriter(new File(subDir, fname)))) {
writer.write(contents);
writer.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* Writes the given entry information into the output stream
*
* @param outputStream the output stream to write out to
* @param entryName
* @param bytes
* @param crc32
* @param bits
* @throws IOException
*/
public static void writeZipFileEntry(ZipOutputStream outputStream, String entryName, byte[] bytes) throws IOException {
CRC32.reset();
int byteArraySize = bytes.length;
CRC32.update(bytes, 0, byteArraySize);
ZipEntry entry = new ZipEntry(entryName);
entry.setMethod(ZipEntry.DEFLATED);
entry.setSize(byteArraySize);
entry.setCrc(CRC32.getValue());
outputStream.putNextEntry(entry);
outputStream.write(bytes, 0, byteArraySize);
outputStream.closeEntry();
}
/*
* We don't use getContentEncoding() on the URL connection, because it might
* leave open streams behind. See
* https://bugs.eclipse.org/bugs/show_bug.cgi?id=117890
*/
public static char[] getURLContents(String docUrlValue) {
InputStream stream = null;
JarURLConnection connection2 = null;
try {
URL docUrl = new URL(docUrlValue);
URLConnection connection = docUrl.openConnection();
connection.setConnectTimeout(10000);
connection.setReadTimeout(10000);
if (connection instanceof JarURLConnection) {
connection2 = (JarURLConnection) connection;
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=156307
connection.setUseCaches(false);
}
try {
stream = new BufferedInputStream(connection.getInputStream());
} catch (IllegalArgumentException e) {
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=304316
return null;
} catch (NullPointerException e) {
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=304316
return null;
}
String encoding = connection.getContentEncoding();
byte[] contents = Util.getInputStreamAsByteArray(stream, connection.getContentLength());
if (encoding == null) {
int index = getIndexOf(contents, CONTENT_TYPE, 0);
if (index != -1) {
index = getIndexOf(contents, CONTENT, index);
if (index != -1) {
int offset = index + CONTENT.length;
int index2 = getIndexOf(contents, CLOSING_DOUBLE_QUOTE, offset);
if (index2 != -1) {
final int charsetIndex = getIndexOf(contents, CHARSET, offset);
if (charsetIndex != -1) {
int start = charsetIndex + CHARSET.length;
encoding = new String(contents, start, index2 - start, StandardCharsets.UTF_8);
}
}
}
}
}
if (contents != null) {
if (encoding != null) {
return new String(contents, encoding).toCharArray();
} else {
// platform encoding is used
return new String(contents).toCharArray();
}
}
} catch (SocketTimeoutException e) {
e.printStackTrace();
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (FileNotFoundException e) {
// ignore. see bug
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=120559
} catch (SocketException e) {
// ignore. see bug
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=247845
} catch (UnknownHostException e) {
// ignore. see bug
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=247845
} catch (ProtocolException e) {
// ignore. see bug
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=247845
} catch (IOException e) {
e.printStackTrace();
} finally {
if (stream != null) {
try {
stream.close();
} catch (IOException e) {
// ignore
}
}
if (connection2 != null) {
try {
connection2.getJarFile().close();
} catch (IOException e) {
// ignore
} catch (IllegalStateException e) {
/*
* ignore. Can happen in case the stream.close() did close
* the jar file see
* https://bugs.eclipse.org/bugs/show_bug.cgi?id=140750
*/
}
}
}
return null;
}
private static void collectAllFiles(File root, ArrayList<File> collector, FileFilter fileFilter) {
File[] files = root.listFiles(fileFilter);
for (final File currentFile : files) {
if (currentFile.isDirectory()) {
collectAllFiles(currentFile, collector, fileFilter);
} else {
collector.add(currentFile);
}
}
}
public static File[] getAllFiles(File root, FileFilter fileFilter) {
ArrayList<File> files = new ArrayList<>();
if (root.isDirectory()) {
collectAllFiles(root, files, fileFilter);
// get the jmods
// assumption all the jmods are in jmod folder
// jmods are present post the jars
String path = root.toString();
IPath newPath = new Path(path);
newPath = newPath.removeLastSegments(1).addTrailingSeparator();
newPath = newPath.append("jmods"); //$NON-NLS-1$
File jmod = newPath.toFile();
if (jmod.exists()) {
File[] listFiles = jmod.listFiles(fileFilter);
for (File file : listFiles) {
files.add(file);
}
}
File[] result = new File[files.size()];
files.toArray(result);
return result;
}
return null;
}
public static String getProfileFileName(String profileName) {
return profileName.replace('/', '_');
}
}