/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2017 Serge Rider (serge@jkiss.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jkiss.dbeaver.utils;
import org.eclipse.core.runtime.*;
import org.jkiss.code.NotNull;
import org.jkiss.dbeaver.DBException;
import org.jkiss.dbeaver.Log;
import org.jkiss.dbeaver.ModelPreferences;
import org.jkiss.dbeaver.bundle.ModelActivator;
import org.jkiss.utils.Base64;
import org.jkiss.utils.CommonUtils;
import org.jkiss.utils.StandardConstants;
import org.osgi.framework.Bundle;
import org.osgi.framework.Version;
import java.io.*;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.charset.Charset;
import java.text.NumberFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* General non-ui utility methods
*/
public class GeneralUtils {
private static final Log log = Log.getLog(GeneralUtils.class);
public static final String UTF8_ENCODING = "UTF-8";
public static final String DEFAULT_ENCODING = UTF8_ENCODING;
public static final Charset UTF8_CHARSET = Charset.forName(UTF8_ENCODING);
public static final Charset DEFAULT_FILE_CHARSET = UTF8_CHARSET;
public static final Charset ASCII_CHARSET = Charset.forName("US-ASCII");
private static final String METADATA_FOLDER = ".metadata";
public static final String DEFAULT_TIMESTAMP_PATTERN = "yyyyMMddHHmm";
public static final String DEFAULT_DATE_PATTERN = "yyyyMMdd";
public static final String[] byteToHex = new String[256];
public static final char[] nibbleToHex = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
static final Map<String, byte[]> BOM_MAP = new HashMap<>();
static final char[] HEX_CHAR_TABLE = {
'0', '1', '2', '3',
'4', '5', '6', '7',
'8', '9', 'a', 'b',
'c', 'd', 'e', 'f'
};
static {
// Compose byte to hex map
for (int i = 0; i < 256; ++i) {
byteToHex[i] = Character.toString(nibbleToHex[i >>> 4]) + nibbleToHex[i & 0x0f];
}
}
public static Pattern VAR_PATTERN = Pattern.compile("(\\$\\{([\\w\\.\\-]+)\\})", Pattern.CASE_INSENSITIVE);
/**
* Default encoding (UTF-8)
*/
public static String getDefaultFileEncoding()
{
return UTF8_ENCODING;
//return System.getProperty("file.encoding", DEFAULT_FILE_CHARSET_NAME);
}
public static String getDefaultLocalFileEncoding()
{
return System.getProperty(StandardConstants.ENV_FILE_ENCODING, getDefaultFileEncoding());
}
public static String getDefaultConsoleEncoding()
{
String consoleEncoding = System.getProperty(StandardConstants.ENV_CONSOLE_ENCODING);
if (CommonUtils.isEmpty(consoleEncoding)) {
consoleEncoding = System.getProperty(StandardConstants.ENV_FILE_ENCODING);
}
if (CommonUtils.isEmpty(consoleEncoding)) {
consoleEncoding = getDefaultFileEncoding();
}
return consoleEncoding;
}
public static String getDefaultLineSeparator()
{
return System.getProperty(StandardConstants.ENV_LINE_SEPARATOR, "\n");
}
public static byte[] getCharsetBOM(String charsetName)
{
return BOM_MAP.get(charsetName.toUpperCase());
}
public static void writeByteAsHex(Writer out, byte b) throws IOException
{
int v = b & 0xFF;
out.write(HEX_CHAR_TABLE[v >>> 4]);
out.write(HEX_CHAR_TABLE[v & 0xF]);
}
public static void writeBytesAsHex(Writer out, byte[] buf, int off, int len) throws IOException
{
for (int i = 0; i < len; i++) {
byte b = buf[off + i];
int v = b & 0xFF;
out.write(HEX_CHAR_TABLE[v >>> 4]);
out.write(HEX_CHAR_TABLE[v & 0xF]);
}
}
public static String convertToString(byte[] bytes, int offset, int length)
{
char[] chars = new char[length];
for (int i = offset; i < offset + length; i++) {
int b = bytes[i];
if (b < 0) b = -b + 127;
if (b < 32) b = 32;
chars[i - offset] = (char) b;
}
return new String(chars);
}
/**
* Converts string to byte array.
* This is loosy algorithm because it gets only first byte from each char.
*
* @param strValue
* @return
*/
public static byte[] convertToBytes(String strValue)
{
int length = strValue.length();
byte[] bytes = new byte[length];
for (int i = 0; i < length; i++) {
int c = strValue.charAt(i) & 255;
if (c > 127) {
c = -(c - 127);
}
bytes[i] = (byte)c;
}
return bytes;
}
public static Object makeDisplayString(Object object)
{
if (object == null) {
return ""; //$NON-NLS-1$
}
if (object instanceof Number) {
return NumberFormat.getInstance().format(object);
}
Class<?> eClass = object.getClass();
if (eClass.isArray()) {
if (eClass == byte[].class)
return Arrays.toString((byte[]) object);
else if (eClass == short[].class)
return Arrays.toString((short[]) object);
else if (eClass == int[].class)
return Arrays.toString((int[]) object);
else if (eClass == long[].class)
return Arrays.toString((long[]) object);
else if (eClass == char[].class)
return Arrays.toString((char[]) object);
else if (eClass == float[].class)
return Arrays.toString((float[]) object);
else if (eClass == double[].class)
return Arrays.toString((double[]) object);
else if (eClass == boolean[].class)
return Arrays.toString((boolean[]) object);
else { // element is an array of object references
return Arrays.deepToString((Object[]) object);
}
}
return object;
}
public static Object convertString(String value, Class<?> valueType)
{
try {
if (CommonUtils.isEmpty(value)) {
return null;
}
if (valueType == null || CharSequence.class.isAssignableFrom(valueType)) {
return value;
} else if (valueType == Boolean.class || valueType == Boolean.TYPE) {
return Boolean.valueOf(value);
} else if (valueType == Long.class) {
return Long.valueOf(value);
} else if (valueType == Long.TYPE) {
return Long.parseLong(value);
} else if (valueType == Integer.class) {
return new Integer(value);
} else if (valueType == Integer.TYPE) {
return Integer.parseInt(value);
} else if (valueType == Short.class) {
return Short.valueOf(value);
} else if (valueType == Short.TYPE) {
return Short.parseShort(value);
} else if (valueType == Byte.class) {
return Byte.valueOf(value);
} else if (valueType == Byte.TYPE) {
return Byte.parseByte(value);
} else if (valueType == Double.class) {
return Double.valueOf(value);
} else if (valueType == Double.TYPE) {
return Double.parseDouble(value);
} else if (valueType == Float.class) {
return Float.valueOf(value);
} else if (valueType == Float.TYPE) {
return Float.parseFloat(value);
} else if (valueType == BigInteger.class) {
return new BigInteger(value);
} else if (valueType == BigDecimal.class) {
return new BigDecimal(value);
} else {
return value;
}
} catch (RuntimeException e) {
log.error(e);
return value;
}
}
public static Throwable getRootCause(Throwable ex) {
for (Throwable e = ex; ; e = e.getCause()) {
if (e.getCause() == null) {
return e;
}
}
}
@NotNull
public static IStatus makeInfoStatus(String message) {
return new Status(
IStatus.INFO,
ModelPreferences.PLUGIN_ID,
message,
null);
}
@NotNull
public static String getProductTitle()
{
return getProductName() + " " + getProductVersion();
}
@NotNull
public static String getProductName()
{
final IProduct product = Platform.getProduct();
if (product == null) {
return "DBeaver";
}
return product.getName();
}
@NotNull
public static Version getProductVersion()
{
final IProduct product = Platform.getProduct();
if (product == null) {
return ModelActivator.getInstance().getBundle().getVersion();
}
return product.getDefiningBundle().getVersion();
}
@NotNull
public static Date getProductReleaseDate() {
final IProduct product = Platform.getProduct();
if (product != null) {
Bundle definingBundle = product.getDefiningBundle();
final Dictionary<String, String> headers = definingBundle.getHeaders();
final String releaseDate = headers.get("Bundle-Release-Date");
if (releaseDate != null) {
try {
return new SimpleDateFormat(DEFAULT_DATE_PATTERN).parse(releaseDate);
} catch (ParseException e) {
log.debug(e);
}
}
final String buildTime = headers.get("Build-Time");
if (buildTime != null) {
try {
return new SimpleDateFormat(DEFAULT_TIMESTAMP_PATTERN).parse(buildTime);
} catch (ParseException e) {
log.debug(e);
}
}
}
// Failed to get valid date from product bundle
final Calendar calendar = Calendar.getInstance();
calendar.set(Calendar.YEAR, 2017);
calendar.set(Calendar.MONTH, 0);
calendar.set(Calendar.DAY_OF_MONTH, 1);
return calendar.getTime();
}
public interface IVariableResolver {
String get(String name);
}
public static class MapResolver implements IVariableResolver {
private final Map<String, Object> variables;
public MapResolver(Map<String, Object> variables) {
this.variables = variables;
}
@Override
public String get(String name) {
Object value = variables.get(name);
return value == null ? null : CommonUtils.toString(value);
}
}
@NotNull
public static String variablePattern(String name) {
return "${" + name + "}";
}
@NotNull
public static String replaceVariables(@NotNull String string, IVariableResolver resolver) {
try {
Matcher matcher = VAR_PATTERN.matcher(string);
int pos = 0;
while (matcher.find(pos)) {
pos = matcher.end();
String varName = matcher.group(2);
String varValue = resolver.get(varName);
if (varValue != null) {
if (matcher.start() == 0 && matcher.end() == string.length() - 1) {
string = varValue;
} else {
string = string.substring(0, matcher.start()) + varValue + string.substring(matcher.end());
}
matcher = VAR_PATTERN.matcher(string);
pos = 0;
}
}
return string;
} catch (Exception e) {
log.warn("Error matching regex", e);
return string;
}
}
public static String[] parseCommandLine(String commandLine) {
StringTokenizer st = new StringTokenizer(commandLine);
String[] args = new String[st.countTokens()];
for (int i = 0; st.hasMoreTokens(); i++) {
args[i] = st.nextToken();
}
return args;
}
public static IStatus makeExceptionStatus(Throwable ex)
{
return makeExceptionStatus(IStatus.ERROR, ex);
}
public static IStatus makeExceptionStatus(int severity, Throwable ex)
{
Throwable cause = ex.getCause();
if (cause == null) {
return new Status(
severity,
ModelPreferences.PLUGIN_ID,
getExceptionMessage(ex),
ex);
} else {
if (ex instanceof DBException && CommonUtils.equalObjects(ex.getMessage(), cause.getMessage())) {
// Skip empty duplicate DBException
return makeExceptionStatus(cause);
}
return new MultiStatus(
ModelPreferences.PLUGIN_ID,
0,
new IStatus[]{makeExceptionStatus(severity, cause)},
getExceptionMessage(ex),
ex);
}
}
public static IStatus makeExceptionStatus(String message, Throwable ex)
{
return makeExceptionStatus(IStatus.ERROR, message, ex);
}
public static IStatus makeExceptionStatus(int severity, String message, Throwable ex)
{
return new MultiStatus(
ModelPreferences.PLUGIN_ID,
0,
new IStatus[]{makeExceptionStatus(severity, ex)},
message,
null);
}
public static IStatus getRootStatus(IStatus status) {
IStatus[] children = status.getChildren();
if (children == null || children.length == 0) {
return status;
} else {
return getRootStatus(children[0]);
}
}
public static String getStatusText(IStatus status) {
String text = status.getMessage();
IStatus[] children = status.getChildren();
if (children != null && children.length > 0) {
for (IStatus child : children) {
text += "\n" + getStatusText(child);
}
}
return text;
}
/**
* Returns first non-null and non-empty message from this exception or it's cause
*/
public static String getFirstMessage(Throwable ex)
{
for (Throwable e = ex; e != null; e = e.getCause()) {
String message = e.getMessage();
if (!CommonUtils.isEmpty(message)) {
return message;
}
}
return null;
}
public static String getExceptionMessage(@NotNull Throwable ex)
{
/*
StringBuilder msg = new StringBuilder(*/
/*CommonUtils.getShortClassName(ex.getClass())*//*
);
msg.append(ex.getClass().getSimpleName());
if (ex.getMessage() != null) {
msg.append(": ").append(ex.getMessage());
}
return msg.toString().trim();
*/
return ex.getMessage();
}
@NotNull
public static String serializeObject(@NotNull Object object) {
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try (ObjectOutputStream os = new ObjectOutputStream(baos)) {
os.writeObject(object);
}
return Base64.encode(baos.toByteArray());
} catch (Throwable e) {
log.warn("Error serializing object [" + object + "]", e);
return "";
}
}
public static Object deserializeObject(String text) {
try {
final byte[] bytes = Base64.decode(text);
ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
try (ObjectInputStream is = new ObjectInputStream(bais)) {
return is.readObject();
}
} catch (Throwable e) {
log.warn("Error deserializing object [" + text + "]", e);
return null;
}
}
public static File getMetadataFolder() {
final URL workspaceURL = Platform.getInstanceLocation().getURL();
File metaDir = getMetadataFolder(new File(workspaceURL.getPath()));
if (!metaDir.exists() && !metaDir.mkdir()) {
return Platform.getLogFileLocation().toFile().getParentFile();
}
return metaDir;
}
public static File getMetadataFolder(File workspaceFolder) {
return new File(workspaceFolder, METADATA_FOLDER);
}
@NotNull
public static URI makeURIFromFilePath(@NotNull String path) throws URISyntaxException {
return new URI(path.replace(" ", "%20"));
}
}