diff --git a/.gitignore b/.gitignore index dcb2175..c4c063d 100644 --- a/.gitignore +++ b/.gitignore @@ -34,7 +34,7 @@ local.properties target/ .idea/ -config/ +logs/ *.iml # project specific diff --git a/README.md b/README.md index 121bfbf..b865279 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,28 @@ To get started, begin here: 3. If you have any problems read the [FAQ](https://github.com/NiceSystems/hrider/wiki/FAQ) first. ## News +### 23 June, 2013: Release 1.0.7.2 available +Issues fixed: [#45](https://github.com/NiceSystems/hrider/issues/45), [#46](https://github.com/NiceSystems/hrider/issues/46) + +[Download for hbase 0.94.1 - with dependencies](http://bit.ly/14R3Pdv). + +[Download for hbase 0.94.1 - without dependencies](http://bit.ly/1a3YF1A). +### 18 June, 2013: Release 1.0.7.1 available +Issues fixed: [#41](https://github.com/NiceSystems/hrider/issues/41), [#43](https://github.com/NiceSystems/hrider/issues/43), [#44](https://github.com/NiceSystems/hrider/issues/44) + +[Download for hbase 0.94.1](http://bit.ly/11Iesdr). +### 19 May, 2013: Release 1.0.7.0 available +New features: [#38](https://github.com/NiceSystems/hrider/issues/38) + +Issues fixed: [#36](https://github.com/NiceSystems/hrider/issues/36), [#39](https://github.com/NiceSystems/hrider/issues/39) + +[Download for hbase 0.94.1](http://bit.ly/19PgWvd). +### 21 April, 2013: Release 1.0.6.0 available +New features: [#34](https://github.com/NiceSystems/hrider/issues/34), [#35](https://github.com/NiceSystems/hrider/issues/35) + +Issues fixed: [#31](https://github.com/NiceSystems/hrider/issues/31) + +[Download for hbase 0.94.1](http://bit.ly/15uOEbu). ### 02 April, 2013: Release 1.0.5.0 available New features: [#28](https://github.com/NiceSystems/hrider/issues/28) diff --git a/documentation/images/h-rider.png b/documentation/images/h-rider.png index 7d479f4..cf10e9e 100644 Binary files a/documentation/images/h-rider.png and b/documentation/images/h-rider.png differ diff --git a/documentation/images/typeConverterDialog.png b/documentation/images/typeConverterDialog.png new file mode 100644 index 0000000..f3b9da3 Binary files /dev/null and b/documentation/images/typeConverterDialog.png differ diff --git a/documentation/images/welcome.png b/documentation/images/welcome.png index f484f47..a1ed61c 100644 Binary files a/documentation/images/welcome.png and b/documentation/images/welcome.png differ diff --git a/pom.xml b/pom.xml index 42060e1..3c0c510 100644 --- a/pom.xml +++ b/pom.xml @@ -10,11 +10,11 @@ hbase viewer and editor jar - 1.0.5.0 + 1.0.7.2 - local + localrepository file://${project.basedir}/repo @@ -91,6 +91,12 @@ forms_rt 7.0.3 + + com.sun + tools + compile + 1.6 + @@ -128,8 +134,8 @@ lib/ - ${project.artifactId} ${project.version} + ${hbase.version} diff --git a/repo/com/sun/tools/1.6/_maven.repositories b/repo/com/sun/tools/1.6/_maven.repositories new file mode 100644 index 0000000..02cc1c6 --- /dev/null +++ b/repo/com/sun/tools/1.6/_maven.repositories @@ -0,0 +1,4 @@ +#NOTE: This is an internal implementation file, its format can be changed without prior notice. +#Thu Apr 18 18:52:10 IDT 2013 +tools-1.6.jar>= +tools-1.6.pom>= diff --git a/repo/com/sun/tools/1.6/tools-1.6.jar b/repo/com/sun/tools/1.6/tools-1.6.jar new file mode 100644 index 0000000..1d44f43 Binary files /dev/null and b/repo/com/sun/tools/1.6/tools-1.6.jar differ diff --git a/repo/com/sun/tools/1.6/tools-1.6.pom b/repo/com/sun/tools/1.6/tools-1.6.pom new file mode 100644 index 0000000..f3bb257 --- /dev/null +++ b/repo/com/sun/tools/1.6/tools-1.6.pom @@ -0,0 +1,9 @@ + + + 4.0.0 + com.sun + tools + 1.6 + POM was created from install:install-file + diff --git a/repo/com/sun/tools/maven-metadata-local.xml b/repo/com/sun/tools/maven-metadata-local.xml new file mode 100644 index 0000000..ace1dd4 --- /dev/null +++ b/repo/com/sun/tools/maven-metadata-local.xml @@ -0,0 +1,12 @@ + + + com.sun + tools + + 1.6 + + 1.6 + + 20130418155210 + + diff --git a/src/main/java/hrider/actions/Action.java b/src/main/java/hrider/actions/Action.java new file mode 100644 index 0000000..33465e9 --- /dev/null +++ b/src/main/java/hrider/actions/Action.java @@ -0,0 +1,41 @@ +package hrider.actions; + +/** + * Copyright (C) 2012 NICE Systems ltd. + *

+ * 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. + * + * @author Igor Cher + * @version %I%, %G% + *

+ * This class represents an executable delegate. + */ +public abstract class Action { + + /** + * Executes the action. + * + * @throws Exception Any error. + */ + public abstract R run() throws Exception; + + /** + * The method is called when the error occurred. + * + * @param ex The error. + */ + @SuppressWarnings("NoopMethodInAbstractClass") + public void onError(Exception ex) { + + } +} diff --git a/src/main/java/hrider/actions/RunnableAction.java b/src/main/java/hrider/actions/RunnableAction.java new file mode 100644 index 0000000..f125df2 --- /dev/null +++ b/src/main/java/hrider/actions/RunnableAction.java @@ -0,0 +1,150 @@ +package hrider.actions; + +import hrider.io.Log; +import org.apache.commons.lang.time.StopWatch; + +/** + * Copyright (C) 2012 NICE Systems ltd. + *

+ * 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. + * + * @author Igor Cher + * @version %I%, %G% + *

+ * This class represents a runnable action. + */ +public class RunnableAction implements Runnable { + + //region Constants + private final static Log logger = Log.getLogger(RunnableAction.class); + //endregion + + //region Variables + private String name; + private Action action; + private Thread thread; + private boolean isRunning; + private boolean interrupted; + private R result; + //endregion + + //region Constructor + public RunnableAction(String name, Action action) { + this.name = name; + this.action = action; + } + //endregion + + //region Public Properties + public R getResult() { + return result; + } + + public boolean isCompleted() { + return !isRunning && !interrupted; + } + + public boolean isRunning() { + return isRunning; + } + //endregion + + //region Public Methods + public static RunnableAction run(String name, Action action) { + RunnableAction runnableAction = new RunnableAction(name, action); + runnableAction.start(); + + return runnableAction; + } + + public static R runAndWait(String name, Action action, long timeout) { + RunnableAction runnableAction = new RunnableAction(name, action); + runnableAction.start(); + + runnableAction.waitOrAbort(timeout); + return runnableAction.result; + } + + public void start() { + if (this.thread == null) { + this.thread = new Thread(this); + this.thread.setName(this.name); + this.thread.setDaemon(true); + this.thread.start(); + + this.isRunning = true; + + logger.info("Action '%s' started.", this.name); + } + } + + public void abort() { + if (this.isRunning) { + this.interrupted = true; + + this.thread.interrupt(); + this.thread = null; + } + } + + public void waitUntil(long timeout) { + StopWatch stopWatch = new StopWatch(); + stopWatch.start(); + + long localTimeout = timeout / 10; + + while (this.isRunning) { + try { + Thread.sleep(localTimeout); + + if (stopWatch.getTime() > timeout) { + break; + } + } + catch (InterruptedException ignore) { + } + } + + stopWatch.stop(); + } + + public void waitOrAbort(long timeout) { + waitUntil(timeout); + + if (this.isRunning) { + abort(); + } + } + + @Override + public void run() { + try { + this.result = action.run(); + this.isRunning = false; + + logger.info("Action '%s' completed.", this.name); + } + catch (Exception e) { + this.isRunning = false; + + if (this.interrupted) { + logger.info("Action '%s' aborted.", this.name); + } + else { + logger.info("Action '%s' failed.", this.name); + this.action.onError(e); + } + } + } + //endregion +} diff --git a/src/main/java/hrider/config/ClusterConfig.java b/src/main/java/hrider/config/ClusterConfig.java index 4b9682c..0406d69 100644 --- a/src/main/java/hrider/config/ClusterConfig.java +++ b/src/main/java/hrider/config/ClusterConfig.java @@ -85,14 +85,14 @@ public T getTableConfig(Class clazz, String table) { /** * Gets a configuration data related to the specified table and a corresponding column. * - * @param clazz The class that represents the type of the data to be returned. - * @param table The name of the table. - * @param column The name of the column. - * @param The type of the data to be returned. + * @param clazz The class that represents the type of the data to be returned. + * @param table The name of the table. + * @param key The key to identify the data. + * @param The type of the data to be returned. * @return The data of the specified type. */ - public T getTableConfig(Class clazz, String table, String column) { - return get(clazz, String.format("table.%s.%s", table, column)); + public T getTableConfig(Class clazz, String table, String key) { + return get(clazz, String.format("table.%s.%s", table, key)); } /** @@ -123,12 +123,12 @@ public void setTableConfig(String table, String value) { /** * Sets a configuration data for the specified table and column. * - * @param table The name of the table. - * @param column The name of the column. - * @param value The data to set. + * @param table The name of the table. + * @param key The identifier of the value. + * @param value The data to set. */ - public void setTableConfig(String table, String column, String value) { - set(String.format("table.%s.%s", table, column), value); + public void setTableConfig(String table, String key, String value) { + set(String.format("table.%s.%s", table, key), value); save(); } diff --git a/src/main/java/hrider/config/ConnectionDetails.java b/src/main/java/hrider/config/ConnectionDetails.java index 33d1dbf..b9e7169 100644 --- a/src/main/java/hrider/config/ConnectionDetails.java +++ b/src/main/java/hrider/config/ConnectionDetails.java @@ -1,10 +1,13 @@ package hrider.config; +import hrider.actions.Action; +import hrider.actions.RunnableAction; import hrider.hbase.ConnectionManager; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.hbase.HBaseConfiguration; import java.io.IOException; +import java.io.Serializable; /** * Copyright (C) 2012 NICE Systems ltd. @@ -26,7 +29,11 @@ *

* This class represents a data to be used to connect to the hbase and zookeeper nodes. */ -public class ConnectionDetails { +public class ConnectionDetails implements Serializable { + + //region Constants + private static final long serialVersionUID = -5808921673890223877L; + //endregion //region Variables private ServerDetails zookeeper; @@ -44,13 +51,17 @@ public void setZookeeper(ServerDetails zookeeper) { //region Public Methods public boolean canConnect() { - try { - ConnectionManager.create(this); - return true; - } - catch (IOException ignore) { - return false; - } + Boolean result = RunnableAction.runAndWait( + this.zookeeper.getHost(), new Action() { + + @Override + public Boolean run() throws IOException { + ConnectionManager.create(ConnectionDetails.this); + return true; + } + }, GlobalConfig.instance().getConnectionCheckTimeout()); + + return result != null ? result : false; } public Configuration createConfig() { @@ -59,6 +70,9 @@ public Configuration createConfig() { config.set("hbase.zookeeper.property.clientPort", this.zookeeper.getPort()); config.set("hbase.client.retries.number", "3"); +// config.set("hbase.security.authentication", "kerberos"); +// config.set("hbase.rpc.engine", "org.apache.hadoop.hbase.ipc.SecureRpcEngine"); + return config; } diff --git a/src/main/java/hrider/config/GlobalConfig.java b/src/main/java/hrider/config/GlobalConfig.java index 846d799..0e45c90 100644 --- a/src/main/java/hrider/config/GlobalConfig.java +++ b/src/main/java/hrider/config/GlobalConfig.java @@ -1,5 +1,7 @@ package hrider.config; +import hrider.io.PathHelper; + /** * Copyright (C) 2012 NICE Systems ltd. *

@@ -28,14 +30,20 @@ public class GlobalConfig extends PropertiesConfig { private static final String KEY_EXTERNAL_VIEWER_DELIMETER = "global.externalViewerDelimiter"; private static final String KEY_BATCH_READ_SIZE = "global.batch.readSize"; private static final String KEY_BATCH_WRITE_SIZE = "global.batch.writeSize"; - private static final String KEY_COMPILATION_FOLDER = "global.compilation.folder"; + private static final String KEY_CONNECTION_CHECK_TIMEOUT = "global.connection.check.timeout"; + private static final String KEY_ROW_COUNT_OPERATION_TIMEOUT = "global.operation.timeout.rowCount"; + private static final String KEY_CONVERTERS_CLASSES_FOLDER = "global.converters.classes.folder"; + private static final String KEY_CONVERTERS_CODE_FOLDER = "global.converters.code.folder"; private static final String DEFAULT_DATE_FORMAT = "yyyy-MM-dd HH:mm:ss.SSS ZZ"; private static final String DEFAULT_EXTERNAL_VIEWER_FILE_EXTENSION = ".csv"; private static final String DEFAULT_EXTERNAL_VIEWER_DELIMETER = ","; private static final String DEFAULT_BATCH_READ_SIZE = "1000"; private static final String DEFAULT_BATCH_WRITE_SIZE = "100"; - private static final String DEFAULT_COMPILATION_FOLDER = "dynamic"; + private static final String DEFAULT_CONNECTION_CHECK_TIMEOUT = "5000"; + private static final String DEFAULT_ROW_COUNT_OPERATION_TIMEOUT = "30000"; + private static final String DEFAULT_CONVERTERS_CLASSES_FOLDER = "converters/classes"; + private static final String DEFAULT_CONVERTERS_CODE_FOLDER = "converters/code"; //endregion //region Variables @@ -62,7 +70,7 @@ public static GlobalConfig instance() { } /** - * Gets the date time format to be used to parse/convert date time strings. + * Gets the date time format to be used to parse/converters date time strings. * * @return A {@link String} representing date time format. */ @@ -107,12 +115,54 @@ public int getBatchSizeForWrite() { } /** - * Gets a folder where the compiled files should be saved. + * Gets an amount of time to wait for hbase connection during check. + * + * @return An amount of time to wait. + */ + public long getConnectionCheckTimeout() { + return get(Long.class, KEY_CONNECTION_CHECK_TIMEOUT, DEFAULT_CONNECTION_CHECK_TIMEOUT); + } + + /** + * Gets an amount of time to wait before stopping row count operation. + * + * @return An amount of time to wait. + */ + public long getRowCountTimeout() { + return get(Long.class, KEY_ROW_COUNT_OPERATION_TIMEOUT, DEFAULT_ROW_COUNT_OPERATION_TIMEOUT); + } + + /** + * Gets a folder where the compiled classes of the custom converters should be located. + * + * @return A path to the folder. + */ + public String getConvertersClassesFolder() { + return PathHelper.append(PathHelper.getCurrentFolder(), get(String.class, KEY_CONVERTERS_CLASSES_FOLDER, DEFAULT_CONVERTERS_CLASSES_FOLDER)); + } + + /** + * Gets a folder where the java code of the custom converters should be located. * - * @return A path to a folder. + * @return A path to the folder. */ - public String getCompilationFolder() { - return get(String.class, KEY_COMPILATION_FOLDER, DEFAULT_COMPILATION_FOLDER); + public String getConvertersCodeFolder() { + return PathHelper.append(PathHelper.getCurrentFolder(), get(String.class, KEY_CONVERTERS_CODE_FOLDER, DEFAULT_CONVERTERS_CODE_FOLDER)); + } + //endregion + + //region Protected Methods + @Override + protected void onFileCreated() { + set(KEY_ROW_COUNT_OPERATION_TIMEOUT, DEFAULT_ROW_COUNT_OPERATION_TIMEOUT); + set(KEY_BATCH_READ_SIZE, DEFAULT_BATCH_READ_SIZE); + set(KEY_BATCH_WRITE_SIZE, DEFAULT_BATCH_WRITE_SIZE); + set(KEY_CONNECTION_CHECK_TIMEOUT, DEFAULT_CONNECTION_CHECK_TIMEOUT); + set(KEY_DATE_FORMAT, DEFAULT_DATE_FORMAT); + set(KEY_EXTERNAL_VIEWER_DELIMETER, DEFAULT_EXTERNAL_VIEWER_DELIMETER); + set(KEY_EXTERNAL_VIEWER_FILE_EXTENSION, DEFAULT_EXTERNAL_VIEWER_FILE_EXTENSION); + + save(); } //endregion } diff --git a/src/main/java/hrider/config/PropertiesConfig.java b/src/main/java/hrider/config/PropertiesConfig.java index fd8a9a0..f1c371d 100644 --- a/src/main/java/hrider/config/PropertiesConfig.java +++ b/src/main/java/hrider/config/PropertiesConfig.java @@ -1,5 +1,6 @@ package hrider.config; +import hrider.io.Log; import hrider.reflection.Clazz; import java.io.*; @@ -28,9 +29,12 @@ * The base class for all configuration classes that are based on files represented as properties. * This class also supports dynamic update of the configuration data meaning that a file is tracked for changes. */ +@SuppressWarnings("ResultOfMethodCallIgnored") public abstract class PropertiesConfig { //region Variables + private final static Log logger = Log.getLogger(PropertiesConfig.class); + private Properties properties; private File file; //endregion @@ -53,13 +57,15 @@ protected PropertiesConfig(String name) { folder.mkdirs(); } this.file.createNewFile(); + + onFileCreated(); } loadProperties(this.file); loadSystemProperties(); } catch (IOException e) { - e.printStackTrace(); + logger.error(e, "Failed to load properties from the file '%s'", name); } } //endregion @@ -107,10 +113,11 @@ public T get(Class clazz, String name) { * @param The type of the value. * @return A value of the requested property. */ + @SuppressWarnings("unchecked") public T get(Class clazz, String name, String defaultValue) { String value = this.properties.getProperty(name, defaultValue); if (value != null && !value.isEmpty()) { - return (T)Clazz.primitiveToObject(clazz, value); + return (T)Clazz.fromPrimitive(clazz, value); } return null; } @@ -173,12 +180,18 @@ public void save() { } //endregion + //region Protected Methods + protected void onFileCreated() { + + } + //endregion + //region Private Methods private static File loadFile(String name) { return new File("config/" + name + ".properties"); } - private void loadProperties(File file) throws IOException { + private void loadProperties(File file) throws IOException, FileNotFoundException { if (file != null) { InputStream stream = null; try { diff --git a/src/main/java/hrider/config/ServerDetails.java b/src/main/java/hrider/config/ServerDetails.java index 9ff4bb9..6813666 100644 --- a/src/main/java/hrider/config/ServerDetails.java +++ b/src/main/java/hrider/config/ServerDetails.java @@ -1,5 +1,7 @@ package hrider.config; +import java.io.Serializable; + /** * Copyright (C) 2012 NICE Systems ltd. *

@@ -20,7 +22,11 @@ *

* This class represents a details of the server to connect to.. */ -public class ServerDetails { +public class ServerDetails implements Serializable { + + //region Constants + private static final long serialVersionUID = -407779299748851910L; + //endregion //region Variables /** @@ -37,6 +43,7 @@ public class ServerDetails { /** * Initializes a new instance of the {@link ServerDetails} class. + * * @param host The server name. * @param port The server port. */ @@ -50,6 +57,7 @@ public ServerDetails(String host, String port) { /** * Gets the name of the server. + * * @return The name of the server. */ public String getHost() { @@ -58,6 +66,7 @@ public String getHost() { /** * Sets a new server name. + * * @param host A new server name. */ public void setHost(String host) { @@ -66,6 +75,7 @@ public void setHost(String host) { /** * Gets a port. + * * @return An {@link Integer} representing the port. */ public String getPort() { @@ -74,6 +84,7 @@ public String getPort() { /** * Sets a new port. + * * @param port A new port. */ public void setPort(String port) { diff --git a/src/main/java/hrider/converters/BinaryStringConverter.java b/src/main/java/hrider/converters/BinaryStringConverter.java new file mode 100644 index 0000000..fd67c2d --- /dev/null +++ b/src/main/java/hrider/converters/BinaryStringConverter.java @@ -0,0 +1,44 @@ +package hrider.converters; + +import org.apache.hadoop.hbase.util.Bytes; + +/** + * Copyright (C) 2012 NICE Systems ltd. + *

+ * 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. + * + * @author Igor Cher + * @version %I%, %G% + *

+ * This class is responsible for converting data from {@link byte[]} to {@link String} with support of specific bytes and vice versa. + */ +public class BinaryStringConverter extends StringConverter { + + private static final long serialVersionUID = 3532868191797750281L; + + @Override + public String toString(byte[] value) { + if (value == null) { + return null; + } + return Bytes.toStringBinary(value); + } + + @Override + public byte[] toBytes(String value) { + if (value == null) { + return EMPTY_BYTES_ARRAY; + } + return Bytes.toBytesBinary(value); + } +} diff --git a/src/main/java/hrider/converters/BooleanConverter.java b/src/main/java/hrider/converters/BooleanConverter.java new file mode 100644 index 0000000..c59ab3d --- /dev/null +++ b/src/main/java/hrider/converters/BooleanConverter.java @@ -0,0 +1,44 @@ +package hrider.converters; + +import org.apache.hadoop.hbase.util.Bytes; + +/** + * Copyright (C) 2012 NICE Systems ltd. + *

+ * 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. + * + * @author Igor Cher + * @version %I%, %G% + *

+ * This class is responsible for converting data from byte[] to boolean and vice versa. + */ +public class BooleanConverter extends TypeConverter { + + private static final long serialVersionUID = 804613405302620990L; + + @Override + public String toString(byte[] value) { + if (value == null) { + return null; + } + return Boolean.toString(Bytes.toBoolean(value)); + } + + @Override + public byte[] toBytes(String value) { + if (value == null) { + return EMPTY_BYTES_ARRAY; + } + return Bytes.toBytes(Boolean.parseBoolean(value)); + } +} diff --git a/src/main/java/hrider/converters/ConvertersLoader.java b/src/main/java/hrider/converters/ConvertersLoader.java new file mode 100644 index 0000000..6e8dc5a --- /dev/null +++ b/src/main/java/hrider/converters/ConvertersLoader.java @@ -0,0 +1,268 @@ +package hrider.converters; + +import hrider.config.GlobalConfig; +import hrider.io.Log; +import hrider.io.PathHelper; +import hrider.reflection.JavaPackage; + +import java.io.*; +import java.lang.reflect.Modifier; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.*; + +/** + * Copyright (C) 2012 NICE Systems ltd. + *

+ * 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. + * + * @author Igor Cher + * @version %I%, %G% + *

+ * This class is responsible for loading and creation of type converters. + */ +@SuppressWarnings({"CallToPrintStackTrace", "ResultOfMethodCallIgnored"}) +public class ConvertersLoader { + + //region Variables + private static final Log logger = Log.getLogger(ConvertersLoader.class); + private static final List handlers; + private static Map converters; + //endregion + + //region Constructor + static { + handlers = new ArrayList(); + converters = load(); + } + + private ConvertersLoader() { + } + //endregion + + //region Public Methods + + /** + * Adds a handler. + * + * @param handler The new handler. + */ + public static void addHandler(ConvertersLoaderHandler handler) { + handlers.add(handler); + } + + /** + * Removes a handler. + * + * @param handler A handler to remove. + */ + public static void removeHandler(ConvertersLoaderHandler handler) { + handlers.remove(handler); + } + + /** + * Checks whether the specified converter exists. + * + * @param name The name of the converter to check. + * @return True if the specified converter exists or False otherwise. + */ + public static boolean exists(String name) { + return converters.containsKey(name); + } + + /** + * Gets all loaded converters. + * + * @return A list of type converters. + */ + public static Collection getConverters() { + return converters.values(); + } + + /** + * Gets all loaded converters that support column name conversions. + * + * @return A list of type converters. + */ + public static Collection getNameConverters() { + Collection list = new ArrayList(); + for (TypeConverter converter : converters.values()) { + if (converter.isValidForNameConversion()) { + list.add(converter); + } + } + return list; + } + + /** + * Gets a specific converter according to the provided name. + * + * @param name The name of the converter to get. + * @return A type converter if found or null otherwise. + */ + public static TypeConverter getConverter(String name) { + return converters.get(name); + } + + /** + * Handles converter editing. + * + * @param oldName A new converter name if the name was changed or the old one. + * @param newName A new converter name if the name was changed or the old one. + */ + public static void editConverter(String oldName, String newName) { + if (!oldName.equals(newName)) { + deleteConverter(oldName); + } + + reload(); + + for (ConvertersLoaderHandler handler : handlers) { + handler.onEdit(oldName, newName); + } + } + + /** + * Removes converter from the loader's cache and from the file system. + * + * @param name The name of the converter to remove. + */ + public static void removeConverter(String name) { + if (deleteConverter(name)) { + reload(); + + for (ConvertersLoaderHandler handler : handlers) { + handler.onRemove(name); + } + } + } + + /** + * Reloads converters. + */ + public static void reload() { + converters = load(); + + for (ConvertersLoaderHandler handler : handlers) { + handler.onLoad(); + } + } + //endregion + + //region Private Methods + private static boolean deleteConverter(String name) { + TypeConverter converter = converters.get(name); + if (converter != null) { + converters.remove(name); + + File classFile = new File( + PathHelper.append( + PathHelper.append(GlobalConfig.instance().getConvertersClassesFolder(), "hrider/converters/custom"), + converter.getClass().getSimpleName() + ".class")); + + if (classFile.exists()) { + classFile.delete(); + } + + File codeFile = new File(PathHelper.append(GlobalConfig.instance().getConvertersCodeFolder(), converter.getClass().getSimpleName() + ".java")); + if (codeFile.exists()) { + codeFile.delete(); + } + + return true; + } + return false; + } + + private static Map load() { + Map map = new TreeMap(); + + loadPackage("hrider.converters", map); + loadFolder(GlobalConfig.instance().getConvertersClassesFolder(), "hrider.converters.custom", map); + + return map; + } + + private static void loadPackage(String packageName, Map map) { + try { + for (Class clazz : JavaPackage.getClasses(packageName)) { + if (TypeConverter.class.isAssignableFrom(clazz) && !Modifier.isAbstract(clazz.getModifiers())) { + try { + TypeConverter converter = (TypeConverter)clazz.getConstructor().newInstance(); + map.put(converter.getName(), converter); + } + catch (Exception e) { + logger.error(e, "Failed to load converter '%s'", clazz.getName()); + } + } + } + } + catch (Exception e) { + logger.error(e, "Failed to load converters from the package '%s'", packageName); + } + } + + private static void loadFolder(String folder, String packageName, Map map) { + try { + URLClassLoader loader = new URLClassLoader( + new URL[]{ + new File(folder).toURI().toURL() + }, Thread.currentThread().getContextClassLoader()); + + for (Class clazz : JavaPackage.getClassesFromFolder(loader, new File(folder), packageName)) { + if (TypeConverter.class.isAssignableFrom(clazz) && !Modifier.isAbstract(clazz.getModifiers())) { + try { + TypeConverter converter = (TypeConverter)clazz.getConstructor().newInstance(); + converter.setCode( + loadCode( + new File( + PathHelper.append(GlobalConfig.instance().getConvertersCodeFolder(), converter.getClass().getSimpleName() + ".java")))); + + map.put(converter.getName(), converter); + } + catch (Exception e) { + logger.error(e, "Failed to load converter '%s'", clazz.getName()); + } + } + } + } + catch (Exception e) { + logger.error(e, "Failed to load converters from the folder '%s' for the package '%s'", folder, packageName); + } + } + + private static String loadCode(File file) throws IOException, FileNotFoundException { + if (file.exists()) { + StringBuilder code = new StringBuilder(); + + BufferedReader reader = null; + try { + reader = new BufferedReader(new FileReader(file)); + + String line; + while ((line = reader.readLine()) != null) { + code.append(line); + code.append(PathHelper.LINE_SEPARATOR); + } + return code.toString(); + } + finally { + if (reader != null) { + reader.close(); + } + } + } + return null; + } + //endregion +} diff --git a/src/main/java/hrider/converters/ConvertersLoaderHandler.java b/src/main/java/hrider/converters/ConvertersLoaderHandler.java new file mode 100644 index 0000000..4b068d3 --- /dev/null +++ b/src/main/java/hrider/converters/ConvertersLoaderHandler.java @@ -0,0 +1,42 @@ +package hrider.converters; + +/** + * Copyright (C) 2012 NICE Systems ltd. + *

+ * 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. + * + * @author Igor Cher + * @version %I%, %G% + */ +public interface ConvertersLoaderHandler { + + /** + * The method is called when all the converters were loaded. + */ + void onLoad(); + + /** + * The method is called when a converter was edited. + * + * @param oldName The old name of the converter if it was changed. + * @param newName The new name of the converter if it was changed. + */ + void onEdit(String oldName, String newName); + + /** + * The method is called when the converter was deleted. + * + * @param name The name of the deleted converter. + */ + void onRemove(String name); +} diff --git a/src/main/java/hrider/converters/DateAsLongConverter.java b/src/main/java/hrider/converters/DateAsLongConverter.java new file mode 100644 index 0000000..163b914 --- /dev/null +++ b/src/main/java/hrider/converters/DateAsLongConverter.java @@ -0,0 +1,64 @@ +package hrider.converters; + +import hrider.config.GlobalConfig; +import hrider.io.Log; +import org.apache.hadoop.hbase.util.Bytes; + +import java.text.DateFormat; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.Locale; + +/** + * Copyright (C) 2012 NICE Systems ltd. + *

+ * 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. + * + * @author Igor Cher + * @version %I%, %G% + *

+ * This class is responsible for converting data from byte[] to Date represented as Long and vice versa. + */ +public class DateAsLongConverter extends TypeConverter { + + private static final Log logger = Log.getLogger(DateAsLongConverter.class); + private static final long serialVersionUID = 7524770450006316409L; + + @Override + public String toString(byte[] value) { + if (value == null) { + return null; + } + + DateFormat df = new SimpleDateFormat(GlobalConfig.instance().getDateFormat(), Locale.ENGLISH); + return df.format(new Date(Bytes.toLong(value))); + } + + @Override + public byte[] toBytes(String value) { + if (value == null) { + return EMPTY_BYTES_ARRAY; + } + + DateFormat df = new SimpleDateFormat(GlobalConfig.instance().getDateFormat(), Locale.ENGLISH); + try { + Date date = df.parse(value); + return Bytes.toBytes(date.getTime()); + } + catch (ParseException e) { + logger.error(e, "Failed to convert DateTime '%s' to byte array.", value); + return null; + } + } +} diff --git a/src/main/java/hrider/converters/DateAsStringConverter.java b/src/main/java/hrider/converters/DateAsStringConverter.java new file mode 100644 index 0000000..3b0758b --- /dev/null +++ b/src/main/java/hrider/converters/DateAsStringConverter.java @@ -0,0 +1,31 @@ +package hrider.converters; + +/** + * Copyright (C) 2012 NICE Systems ltd. + *

+ * 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. + * + * @author Igor Cher + * @version %I%, %G% + *

+ * This class is responsible for converting data from byte[] to Date represented as String and vice versa. + */ +public class DateAsStringConverter extends StringConverter { + + private static final long serialVersionUID = 8385887468388870228L; + + @Override + public boolean isValidForNameConversion() { + return false; + } +} diff --git a/src/main/java/hrider/converters/DoubleConverter.java b/src/main/java/hrider/converters/DoubleConverter.java new file mode 100644 index 0000000..0367b4a --- /dev/null +++ b/src/main/java/hrider/converters/DoubleConverter.java @@ -0,0 +1,44 @@ +package hrider.converters; + +import org.apache.hadoop.hbase.util.Bytes; + +/** + * Copyright (C) 2012 NICE Systems ltd. + *

+ * 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. + * + * @author Igor Cher + * @version %I%, %G% + *

+ * This class is responsible for converting data from byte[] to double and vice versa. + */ +public class DoubleConverter extends TypeConverter { + + private static final long serialVersionUID = 2098974229929678180L; + + @Override + public String toString(byte[] value) { + if (value == null) { + return null; + } + return Double.toString(Bytes.toDouble(value)); + } + + @Override + public byte[] toBytes(String value) { + if (value == null) { + return EMPTY_BYTES_ARRAY; + } + return Bytes.toBytes(Double.parseDouble(value)); + } +} diff --git a/src/main/java/hrider/converters/FloatConverter.java b/src/main/java/hrider/converters/FloatConverter.java new file mode 100644 index 0000000..2686886 --- /dev/null +++ b/src/main/java/hrider/converters/FloatConverter.java @@ -0,0 +1,44 @@ +package hrider.converters; + +import org.apache.hadoop.hbase.util.Bytes; + +/** + * Copyright (C) 2012 NICE Systems ltd. + *

+ * 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. + * + * @author Igor Cher + * @version %I%, %G% + *

+ * This class is responsible for converting data from byte[] to float and vice versa. + */ +public class FloatConverter extends TypeConverter { + + private static final long serialVersionUID = -4945353944739350169L; + + @Override + public String toString(byte[] value) { + if (value == null) { + return null; + } + return Float.toString(Bytes.toFloat(value)); + } + + @Override + public byte[] toBytes(String value) { + if (value == null) { + return EMPTY_BYTES_ARRAY; + } + return Bytes.toBytes(Float.parseFloat(value)); + } +} diff --git a/src/main/java/hrider/converters/IntegerConverter.java b/src/main/java/hrider/converters/IntegerConverter.java new file mode 100644 index 0000000..3b6bf7e --- /dev/null +++ b/src/main/java/hrider/converters/IntegerConverter.java @@ -0,0 +1,44 @@ +package hrider.converters; + +import org.apache.hadoop.hbase.util.Bytes; + +/** + * Copyright (C) 2012 NICE Systems ltd. + *

+ * 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. + * + * @author Igor Cher + * @version %I%, %G% + *

+ * This class is responsible for converting data from byte[] to integer and vice versa. + */ +public class IntegerConverter extends TypeConverter { + + private static final long serialVersionUID = -6800729143958428018L; + + @Override + public String toString(byte[] value) { + if (value == null) { + return null; + } + return Integer.toString(Bytes.toInt(value)); + } + + @Override + public byte[] toBytes(String value) { + if (value == null) { + return EMPTY_BYTES_ARRAY; + } + return Bytes.toBytes(Integer.parseInt(value)); + } +} diff --git a/src/main/java/hrider/converters/JsonConverter.java b/src/main/java/hrider/converters/JsonConverter.java new file mode 100644 index 0000000..da84773 --- /dev/null +++ b/src/main/java/hrider/converters/JsonConverter.java @@ -0,0 +1,31 @@ +package hrider.converters; + +/** + * Copyright (C) 2012 NICE Systems ltd. + *

+ * 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. + * + * @author Igor Cher + * @version %I%, %G% + *

+ * This class is a place holder for JSON editors. + */ +public class JsonConverter extends StringConverter { + + private static final long serialVersionUID = 6603609684230707505L; + + @Override + public boolean isValidForNameConversion() { + return false; + } +} diff --git a/src/main/java/hrider/converters/LongConverter.java b/src/main/java/hrider/converters/LongConverter.java new file mode 100644 index 0000000..4edc3ed --- /dev/null +++ b/src/main/java/hrider/converters/LongConverter.java @@ -0,0 +1,44 @@ +package hrider.converters; + +import org.apache.hadoop.hbase.util.Bytes; + +/** + * Copyright (C) 2012 NICE Systems ltd. + *

+ * 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. + * + * @author Igor Cher + * @version %I%, %G% + *

+ * This class is responsible for converting data from byte[] to long and vice versa. + */ +public class LongConverter extends TypeConverter { + + private static final long serialVersionUID = -7610134214895755716L; + + @Override + public String toString(byte[] value) { + if (value == null) { + return null; + } + return Long.toString(Bytes.toLong(value)); + } + + @Override + public byte[] toBytes(String value) { + if (value == null) { + return EMPTY_BYTES_ARRAY; + } + return Bytes.toBytes(Long.parseLong(value)); + } +} diff --git a/src/main/java/hrider/converters/ShortConverter.java b/src/main/java/hrider/converters/ShortConverter.java new file mode 100644 index 0000000..f4374da --- /dev/null +++ b/src/main/java/hrider/converters/ShortConverter.java @@ -0,0 +1,44 @@ +package hrider.converters; + +import org.apache.hadoop.hbase.util.Bytes; + +/** + * Copyright (C) 2012 NICE Systems ltd. + *

+ * 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. + * + * @author Igor Cher + * @version %I%, %G% + *

+ * This class is responsible for converting data from byte[] to short and vice versa. + */ +public class ShortConverter extends TypeConverter { + + private static final long serialVersionUID = -4063681955061981871L; + + @Override + public String toString(byte[] value) { + if (value == null) { + return null; + } + return Short.toString(Bytes.toShort(value)); + } + + @Override + public byte[] toBytes(String value) { + if (value == null) { + return EMPTY_BYTES_ARRAY; + } + return Bytes.toBytes(Short.parseShort(value)); + } +} diff --git a/src/main/java/hrider/converters/StringConverter.java b/src/main/java/hrider/converters/StringConverter.java new file mode 100644 index 0000000..cc9cdb0 --- /dev/null +++ b/src/main/java/hrider/converters/StringConverter.java @@ -0,0 +1,49 @@ +package hrider.converters; + +import org.apache.hadoop.hbase.util.Bytes; + +/** + * Copyright (C) 2012 NICE Systems ltd. + *

+ * 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. + * + * @author Igor Cher + * @version %I%, %G% + *

+ * This class is responsible for converting data from byte[] to string and vice versa. + */ +public class StringConverter extends TypeConverter { + + private static final long serialVersionUID = 8661833153430116861L; + + @Override + public byte[] toBytes(String value) { + if (value == null) { + return EMPTY_BYTES_ARRAY; + } + return Bytes.toBytes(value); + } + + @Override + public String toString(byte[] value) { + if (value == null) { + return null; + } + return Bytes.toString(value); + } + + @Override + public boolean isValidForNameConversion() { + return true; + } +} diff --git a/src/main/java/hrider/converters/TypeConverter.java b/src/main/java/hrider/converters/TypeConverter.java new file mode 100644 index 0000000..1cf121d --- /dev/null +++ b/src/main/java/hrider/converters/TypeConverter.java @@ -0,0 +1,139 @@ +package hrider.converters; + +import hrider.io.Log; + +import java.io.Serializable; + +/** + * Copyright (C) 2012 NICE Systems ltd. + *

+ * 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. + * + * @author Igor Cher + * @version %I%, %G% + *

+ * This class is a base class for type converters. + */ +@SuppressWarnings("CallToSimpleGetterFromWithinClass") +public abstract class TypeConverter implements Comparable, Serializable { + + //region Constants + protected static final Log logger = Log.getLogger(TypeConverter.class); + protected static final byte[] EMPTY_BYTES_ARRAY = new byte[0]; + + private static final long serialVersionUID = 1490434164342371320L; + //endregion + + //region Variables + private String name; + private String code; + //endregion + + //region Constructor + protected TypeConverter() { + this.name = this.getClass().getSimpleName().replace("Converter", ""); + } + //endregion + + //region Public Methods + + /** + * Gets the code of the type converter if available. + * + * @return The original code. + */ + public String getCode() { + return code; + } + + /** + * Sets the code for the type converter. + * + * @param code The code to set. + */ + public void setCode(String code) { + this.code = code; + } + + /** + * Gets the name of the converter to be presented as a column type. + * + * @return The name of the converter. + */ + public String getName() { + return this.name; + } + + /** + * Checks whether the type converter can be edited. + * + * @return True if the type converter can be edited or False otherwise. + */ + public boolean isEditable() { + return code != null; + } + + /** + * Indicates whether the type converter can be used for column name conversions. + * + * @return True if the type converter can be used for column name conversions or False otherwise. + */ + public boolean isValidForNameConversion() { + return false; + } + + /** + * Converts an {@link String} to an array of bytes. + * + * @param value An string to convert. + * @return A converted byte array. + */ + public abstract byte[] toBytes(String value); + + /** + * Converts an {@link byte[]} to a {@link String}. + * + * @param value An byte[] to convert. + * @return A converted string. + */ + public abstract String toString(byte[] value); + + @Override + public int compareTo(TypeConverter o) { + return this.getName().compareTo(o.getName()); + } + + @Override + public boolean equals(Object obj) { + if (obj == null) { + return false; + } + + if (TypeConverter.class.isAssignableFrom(obj.getClass())) { + return getName().equals(((TypeConverter)obj).getName()); + } + + return false; + } + + @Override + public int hashCode() { + return getName().hashCode(); + } + + @Override + public String toString() { + return getName(); + } + //endregion +} diff --git a/src/main/java/hrider/converters/XmlConverter.java b/src/main/java/hrider/converters/XmlConverter.java new file mode 100644 index 0000000..94aebfe --- /dev/null +++ b/src/main/java/hrider/converters/XmlConverter.java @@ -0,0 +1,31 @@ +package hrider.converters; + +/** + * Copyright (C) 2012 NICE Systems ltd. + *

+ * 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. + * + * @author Igor Cher + * @version %I%, %G% + *

+ * This class is a place holder for XML editors. + */ +public class XmlConverter extends StringConverter { + + private static final long serialVersionUID = -2217840069549275853L; + + @Override + public boolean isValidForNameConversion() { + return false; + } +} diff --git a/src/main/java/hrider/data/ColumnQualifier.java b/src/main/java/hrider/data/ColumnQualifier.java index a6b8869..fc192b6 100644 --- a/src/main/java/hrider/data/ColumnQualifier.java +++ b/src/main/java/hrider/data/ColumnQualifier.java @@ -1,6 +1,10 @@ package hrider.data; +import hrider.converters.TypeConverter; +import hrider.io.Log; + import java.io.Serializable; +import java.util.Arrays; /** * Copyright (C) 2012 NICE Systems ltd. @@ -25,50 +29,54 @@ public class ColumnQualifier implements Serializable { //region Constants - public final static ColumnQualifier KEY = new ColumnQualifier("key"); + public final static ColumnQualifier KEY = new ColumnQualifier("key", ColumnType.BinaryString.getConverter()); //endregion //region Variables + private static final Log logger = Log.getLogger(ColumnQualifier.class); private static final long serialVersionUID = 7851349292786398645L; - private String name; - private ColumnFamily columnFamily; + + private byte[] name; + private ColumnFamily columnFamily; + private TypeConverter nameConverter; //endregion //region Constructor - public ColumnQualifier(String name) { - this(name, null); + public ColumnQualifier(String name, TypeConverter nameConverter) { + this(nameConverter.toBytes(name), null, nameConverter); } - public ColumnQualifier(String name, ColumnFamily columnFamily) { - if (columnFamily == null) { - if (name.contains(":")) { - String[] parts = name.split(":"); - - this.name = parts[1]; - this.columnFamily = new ColumnFamily(parts[0]); - } - else { - this.name = name; - } - } - else { - this.name = name; - this.columnFamily = columnFamily; - } + public ColumnQualifier(String name, ColumnFamily columnFamily, TypeConverter nameConverter) { + this(nameConverter.toBytes(name), columnFamily, nameConverter); + } + + public ColumnQualifier(byte[] name, ColumnFamily columnFamily, TypeConverter nameConverter) { + this.name = name; + this.columnFamily = columnFamily; + this.nameConverter = nameConverter; } //endregion //region Public Properties public String getName() { + try { + return this.nameConverter.toString(this.name); + } + catch (Exception e) { + logger.error(e, "Failed to convert byte array '%s' to string using '%s' converter.", Arrays.toString(this.name), this.nameConverter.getName()); + return ColumnType.BinaryString.toString(this.name); + } + } + + public byte[] getNameAsByteArray() { return this.name; } public String getFullName() { if (this.columnFamily != null) { - return String.format("%s:%s", this.columnFamily, this.name); + return String.format("%s:%s", this.columnFamily, getName()); } - - return this.name; + return getName(); } public String getFamily() { @@ -81,11 +89,19 @@ public String getFamily() { public ColumnFamily getColumnFamily() { return this.columnFamily; } + + public TypeConverter getNameConverter() { + return nameConverter; + } + + public void setNameConverter(TypeConverter nameConverter) { + this.nameConverter = nameConverter; + } //endregion //region Public Methods public static boolean isKey(String name) { - return KEY.name.equalsIgnoreCase(name); + return KEY.getName().equalsIgnoreCase(name); } public boolean isKey() { diff --git a/src/main/java/hrider/data/ColumnType.java b/src/main/java/hrider/data/ColumnType.java new file mode 100644 index 0000000..65cfab7 --- /dev/null +++ b/src/main/java/hrider/data/ColumnType.java @@ -0,0 +1,202 @@ +package hrider.data; + +import hrider.converters.ConvertersLoader; +import hrider.converters.TypeConverter; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Collection; + +/** + * Copyright (C) 2012 NICE Systems ltd. + *

+ * 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. + * + * @author Igor Cher + * @version %I%, %G% + *

+ * This class represents a type of the data supported by the tool. + */ +public class ColumnType implements Serializable { + + //region Constants + public final static ColumnType String = new ColumnType("String"); + public final static ColumnType BinaryString = new ColumnType("BinaryString"); + public final static ColumnType Boolean = new ColumnType("Boolean"); + public final static ColumnType Integer = new ColumnType("Integer"); + public final static ColumnType DateAsLong = new ColumnType("DateAsLong"); + public final static ColumnType DateAsString = new ColumnType("DateAsString"); + public final static ColumnType Double = new ColumnType("Double"); + public final static ColumnType Float = new ColumnType("Float"); + public final static ColumnType Long = new ColumnType("Long"); + public final static ColumnType Short = new ColumnType("Short"); + public final static ColumnType Json = new ColumnType("Json"); + public final static ColumnType Xml = new ColumnType("Xml"); + + private static final long serialVersionUID = -5311385557088499851L; + //endregion + + //region Variables + private TypeConverter converter; + //endregion + + //region Constructor + public ColumnType(String name) { + this(ConvertersLoader.getConverter(name)); + } + + public ColumnType(TypeConverter converter) { + if (converter == null) { + throw new IllegalArgumentException("converter cannot be null"); + } + this.converter = converter; + } + //endregion + + //region Public Methods + + /** + * Creates an instance of {@link ColumnType} from the name. + * + * @param name The name of the type to be created. + * @return A newly created {@link ColumnType}. + */ + public static ColumnType fromName(String name) { + if (ConvertersLoader.exists(name)) { + return new ColumnType(name); + } + return null; + } + + /** + * Creates an instance of {@link ColumnType} from the name. If there is no column type matching the name the default type is returned. + * + * @param name The name of the type to be created. + * @param defaultType The default type to be returned if the requested one does not exist. + * @return A newly created {@link ColumnType} if found or user provided default type otherwise. + */ + public static ColumnType fromNameOrDefault(String name, ColumnType defaultType) { + if (ConvertersLoader.exists(name)) { + return new ColumnType(name); + } + return defaultType; + } + + /** + * Gets all supported column types. + * + * @return A list of supported column types. + */ + public static Collection getTypes() { + Collection types = new ArrayList(); + for (TypeConverter converter : ConvertersLoader.getConverters()) { + types.add(new ColumnType(converter)); + } + return types; + } + + /** + * Gets column types that support column name conversions. + * + * @return A list of column types. + */ + public static Collection getNameTypes() { + Collection types = new ArrayList(); + for (TypeConverter converter : ConvertersLoader.getNameConverters()) { + types.add(new ColumnType(converter)); + } + return types; + } + + /** + * Gets the type of the column. The default value is {@link ColumnType#String} in case the column is not known. + * + * @param column The name of the column. + * @return The type of the column if found or a {@link ColumnType#String} as a default. + */ + public static ColumnType fromColumn(String column) { + if (column.contains("timestamp")) { + return DateAsLong; + } + if ("key".equalsIgnoreCase(column)) { + return BinaryString; + } + return String; + } + + /** + * Gets the name of the column type. + * + * @return The type's name. + */ + public String getName() { + return converter.getName(); + } + + /** + * Gets a converter used to convert the values of the type. + * + * @return An instance of {@link TypeConverter}. + */ + public TypeConverter getConverter() { + return converter; + } + + /** + * Checks whether the column type can be edited. + * + * @return True if the column type can be edited or False otherwise. + */ + public boolean isEditable() { + return converter.isEditable(); + } + + /** + * Converts an {@link Object} to a {@link String}. + * + * @param value An object to convert. + * @return A converted string. + */ + public String toString(byte[] value) { + return converter.toString(value); + } + + /** + * Converts a value represented as a {@link String} to an array of bytes according to the type. + * + * @param value The value to converters. + * @return A byte array representing the value or a null if something went wrong during conversion. + */ + public byte[] toBytes(String value) { + return converter.toBytes(value); + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof ColumnType) { + return this.converter.equals(((ColumnType)obj).converter); + } + return false; + } + + @Override + public int hashCode() { + return this.converter.hashCode(); + } + + @Override + public String toString() { + return this.converter.toString(); + } + //endregion +} diff --git a/src/main/java/hrider/data/ConvertibleObject.java b/src/main/java/hrider/data/ConvertibleObject.java new file mode 100644 index 0000000..a06313d --- /dev/null +++ b/src/main/java/hrider/data/ConvertibleObject.java @@ -0,0 +1,174 @@ +package hrider.data; + +import java.io.Serializable; +import java.util.Arrays; + +/** + * Copyright (C) 2012 NICE Systems ltd. + *

+ * 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. + * + * @author Igor Cher + * @version %I%, %G% + *

+ * This class represents an object with type. + */ +public class ConvertibleObject implements Serializable { + + //region Constants + private static final long serialVersionUID = 1701395986295913520L; + //endregion + + //region Variables + /** + * The type of the {@link ConvertibleObject#value}. + */ + private ColumnType type; + /** + * The original value. + */ + private byte[] value; + //endregion + + //region Constructor + + /** + * Initializes a new instance of the {@link ConvertibleObject} class. + * + * @param type The type of the object. + * @param value The object. + */ + public ConvertibleObject(ColumnType type, byte[] value) { + this.type = type; + this.value = value; + } + //endregion + + //region Public Properties + + /** + * Gets the value. + * + * @return A value. + */ + public byte[] getValue() { + return this.value; + } + + /** + * Sets a new value. + * + * @param value A new value. + */ + public void setValue(byte[] value) { + this.value = value; + } + + /** + * Sets a new value. + * + * @param value A new value. + */ + public void setValueAsString(String value) { + this.value = this.type.toBytes(value); + } + + /** + * Gets a byte[] value as a {@link String}. + * + * @return A string representing the original byte[] value. + */ + public String getValueAsString() { + if (this.value != null) { + return this.type.toString(this.value); + } + return ""; + } + + /** + * Gets the type of the object. + * + * @return The object type. + */ + public ColumnType getType() { + return this.type; + } + + /** + * Sets a new object type. + * + * @param type A new object type. + */ + public void setType(ColumnType type) { + this.type = type; + } + + /** + * Tries to understand the type of the value based on value itself. + * + * @return A guessed type if successful or null. + */ + public ColumnType guessType() { + if (type.equals(ColumnType.String)) { + String str = type.toString(this.value); + + if (str.startsWith("{") && str.endsWith("}")) { + return ColumnType.Json; + } + + if (str.startsWith("[") && str.endsWith("]")) { + return ColumnType.Json; + } + + if (str.startsWith("<") && str.endsWith(">")) { + return ColumnType.Xml; + } + } + return null; + } + //endregion + + //region Public Methods + + /** + * Checks if the object has exactly the same value. + * + * @param value The value to check. + * @return True if the provided value equals to the object's value or False otherwise. + */ + public boolean isEqual(String value) { + if (this.value != null) { + return this.type.toString(this.value).equals(value); + } + return false; + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof ConvertibleObject) { + return Arrays.equals(((ConvertibleObject)obj).value, this.value); + } + return false; + } + + @Override + public int hashCode() { + return this.value != null ? Arrays.hashCode(this.value) : 0; + } + + @Override + public String toString() { + return getValueAsString(); + } + //endregion +} diff --git a/src/main/java/hrider/data/DataCell.java b/src/main/java/hrider/data/DataCell.java index 7339512..4ed70c2 100644 --- a/src/main/java/hrider/data/DataCell.java +++ b/src/main/java/hrider/data/DataCell.java @@ -1,5 +1,9 @@ package hrider.data; +import hrider.converters.TypeConverter; + +import java.io.Serializable; + /** * Copyright (C) 2012 NICE Systems ltd. *

@@ -20,21 +24,25 @@ *

* This class represents a cell in the grid or a key/value pair of the hbase row. */ -public class DataCell { +public class DataCell implements Serializable { + + //region Constants + private static final long serialVersionUID = 387452287094391735L; + //endregion //region Variables /** * The row the current cell belongs to. */ - private DataRow row; + private DataRow row; /** * The column (an hbase qualifier) where the cell is located. */ - private ColumnQualifier column; + private ColumnQualifier column; /** * The value of the cell. */ - private TypedObject typedValue; + private ConvertibleObject convertibleValue; //endregion //region Constructor @@ -42,14 +50,14 @@ public class DataCell { /** * Initializes a new instance of the {@link DataCell} with parameters. * - * @param row The owner of the cell. - * @param column The name of the column/qualifier. - * @param typedValue The value. + * @param row The owner of the cell. + * @param column The name of the column/qualifier. + * @param convertibleValue The value. */ - public DataCell(DataRow row, ColumnQualifier column, TypedObject typedValue) { + public DataCell(DataRow row, ColumnQualifier column, ConvertibleObject convertibleValue) { this.row = row; this.column = column; - this.typedValue = typedValue; + this.convertibleValue = convertibleValue; } //endregion @@ -92,21 +100,57 @@ public void setColumn(ColumnQualifier column) { } /** - * Gets a cell value. + * Gets cell's value. + * + * @return A cell's value represented as a string. + */ + public String getValue() { + return this.convertibleValue.getValueAsString(); + } + + /** + * Sets a new value for the cell. + * + * @param value A new value to set. + */ + public void setValue(String value) { + this.convertibleValue.setValueAsString(value); + } + + /** + * Gets cell's value as byte array. * - * @return A reference to the {@link TypedObject} that holds the value. + * @return A cell's value represented as a byte array. */ - public TypedObject getTypedValue() { - return this.typedValue; + public byte[] getValueAsByteArray() { + return this.convertibleValue.getValue(); } /** - * Sets a new cell value. + * Gets cell's type. * - * @param typedValue A new cell value. + * @return The cell's type. */ - public void setTypedValue(TypedObject typedValue) { - this.typedValue = typedValue; + public ColumnType getType() { + return this.convertibleValue.getType(); + } + + /** + * Sets a new object type for the cell. + * + * @param type A new object type. + */ + public void setType(ColumnType type) { + this.convertibleValue.setType(type); + } + + /** + * Sets new converter for column name. + * + * @param converter A new converter to set. + */ + public void setColumnNameConverter(TypeConverter converter) { + this.column.setNameConverter(converter); } /** @@ -115,11 +159,8 @@ public void setTypedValue(TypedObject typedValue) { * @param value The value to check. * @return True if the cell contains the value or False otherwise. */ - public boolean contains(Object value) { - if (this.typedValue.getValue() != null) { - return this.typedValue.getValue().equals(value); - } - return false; + public boolean hasValue(String value) { + return convertibleValue.isEqual(value); } /** @@ -131,30 +172,20 @@ public boolean isKey() { return this.column.isKey(); } - /** - * Converts a data represented as {@link String} to an actual type. - * - * @param data The data to convert. - * @return A converted data. - */ - public Object toObject(String data) { - return this.typedValue.getType().toObject(data); - } - /** * Tries to understand the type of the value based on value itself. * * @return A guessed type if successful or null. */ - public ObjectType guessType() { - return this.typedValue.guessType(); + public ColumnType guessType() { + return this.convertibleValue.guessType(); } @Override public boolean equals(Object obj) { if (obj instanceof DataCell) { DataCell cell = (DataCell)obj; - return cell.typedValue.equals(this.typedValue) && + return cell.convertibleValue.equals(this.convertibleValue) && cell.row.equals(this.row) && cell.column.equals(this.column); } @@ -163,12 +194,12 @@ public boolean equals(Object obj) { @Override public int hashCode() { - return this.typedValue.hashCode(); + return this.convertibleValue.hashCode(); } @Override public String toString() { - return this.typedValue.toString(); + return this.convertibleValue.toString(); } //endregion } diff --git a/src/main/java/hrider/data/DataRow.java b/src/main/java/hrider/data/DataRow.java index c76dd35..9c13935 100644 --- a/src/main/java/hrider/data/DataRow.java +++ b/src/main/java/hrider/data/DataRow.java @@ -1,5 +1,8 @@ package hrider.data; +import hrider.converters.TypeConverter; + +import java.io.Serializable; import java.util.HashMap; import java.util.Map; @@ -23,13 +26,17 @@ *

* This class represents a row in the grid or an hbase row. */ -public class DataRow { +public class DataRow implements Serializable { + + //region Constants + private static final long serialVersionUID = 5158560383309065143L; + //endregion //region Variables /** * The row key. */ - private TypedObject key; + private ConvertibleObject key; /** * The list of cells that belong to the row. */ @@ -50,7 +57,7 @@ public DataRow() { * * @param key The row key. */ - public DataRow(TypedObject key) { + public DataRow(ConvertibleObject key) { this(); this.key = key; } @@ -63,7 +70,7 @@ public DataRow(TypedObject key) { * * @return The key of the row. */ - public TypedObject getKey() { + public ConvertibleObject getKey() { return this.key; } @@ -72,7 +79,7 @@ public TypedObject getKey() { * * @param key The new key to set. */ - public void setKey(TypedObject key) { + public void setKey(ConvertibleObject key) { this.key = key; } @@ -88,6 +95,7 @@ public DataCell getCell(String columnName) { /** * Gets the cell according to the specified column qualifier. + * * @param columnQualifier The column qualifier to look for the cell. * @return The instance of the {@link DataCell} if found or null otherwise. */ @@ -147,11 +155,26 @@ public Iterable getCells() { * @param columnName The name of the column which type should be updated. * @param columnType The new column type. */ - public void updateColumnType(String columnName, ObjectType columnType) { + public void updateColumnType(String columnName, ColumnType columnType) { DataCell cell = getCell(columnName); - if (cell != null && cell.getTypedValue() != null) { - cell.getTypedValue().setType(columnType); + if (cell != null) { + cell.setType(columnType); + } + } + + /** + * Updates converter for the column name. + * + * @param converter The new column name converter. + */ + public void updateColumnNameConverter(TypeConverter converter) { + Map map = new HashMap(); + for (DataCell cell : this.cells.values()) { + cell.setColumnNameConverter(converter); + + map.put(cell.getColumn().getFullName(), cell); } + this.cells = map; } /** @@ -162,7 +185,7 @@ public void updateColumnType(String columnName, ObjectType columnType) { public Map toMap() { Map values = new HashMap(); for (Map.Entry entry : this.cells.entrySet()) { - values.put(entry.getKey(), entry.getValue().getTypedValue().getValue()); + values.put(entry.getKey(), entry.getValue().getValue()); } return values; } diff --git a/src/main/java/hrider/data/ObjectType.java b/src/main/java/hrider/data/ObjectType.java deleted file mode 100644 index 7dd86e0..0000000 --- a/src/main/java/hrider/data/ObjectType.java +++ /dev/null @@ -1,251 +0,0 @@ -package hrider.data; - -import hrider.config.GlobalConfig; -import org.apache.hadoop.hbase.util.Bytes; - -import java.text.DateFormat; -import java.text.ParseException; -import java.text.SimpleDateFormat; -import java.util.Date; -import java.util.Locale; - -/** - * Copyright (C) 2012 NICE Systems ltd. - *

- * 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. - * - * @author Igor Cher - * @version %I%, %G% - *

- * This enum represents a type of the data supported by the tool. - */ -@SuppressWarnings("UnnecessaryDefault") -public enum ObjectType { - - String, - BinaryString, - Integer, - Long, - Float, - Double, - Boolean, - Short, - DateAsString, - DateAsLong, - Xml, - Json; - - //region Constants - private static final byte[] EMPTY_BYTES_ARRAY = new byte[0]; - //endregion - - //region Public Methods - - /** - * Gets the type of the column. The default value is {@link ObjectType#String} in case the column is not known. - * - * @param column The name of the column. - * @return The type of the column if found or a {@link ObjectType#String} as a default. - */ - public static ObjectType fromColumn(String column) { - if (column.toLowerCase().endsWith("timestamp")) { - return DateAsLong; - } - return String; - } - - /** - * Converts a value represented as a {@link String} to an object according to the type. - * - * @param value The value to convert. - * @return An object representing the value or a null if something went wrong during conversion. - */ - public Object toObject(String value) { - if (value == null) { - return null; - } - - switch (this) { - case String: - case BinaryString: - return value; - case Integer: - return java.lang.Integer.parseInt(value); - case Long: - return java.lang.Long.parseLong(value); - case Float: - return java.lang.Float.parseFloat(value); - case Double: - return java.lang.Double.parseDouble(value); - case Boolean: - return java.lang.Boolean.parseBoolean(value); - case Short: - return java.lang.Short.parseShort(value); - case DateAsLong: - case DateAsString: - DateFormat df = new SimpleDateFormat(GlobalConfig.instance().getDateFormat(), Locale.ENGLISH); - try { - return df.parse(value); - } - catch (ParseException ignored) { - return null; - } - case Xml: - case Json: - return value; - default: - return value; - } - } - - /** - * Converts a value represented as a {@link String} to an array of bytes according to the type. - * - * @param value The value to convert. - * @return A byte array representing the value or a null if something went wrong during conversion. - */ - public byte[] fromString(String value) { - if (value == null) { - return EMPTY_BYTES_ARRAY; - } - - switch (this) { - case String: - return Bytes.toBytes(value); - case BinaryString: - return Bytes.toBytesBinary(value); - case Integer: - return Bytes.toBytes(java.lang.Integer.parseInt(value)); - case Long: - return Bytes.toBytes(java.lang.Long.parseLong(value)); - case Float: - return Bytes.toBytes(java.lang.Float.parseFloat(value)); - case Double: - return Bytes.toBytes(java.lang.Double.parseDouble(value)); - case Boolean: - return Bytes.toBytes(java.lang.Boolean.parseBoolean(value)); - case Short: - return Bytes.toBytes(java.lang.Short.parseShort(value)); - case DateAsString: - return Bytes.toBytes(value); - case DateAsLong: - return Bytes.toBytes(((Date)toObject(value)).getTime()); - case Xml: - case Json: - return Bytes.toBytes(value); - default: - return Bytes.toBytes(value); - } - } - - /** - * Converts a value represented as a {@link byte[]} to an object according to the type. - * - * @param value The value to convert. - * @return An object representing the value or a null if something went wrong during conversion. - */ - public Object fromByteArray(byte[] value) { - if (value == null) { - return null; - } - - switch (this) { - case String: - return Bytes.toString(value); - case BinaryString: - return Bytes.toStringBinary(value); - case Integer: - return Bytes.toInt(value); - case Long: - return Bytes.toLong(value); - case Float: - return Bytes.toFloat(value); - case Double: - return Bytes.toDouble(value); - case Boolean: - return Bytes.toBoolean(value); - case Short: - return Bytes.toShort(value); - case DateAsString: - DateFormat df = new SimpleDateFormat(GlobalConfig.instance().getDateFormat(), Locale.ENGLISH); - try { - return df.parse(Bytes.toString(value)); - } - catch (ParseException ignored) { - return null; - } - case DateAsLong: - return new Date(Bytes.toLong(value)); - case Xml: - case Json: - return Bytes.toString(value); - default: - return Bytes.toString(value); - } - } - - /** - * Converts a value represented as an {@link Object} to an array of bytes according to the type. - * - * @param value The value to convert. - * @return A byte array representing the value or a null if something went wrong during conversion. - */ - public byte[] fromObject(Object value) { - if (value == null) { - return EMPTY_BYTES_ARRAY; - } - - switch (this) { - case String: - return Bytes.toBytes((String)value); - case BinaryString: - return Bytes.toBytesBinary((String)value); - case Integer: - return Bytes.toBytes((java.lang.Integer)value); - case Long: - return Bytes.toBytes((java.lang.Long)value); - case Float: - return Bytes.toBytes((java.lang.Float)value); - case Double: - return Bytes.toBytes((java.lang.Double)value); - case Boolean: - return Bytes.toBytes((java.lang.Boolean)value); - case Short: - return Bytes.toBytes((java.lang.Short)value); - case DateAsString: - if (value instanceof Date) { - DateFormat df = new SimpleDateFormat(GlobalConfig.instance().getDateFormat(), Locale.ENGLISH); - return Bytes.toBytes(df.format((Date)value)); - } - else { - return Bytes.toBytes((String)value); - } - case DateAsLong: - if (value instanceof Date) { - return Bytes.toBytes(((Date)value).getTime()); - } - else if (value instanceof java.lang.Long) { - return Bytes.toBytes((java.lang.Long)value); - } - else { - return Bytes.toBytes(((Date)toObject((String)value)).getTime()); - } - case Xml: - case Json: - return Bytes.toBytes((String)value); - default: - return Bytes.toBytes((String)value); - } - } - //endregion -} diff --git a/src/main/java/hrider/data/TypedColumn.java b/src/main/java/hrider/data/TypedColumn.java index bc1f61b..da75925 100644 --- a/src/main/java/hrider/data/TypedColumn.java +++ b/src/main/java/hrider/data/TypedColumn.java @@ -18,7 +18,7 @@ * @author Igor Cher * @version %I%, %G% *

- * The class represents a column with type. The type information is used to convert the value represented by an array of bytes to the actual object. + * The class represents a column with type. The type information is used to converters the value represented by an array of bytes to the actual object. */ public class TypedColumn { @@ -30,7 +30,7 @@ public class TypedColumn { /** * The type of the column. */ - private ObjectType type; + private ColumnType type; //endregion //region Constructor @@ -41,7 +41,7 @@ public class TypedColumn { * @param column The qualifier of the column. * @param columnType The type of the column. */ - public TypedColumn(ColumnQualifier column, ObjectType columnType) { + public TypedColumn(ColumnQualifier column, ColumnType columnType) { this.column = column; this.type = columnType; } @@ -72,7 +72,7 @@ public void setColumn(ColumnQualifier value) { * * @return The type of the column. */ - public ObjectType getType() { + public ColumnType getType() { return this.type; } @@ -81,7 +81,7 @@ public ObjectType getType() { * * @param value A new column type. */ - public void setType(ObjectType value) { + public void setType(ColumnType value) { this.type = value; } //endregion diff --git a/src/main/java/hrider/data/TypedObject.java b/src/main/java/hrider/data/TypedObject.java deleted file mode 100644 index 50a2b8c..0000000 --- a/src/main/java/hrider/data/TypedObject.java +++ /dev/null @@ -1,179 +0,0 @@ -package hrider.data; - -import hrider.config.GlobalConfig; - -import java.text.DateFormat; -import java.text.SimpleDateFormat; -import java.util.Date; -import java.util.Locale; - -/** - * Copyright (C) 2012 NICE Systems ltd. - *

- * 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. - * - * @author Igor Cher - * @version %I%, %G% - *

- * This class represents an object with type. - */ -public class TypedObject { - - //region Variables - /** - * An actual value. - */ - private Object value; - /** - * The type of the {@link TypedObject#value}. - */ - private ObjectType type; - /** - * The original value. - */ - private byte[] orgValue; - //endregion - - //region Constructor - - /** - * Initializes a new instance of the {@link TypedObject} class. - * - * @param objType The type of the object. - * @param objValue The object. - */ - public TypedObject(ObjectType objType, Object objValue) { - this.type = objType; - - if (this.type == null) { - this.type = ObjectType.String; - } - - if (objValue instanceof byte[]) { - this.orgValue = (byte[])objValue; - this.value = this.type.fromByteArray((byte[])objValue); - } - else { - this.orgValue = this.type.fromObject(objValue); - this.value = objValue; - } - } - //endregion - - //region Public Properties - - /** - * Gets the value. - * - * @return A value. - */ - public Object getValue() { - return this.value; - } - - /** - * Sets a new value. - * - * @param obj A new value. - */ - public void setValue(Object obj) { - this.value = obj; - } - - /** - * Gets the type of the object. - * - * @return The object type. - */ - public ObjectType getType() { - return this.type; - } - - /** - * Sets a new object type. - * - * @param objType A new object type. - */ - public void setType(ObjectType objType) { - if (this.type != null) { - Object objValue = objType.fromByteArray(this.orgValue); - if (objValue != null) { - this.value = objValue; - this.type = objType; - } - } - } - - /** - * Tries to understand the type of the value based on value itself. - * - * @return A guessed type if successful or null. - */ - public ObjectType guessType() { - if (type == ObjectType.String) { - String str = ((String)value).trim(); - - if (str.startsWith("{") && str.endsWith("}")) { - return ObjectType.Json; - } - - if (str.startsWith("[") && str.endsWith("]")) { - return ObjectType.Json; - } - - if (str.startsWith("<") && str.endsWith(">")) { - return ObjectType.Xml; - } - } - return null; - } - //endregion - - //region Public Methods - - /** - * Converts an object to a byte array according ot its type. - * - * @return A byte array. - */ - public byte[] toByteArray() { - return this.type.fromObject(this.value); - } - - @Override - public boolean equals(Object obj) { - if (obj instanceof TypedObject) { - TypedObject typedObject = (TypedObject)obj; - return typedObject.value != null ? typedObject.value.equals(this.value) : this.value == null; - } - return false; - } - - @Override - public int hashCode() { - return this.value != null ? this.value.hashCode() : 0; - } - - @Override - public String toString() { - if (this.value != null) { - if (this.type == ObjectType.DateAsString || this.type == ObjectType.DateAsLong) { - DateFormat df = new SimpleDateFormat(GlobalConfig.instance().getDateFormat(), Locale.ENGLISH); - return df.format((Date)this.value); - } - return this.value.toString(); - } - return ""; - } - //endregion -} diff --git a/src/main/java/hrider/export/FileExporter.java b/src/main/java/hrider/export/FileExporter.java index 7955833..d387913 100644 --- a/src/main/java/hrider/export/FileExporter.java +++ b/src/main/java/hrider/export/FileExporter.java @@ -85,7 +85,7 @@ public void write(DataRow row, Iterable columns) throws IOExcep for (ColumnQualifier column : columns) { DataCell cell = row.getCell(column); if (cell != null) { - formatter.append(cell.getTypedValue().getValue()); + formatter.append(cell.getValue()); } else { formatter.append(""); diff --git a/src/main/java/hrider/hbase/Connection.java b/src/main/java/hrider/hbase/Connection.java index 1e1bd3c..efaa583 100644 --- a/src/main/java/hrider/hbase/Connection.java +++ b/src/main/java/hrider/hbase/Connection.java @@ -182,6 +182,17 @@ public boolean tableExists(String tableName) throws IOException { return tableName != null && this.hbaseAdmin.tableExists(tableName); } + /** + * Checks whether the specified table is enabled. + * + * @param tableName The name of the table to check. + * @return True if the table is enabled or False otherwise. + * @throws IOException Error accessing hbase. + */ + public boolean tableEnabled(String tableName) throws IOException { + return tableName != null && this.hbaseAdmin.isTableEnabled(tableName); + } + /** * Creates a new table or modifies an existing one in the hbase cluster. * @@ -280,39 +291,44 @@ public void copyTable(TableDescriptor targetTable, TableDescriptor sourceTable, scan.setCaching(GlobalConfig.instance().getBatchSizeForRead()); ResultScanner scanner = source.getScanner(scan); - List puts = new ArrayList(); + try { + List puts = new ArrayList(); - int batchSize = GlobalConfig.instance().getBatchSizeForWrite(); + int batchSize = GlobalConfig.instance().getBatchSizeForWrite(); - boolean isValid; + boolean isValid; - do { - Result result = scanner.next(); + do { + Result result = scanner.next(); - isValid = result != null; - if (isValid) { - Put put = new Put(result.getRow()); - for (KeyValue kv : result.list()) { - put.add(kv); - } + isValid = result != null; + if (isValid) { + Put put = new Put(result.getRow()); + for (KeyValue kv : result.list()) { + put.add(kv); + } - puts.add(put); + puts.add(put); - if (puts.size() == batchSize) { - target.put(puts); - puts.clear(); - } + if (puts.size() == batchSize) { + target.put(puts); + puts.clear(); + } - for (HbaseActionListener listener : this.listeners) { - listener.copyOperation(sourceCluster.serverName, sourceTable.getName(), this.serverName, targetTable.getName(), result); + for (HbaseActionListener listener : this.listeners) { + listener.copyOperation(sourceCluster.serverName, sourceTable.getName(), this.serverName, targetTable.getName(), result); + } } } - } - while (isValid); + while (isValid); - // add the last puts to the table. - if (!puts.isEmpty()) { - target.put(puts); + // add the last puts to the table. + if (!puts.isEmpty()) { + target.put(puts); + } + } + finally { + scanner.close(); } } @@ -333,11 +349,13 @@ public void saveTable(String tableName, String path) throws IOException { StoreFile.Writer writer = new StoreFile.WriterBuilder( this.getConfiguration(), new CacheConfig(cacheConfig), fs, HFile.DEFAULT_BLOCKSIZE).withFilePath(new Path(path)).build(); + ResultScanner scanner = null; + try { Scan scan = new Scan(); scan.setCaching(GlobalConfig.instance().getBatchSizeForRead()); - ResultScanner scanner = table.getScanner(scan); + scanner = table.getScanner(scan); boolean isValid; do { @@ -357,6 +375,10 @@ public void saveTable(String tableName, String path) throws IOException { while (isValid); } finally { + if (scanner != null) { + scanner.close(); + } + writer.close(); } } @@ -372,6 +394,16 @@ public void flushTable(String tableName) throws IOException, InterruptedExceptio this.hbaseAdmin.flush(tableName); } + /** + * Enables the table. + * + * @param tableName The name of the table to enable. + * @throws IOException Error accessing hbase. + */ + public void enableTable(String tableName) throws IOException { + this.hbaseAdmin.enableTable(tableName); + } + /** * Loads a locally saved HFile to an existing table. * @@ -435,7 +467,7 @@ public void loadTable(String tableName, String path) throws IOException, TableNo familiesToCreate.clear(); } - table.put(puts); + HTableUtil.bucketRsPut(table, puts); puts.clear(); } @@ -458,7 +490,7 @@ public void loadTable(String tableName, String path) throws IOException, TableNo createFamilies(tableName, toDescriptors(familiesToCreate)); } - table.put(puts); + HTableUtil.bucketRsPut(table, puts); } } finally { @@ -485,7 +517,7 @@ public void setRows(String tableName, Iterable rows) throws IOException List puts = new ArrayList(); for (DataRow row : rows) { - Put put = new Put(row.getKey().toByteArray()); + Put put = new Put(row.getKey().getValue()); for (DataCell cell : row.getCells()) { if (!cell.isKey()) { @@ -495,7 +527,7 @@ public void setRows(String tableName, Iterable rows) throws IOException byte[] family = Bytes.toBytesBinary(cell.getColumn().getFamily()); byte[] column = Bytes.toBytesBinary(cell.getColumn().getName()); - byte[] value = cell.getTypedValue().toByteArray(); + byte[] value = cell.getValueAsByteArray(); put.add(family, column, value); } @@ -513,7 +545,7 @@ public void setRows(String tableName, Iterable rows) throws IOException } HTable table = this.factory.get(tableName); - table.put(puts); + HTableUtil.bucketRsPut(table, puts); } /** @@ -533,7 +565,7 @@ public void setRow(String tableName, DataRow row) throws IOException, TableNotFo Collection familiesToCreate = new HashSet(); - Put put = new Put(row.getKey().toByteArray()); + Put put = new Put(row.getKey().getValue()); for (DataCell cell : row.getCells()) { if (!cell.isKey()) { if (!families.contains(cell.getColumn().getColumnFamily())) { @@ -542,7 +574,7 @@ public void setRow(String tableName, DataRow row) throws IOException, TableNotFo byte[] family = Bytes.toBytesBinary(cell.getColumn().getFamily()); byte[] column = Bytes.toBytesBinary(cell.getColumn().getName()); - byte[] value = cell.getTypedValue().toByteArray(); + byte[] value = cell.getValueAsByteArray(); put.add(family, column, value); } @@ -569,7 +601,7 @@ public void setRow(String tableName, DataRow row) throws IOException, TableNotFo */ public void deleteRow(String tableName, DataRow row) throws IOException { HTable table = this.factory.get(tableName); - table.delete(new Delete(row.getKey().toByteArray())); + table.delete(new Delete(row.getKey().getValue())); for (HbaseActionListener listener : this.listeners) { listener.rowOperation(tableName, row, "removed"); @@ -658,7 +690,7 @@ private void createFamilies(String tableName, Iterable famili /** * Converts column family to column descriptor. * - * @param families A list of column families to convert. + * @param families A list of column families to converters. * @return A list of column descriptors. */ private static Iterable toDescriptors(Iterable families) { diff --git a/src/main/java/hrider/hbase/Query.java b/src/main/java/hrider/hbase/Query.java index f7562a2..1e9b18d 100644 --- a/src/main/java/hrider/hbase/Query.java +++ b/src/main/java/hrider/hbase/Query.java @@ -1,6 +1,6 @@ package hrider.hbase; -import hrider.data.ObjectType; +import hrider.data.ColumnType; import java.util.Date; @@ -35,7 +35,7 @@ public class Query { * The type of the start key. The key must be converted to byte array before it can be used in scanner. In order for the scan to match * the key it must be converted to the byte array according to its actual type otherwise we will have different byte arrays which will never match. */ - private ObjectType startKeyType; + private ColumnType startKeyType; /** * A key where the can should stop. */ @@ -43,7 +43,7 @@ public class Query { /** * The type of the end key. */ - private ObjectType endKeyType; + private ColumnType endKeyType; /** * The date to start the scan from. Each key/value pair in hbase has a timestamp. */ @@ -75,7 +75,7 @@ public class Query { /** * The type of the word. */ - private ObjectType wordType; + private ColumnType wordType; //endregion //region Public Properties @@ -96,7 +96,7 @@ public byte[] getStartKey() { */ public String getStartKeyAsString() { if (this.startKeyType != null) { - return this.startKeyType.fromByteArray(this.startKey).toString(); + return this.startKeyType.toString(this.startKey); } return null; } @@ -116,9 +116,9 @@ public void setStartKey(byte[] value) { * @param keyType The type of the key to set. * @param key The key to set. */ - public void setStartKey(ObjectType keyType, String key) { + public void setStartKey(ColumnType keyType, String key) { this.startKeyType = keyType; - this.startKey = keyType.fromString(key); + this.startKey = keyType.toBytes(key); } /** @@ -126,7 +126,7 @@ public void setStartKey(ObjectType keyType, String key) { * * @return The start key type. */ - public ObjectType getStartKeyType() { + public ColumnType getStartKeyType() { return this.startKeyType; } @@ -135,7 +135,7 @@ public ObjectType getStartKeyType() { * * @param keyType A new start key type. */ - public void setStartKeyType(ObjectType keyType) { + public void setStartKeyType(ColumnType keyType) { this.startKeyType = keyType; } @@ -155,7 +155,7 @@ public byte[] getEndKey() { */ public String getEndKeyAsString() { if (this.endKeyType != null) { - return this.endKeyType.fromByteArray(this.endKey).toString(); + return this.endKeyType.toString(this.endKey); } return null; } @@ -175,9 +175,9 @@ public void setEndKey(byte[] value) { * @param keyType The key type. * @param key The key. */ - public void setEndKey(ObjectType keyType, String key) { + public void setEndKey(ColumnType keyType, String key) { this.endKeyType = keyType; - this.endKey = keyType.fromString(key); + this.endKey = keyType.toBytes(key); } /** @@ -185,7 +185,7 @@ public void setEndKey(ObjectType keyType, String key) { * * @return The end key type. */ - public ObjectType getEndKeyType() { + public ColumnType getEndKeyType() { return this.endKeyType; } @@ -194,7 +194,7 @@ public ObjectType getEndKeyType() { * * @param keyType A new key type. */ - public void setEndKeyType(ObjectType keyType) { + public void setEndKeyType(ColumnType keyType) { this.endKeyType = keyType; } @@ -315,7 +315,7 @@ public String getWord() { * @return A byte array. */ public byte[] getWordAsByteArray() { - return this.wordType.fromString(this.word); + return this.wordType.toBytes(this.word); } /** @@ -337,7 +337,7 @@ public void setWord(String value) { * * @return A type of the word. */ - public ObjectType getWordType() { + public ColumnType getWordType() { return this.wordType; } @@ -346,7 +346,7 @@ public ObjectType getWordType() { * * @param wordType A new word type. */ - public void setWordType(ObjectType wordType) { + public void setWordType(ColumnType wordType) { this.wordType = wordType; } //endregion diff --git a/src/main/java/hrider/hbase/Scanner.java b/src/main/java/hrider/hbase/Scanner.java index dbd73c1..1c63f7f 100644 --- a/src/main/java/hrider/hbase/Scanner.java +++ b/src/main/java/hrider/hbase/Scanner.java @@ -1,8 +1,10 @@ package hrider.hbase; import hrider.config.GlobalConfig; +import hrider.converters.TypeConverter; import hrider.data.*; import hrider.ui.MessageHandler; +import org.apache.commons.lang.time.StopWatch; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.hbase.HColumnDescriptor; import org.apache.hadoop.hbase.HTableDescriptor; @@ -10,7 +12,7 @@ import org.apache.hadoop.hbase.client.Result; import org.apache.hadoop.hbase.client.ResultScanner; import org.apache.hadoop.hbase.client.Scan; -import org.apache.hadoop.hbase.util.Bytes; +import org.apache.hadoop.hbase.filter.FirstKeyOnlyFilter; import java.io.IOException; import java.util.*; @@ -51,6 +53,10 @@ public class Scanner { * This operation is time consuming and its result should be cached. */ private long rowsCount; + /** + * Holds a rows number in the table if the calculation has not been completed because of the timeout. + */ + private long partialRowsCount; /** * The number of the last loaded row. */ @@ -58,7 +64,7 @@ public class Scanner { /** * The map of column types. The key is the name of the column and the value is the type of the objects within the column. */ - private Map columnTypes; + private Map columnTypes; /** * A list of markers. The marker is used for pagination to mark where the previous scan has stopped. */ @@ -71,6 +77,10 @@ public class Scanner { * Indicates if this scanner should support only forward navigation. */ private boolean forwardNavigateOnly; + /** + * Represents a converter for column names. + */ + private TypeConverter columnNameConverter; //endregion //region Constructor @@ -137,6 +147,15 @@ public String getTableName() { return this.tableName; } + /** + * Indicates that the rows count has been partially calculated. + * + * @return True if the rows count has been stopped before reaching end of the table or False otherwise. + */ + public boolean isRowsCountPartiallyCalculated() { + return this.partialRowsCount > 0; + } + /** * Gets a list of columns. If there is no columns at this moment they will be loaded according to the provided rows number. In other words only columns * of loaded rows will be returned. @@ -164,7 +183,7 @@ public Collection getColumns(int rowsNumber) { * * @return A map of column types. */ - public Map getColumnTypes() { + public Map getColumnTypes() { return this.columnTypes; } @@ -173,7 +192,7 @@ public Map getColumnTypes() { * * @param columnTypes A new map of column types. */ - public void setColumnTypes(Map columnTypes) { + public void setColumnTypes(Map columnTypes) { this.columnTypes = columnTypes; } @@ -213,7 +232,7 @@ public boolean hasPrev() { * @param columnName The name of the column. * @param columnType The new column type. */ - public void updateColumnType(String columnName, ObjectType columnType) { + public void updateColumnType(String columnName, ColumnType columnType) { Collection rows = this.current; if (rows != null) { for (DataRow row : rows) { @@ -222,12 +241,28 @@ public void updateColumnType(String columnName, ObjectType columnType) { } } + /** + * Updates converter for the column name. + * + * @param converter The new column name converter. + */ + public void updateColumnNameConverter(TypeConverter converter) { + this.columnNameConverter = converter; + + Collection rows = this.current; + if (rows != null) { + for (DataRow row : rows) { + row.updateColumnNameConverter(converter); + } + } + } + /** * Resets the cache. * * @param startKey The key the scan should start from. This parameter can be null. */ - public void resetCurrent(TypedObject startKey) { + public void resetCurrent(ConvertibleObject startKey) { this.current = null; this.rowsCount = 0; this.lastRow = 0; @@ -250,17 +285,22 @@ public DataRow getFirstRow() throws IOException { HTable table = this.connection.getTableFactory().get(this.tableName); ResultScanner scanner = table.getScanner(scan); - Collection rows = new ArrayList(); - Collection columns = new ArrayList(); + try { + Collection rows = new ArrayList(); + Collection columns = new ArrayList(); - columns.add(ColumnQualifier.KEY); + columns.add(ColumnQualifier.KEY); - loadRows(scanner, 0, 1, rows, columns); - if (rows.isEmpty()) { - return null; - } + loadRows(scanner, 0, 1, rows, columns); + if (rows.isEmpty()) { + return null; + } - return rows.iterator().next(); + return rows.iterator().next(); + } + finally { + scanner.close(); + } } /** @@ -336,6 +376,12 @@ public Collection prev() throws IOException { this.lastRow -= this.current.size(); this.current = peekMarker().rows; + + updateColumnNameConverter(columnNameConverter); + + for (Map.Entry entry : this.columnTypes.entrySet()) { + updateColumnType(entry.getKey(), entry.getValue()); + } } return this.current; } @@ -347,22 +393,40 @@ public Collection prev() throws IOException { * @return A total number of rows in the table. * @throws IOException Error accessing hbase. */ - public synchronized long getRowsCount() throws IOException { + public long getRowsCount(long timeout) throws IOException { if (this.rowsCount == 0) { + this.partialRowsCount = 0; + Scan scan = getScanner(); + scan.setFilter(new FirstKeyOnlyFilter()); scan.setCaching(GlobalConfig.instance().getBatchSizeForRead()); HTable table = this.connection.getTableFactory().get(this.tableName); ResultScanner scanner = table.getScanner(scan); - int count = 0; - for (Result rr = scanner.next() ; rr != null ; rr = scanner.next()) { - if (isValidRow(rr)) { - ++count; + StopWatch stopWatch = new StopWatch(); + stopWatch.start(); + + try { + int count = 0; + for (Result rr = scanner.next() ; rr != null ; rr = scanner.next()) { + if (isValidRow(rr)) { + ++count; + } + + if (stopWatch.getTime() > timeout) { + this.partialRowsCount = count; + + break; + } } - } - this.rowsCount = count; + this.rowsCount = count; + } + finally { + stopWatch.stop(); + scanner.close(); + } } return this.rowsCount; } @@ -404,13 +468,15 @@ protected boolean isValidRow(Result row) { * @return A key of the last loaded row. Used to mark the current position for the next scan. * @throws IOException Error accessing hbase. */ - protected TypedObject loadRows(ResultScanner scanner, long offset, int rowsNumber, Collection rows, Collection columns) throws - IOException { - ObjectType keyType = this.columnTypes.get(ColumnQualifier.KEY.getName()); + protected ConvertibleObject loadRows( + ResultScanner scanner, long offset, int rowsNumber, Collection rows, Collection columns) throws IOException { + ColumnType keyType = this.columnTypes.get(ColumnQualifier.KEY.getName()); int index = 0; boolean isValid; - TypedObject key = null; + ConvertibleObject key = null; + + TypeConverter nameConverter = getColumnNameConverterInternal(); HTable table = this.connection.getTableFactory().get(this.tableName); HTableDescriptor tableDescriptor = table.getTableDescriptor(); @@ -421,7 +487,7 @@ protected TypedObject loadRows(ResultScanner scanner, long offset, int rowsNumbe isValid = result != null && rows.size() < rowsNumber; if (isValid && isValidRow(result)) { if (index >= offset) { - key = new TypedObject(keyType, result.getRow()); + key = new ConvertibleObject(keyType, result.getRow()); DataRow row = new DataRow(key); row.addCell(new DataCell(row, ColumnQualifier.KEY, key)); @@ -431,9 +497,9 @@ protected TypedObject loadRows(ResultScanner scanner, long offset, int rowsNumbe HColumnDescriptor columnDescriptor = tableDescriptor.getFamily(familyEntry.getKey()); for (NavigableMap.Entry> qualifierEntry : familyEntry.getValue().entrySet()) { - ColumnQualifier qualifier = new ColumnQualifier(Bytes.toStringBinary(qualifierEntry.getKey()), new ColumnFamily(columnDescriptor)); + ColumnQualifier qualifier = new ColumnQualifier(qualifierEntry.getKey(), new ColumnFamily(columnDescriptor), nameConverter); - ObjectType columnType = ObjectType.String; + ColumnType columnType = ColumnType.String; String columnName = qualifier.getFullName(); if (this.columnTypes.containsKey(columnName)) { @@ -441,7 +507,7 @@ protected TypedObject loadRows(ResultScanner scanner, long offset, int rowsNumbe } for (NavigableMap.Entry cell : qualifierEntry.getValue().entrySet()) { - row.addCell(new DataCell(row, qualifier, new TypedObject(columnType, cell.getValue()))); + row.addCell(new DataCell(row, qualifier, new ConvertibleObject(columnType, cell.getValue()))); } if (!columns.contains(qualifier)) { @@ -480,31 +546,37 @@ private Collection loadColumns(int rowsNumber) throws IOExcepti HTableDescriptor tableDescriptor = table.getTableDescriptor(); ResultScanner scanner = table.getScanner(scan); + try { + TypeConverter nameConverter = getColumnNameConverterInternal(); - Result row; - int counter = 0; + Result row; + int counter = 0; - do { - row = scanner.next(); - if (row != null) { - NavigableMap>> familyMap = row.getMap(); - for (NavigableMap.Entry>> familyEntry : familyMap.entrySet()) { - HColumnDescriptor columnDescriptor = tableDescriptor.getFamily(familyEntry.getKey()); - - for (byte[] quantifier : familyEntry.getValue().keySet()) { - ColumnQualifier columnQualifier = new ColumnQualifier(Bytes.toStringBinary(quantifier), new ColumnFamily(columnDescriptor)); - if (!columns.contains(columnQualifier)) { - columns.add(columnQualifier); + do { + row = scanner.next(); + if (row != null) { + NavigableMap>> familyMap = row.getMap(); + for (NavigableMap.Entry>> familyEntry : familyMap.entrySet()) { + HColumnDescriptor columnDescriptor = tableDescriptor.getFamily(familyEntry.getKey()); + + for (byte[] quantifier : familyEntry.getValue().keySet()) { + ColumnQualifier columnQualifier = new ColumnQualifier(quantifier, new ColumnFamily(columnDescriptor), nameConverter); + if (!columns.contains(columnQualifier)) { + columns.add(columnQualifier); + } } } } + + counter++; } + while (row != null && counter < rowsNumber); - counter++; + return columns; + } + finally { + scanner.close(); } - while (row != null && counter < rowsNumber); - - return columns; } /** @@ -522,7 +594,7 @@ private Collection next(long offset, int rowsNumber) throws IOException scan.setCaching(itemsNumber); if (!this.markers.isEmpty()) { - scan.setStartRow(peekMarker().key.toByteArray()); + scan.setStartRow(peekMarker().key.getValue()); } if (this.forwardNavigateOnly) { @@ -533,17 +605,22 @@ private Collection next(long offset, int rowsNumber) throws IOException HTable table = this.connection.getTableFactory().get(this.tableName); ResultScanner scanner = table.getScanner(scan); - Collection rows = new ArrayList(); - Collection columns = new ArrayList(); + try { + Collection rows = new ArrayList(); + Collection columns = new ArrayList(); - columns.add(ColumnQualifier.KEY); + columns.add(ColumnQualifier.KEY); - TypedObject lastKey = loadRows(scanner, offset, rowsNumber, rows, columns); - if (lastKey != null) { - this.markers.push(new Marker(lastKey, rows, columns)); - } + ConvertibleObject lastKey = loadRows(scanner, offset, rowsNumber, rows, columns); + if (lastKey != null) { + this.markers.push(new Marker(lastKey, rows, columns)); + } - return rows; + return rows; + } + finally { + scanner.close(); + } } /** @@ -569,6 +646,18 @@ private Marker peekMarker() { } return null; } + + /** + * Gets configured column name converter or a default one. + * + * @return A type converter. + */ + private TypeConverter getColumnNameConverterInternal() { + if (this.columnNameConverter != null) { + return this.columnNameConverter; + } + return ColumnType.BinaryString.getConverter(); + } //endregion /** @@ -581,7 +670,7 @@ private static class Marker { /** * The last key loaded from the previous batch of rows. */ - private TypedObject key; + private ConvertibleObject key; /** * A list of previously loaded rows. */ @@ -601,7 +690,7 @@ private static class Marker { * @param rows A list of loaded rows. * @param columns A list of columns loaded from the rows. */ - private Marker(TypedObject key, Collection rows, Collection columns) { + private Marker(ConvertibleObject key, Collection rows, Collection columns) { this.key = key; this.rows = rows; this.columns = columns; diff --git a/src/main/java/hrider/io/CloseableHelper.java b/src/main/java/hrider/io/CloseableHelper.java new file mode 100644 index 0000000..7f85be1 --- /dev/null +++ b/src/main/java/hrider/io/CloseableHelper.java @@ -0,0 +1,37 @@ +package hrider.io; + +import java.io.Closeable; + +/** + * Copyright (C) 2012 NICE Systems ltd. + *

+ * 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. + * + * @author Igor Cher + * @version %I%, %G% + */ +public class CloseableHelper { + + private CloseableHelper() { + } + + public static void closeSilently(Closeable closeable) { + if (closeable != null) { + try { + closeable.close(); + } + catch (Exception ignore) { + } + } + } +} diff --git a/src/main/java/hrider/io/Downloader.java b/src/main/java/hrider/io/Downloader.java new file mode 100644 index 0000000..13385d2 --- /dev/null +++ b/src/main/java/hrider/io/Downloader.java @@ -0,0 +1,98 @@ +package hrider.io; + +import java.io.*; +import java.net.URL; +import java.net.URLConnection; + +/** + * Copyright (C) 2012 NICE Systems ltd. + *

+ * 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. + * + * @author Igor Cher + * @version %I%, %G% + */ +public class Downloader { + + //region Constants + private static final int DEFAULT_BUFFER_SIZE = 1024 * 4; + //endregion + + //region Constructor + private Downloader() { + } + //endregion + + //region Public Methods + public static File download(URL url) throws IOException, FileNotFoundException { + StringBuilder fileName = new StringBuilder(); + StringBuilder extension = new StringBuilder(); + + extractFileNameAndExtension(url.getPath(), fileName, extension); + + File temp = File.createTempFile(fileName.toString() + '-', extension.length() == 0 ? null : extension.toString()); + + InputStream in = url.openStream(); + OutputStream out = new FileOutputStream(temp); + + try { + copy(in, out); + } + finally { + try { + in.close(); + } + catch (IOException ignore) { + } + + try { + out.close(); + } + catch (IOException ignore) { + } + } + + return temp; + } + //endregion + + //region Private Methods + private static void copy(InputStream in, OutputStream out) throws IOException { + byte[] buffer = new byte[DEFAULT_BUFFER_SIZE]; + + int bytesRead; + + while (-1 != (bytesRead = in.read(buffer))) { + out.write(buffer, 0, bytesRead); + } + } + + private static void extractFileNameAndExtension(String path, StringBuilder fileName, StringBuilder extension) { + String leaf = path.replace("\\", "/"); + + int index = leaf.lastIndexOf('/'); + if (index != -1) { + leaf = leaf.substring(index + 1); + } + + index = leaf.lastIndexOf('.'); + if (index == -1) { + fileName.append(leaf); + } + else { + fileName.append(leaf.substring(0, index)); + extension.append(leaf.substring(index)); + } + } + //endregion +} diff --git a/src/main/java/hrider/io/FileHelper.java b/src/main/java/hrider/io/FileHelper.java new file mode 100644 index 0000000..1191e75 --- /dev/null +++ b/src/main/java/hrider/io/FileHelper.java @@ -0,0 +1,69 @@ +package hrider.io; + +import org.apache.commons.io.FileUtils; + +import java.io.File; +import java.io.IOException; +import java.util.Arrays; +import java.util.List; +import java.util.regex.Pattern; + +/** + * Copyright (C) 2012 NICE Systems ltd. + *

+ * 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. + * + * @author Igor Cher + * @version %I%, %G% + */ +public class FileHelper { + + //region Constructor + private FileHelper() { + } + //endregion + + //region Public Methods + public static void delete(File path, String... exclude) { + List excludedPaths = Arrays.asList(exclude); + + File[] files = path.listFiles(); + if (files != null) { + for (File file : files) { + if (!excludedPaths.contains(file.getName())) { + if (file.isDirectory()) { + delete(file); + } + file.delete(); + } + } + } + } + + public static File findFile(File folder, Pattern regex) { + File[] files = folder.listFiles(); + if (files != null) { + for (File file : files) { + if (regex.matcher(file.getName()).find()) { + return file; + } + } + } + return null; + } + + public static void copy(File source, File target) throws IOException { + FileUtils.copyFile(source, target); + } + //endregion +} diff --git a/src/main/java/hrider/io/Log.java b/src/main/java/hrider/io/Log.java new file mode 100644 index 0000000..fb1fa46 --- /dev/null +++ b/src/main/java/hrider/io/Log.java @@ -0,0 +1,58 @@ +package hrider.io; + +import org.apache.log4j.Logger; + +/** + * Copyright (C) 2012 NICE Systems ltd. + *

+ * 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. + * + * @author Igor Cher + * @version %I%, %G% + *

+ * This class is a log wrapper. + */ +public class Log { + + //region Variables + private Logger logger; + //endregion + + //region Constructor + private Log(Class clazz) { + logger = Logger.getLogger(clazz); + } + //endregion + + //region Public Methods + public static Log getLogger(Class clazz) { + return new Log(clazz); + } + + public void info(String format, Object... args) { + logger.info(String.format(format, args)); + } + + public void debug(String format, Object... args) { + logger.debug(String.format(format, args)); + } + + public void warn(Throwable t, String format, Object... args) { + logger.warn(String.format(format, args), t); + } + + public void error(Throwable t, String format, Object... args) { + logger.error(String.format(format, args), t); + } + //endregion +} diff --git a/src/main/java/hrider/io/PathHelper.java b/src/main/java/hrider/io/PathHelper.java index 1fc6762..444a6ec 100644 --- a/src/main/java/hrider/io/PathHelper.java +++ b/src/main/java/hrider/io/PathHelper.java @@ -1,13 +1,39 @@ package hrider.io; +import java.io.File; +import java.net.URI; +import java.net.URISyntaxException; import java.util.regex.Matcher; import java.util.regex.Pattern; +/** + * Copyright (C) 2012 NICE Systems ltd. + *

+ * 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. + * + * @author Igor Cher + * @version %I%, %G% + *

+ * This class is a helper class to work with path's. + */ public class PathHelper { //region Constants - private final static String FILE_SEPARATOR = System.getProperty("file.separator"); - private final static Pattern ENV_VAR = Pattern.compile("((?<=\\$\\{)[a-zA-Z_0-9]*(?=\\}))"); + public final static String FILE_SEPARATOR = "/"; + public final static String LINE_SEPARATOR = System.getProperty("line.separator"); + + private final static Log logger = Log.getLogger(PathHelper.class); + private final static Pattern ENV_VAR = Pattern.compile("((?<=\\$\\{)[a-zA-Z_0-9]*(?=\\}))"); //endregion //region Constructor @@ -16,6 +42,68 @@ private PathHelper() { //endregion //region Public Methods + /** + * Gets current folder of the executing process. + * + * @return A path to the current folder. + */ + public static String getCurrentFolder() { + return getParent(new File(".").getAbsolutePath()); + } + + /** + * Removes extension from the file path. + * + * @param path The path to remove extension. + * @return A new path if the provided path contained extension or an original path otherwise. + */ + public static String getPathWithoutExtension(String path) { + String normalisedPath = expand(path); + + int index = normalisedPath.lastIndexOf('.'); + if (index != -1) { + normalisedPath = normalisedPath.substring(0, index); + } + + return normalisedPath; + } + + public static String getFileNameWithoutExtension(String path) { + String leaf = getLeaf(path); + + int index = leaf.lastIndexOf('.'); + if (index != -1) { + leaf = leaf.substring(0, index); + } + + return leaf; + } + + public static String getFileExtension(String path) { + int index = path.lastIndexOf('.'); + if (index != -1) { + return path.substring(index); + } + return null; + } + + /** + * Removes media from the provided path. For example D://some_path/some_folder -> some_path/some_folder + * + * @param path The path to remove the media. + * @return A new path if the provided path contained media or an original path otherwise. + */ + public static String getPathWithoutMedia(String path) { + String normalisedPath = expand(path); + + int index = normalisedPath.indexOf(':' + FILE_SEPARATOR); + if (index != -1) { + normalisedPath = normalisedPath.substring(index + 1 + FILE_SEPARATOR.length()); + } + + return normalisedPath; + } + public static String expand(String path) { String expandedPath = path; if (expandedPath != null) { @@ -27,16 +115,21 @@ public static String expand(String path) { if (expVar != null) { expandedPath = expandedPath.replace(String.format("${%s}", envVar), expVar); } + + expVar = System.getProperty(envVar); + if (expVar != null) { + expandedPath = expandedPath.replace(String.format("${%s}", envVar), expVar); + } } } - return expandedPath; + return normalise(expandedPath); } public static String append(String path1, String path2) { StringBuilder path = new StringBuilder(); if (path1 != null) { - String normalisedPath = expand(normalise(path1)); + String normalisedPath = expand(path1); path.append(normalisedPath); if (!normalisedPath.endsWith(FILE_SEPARATOR)) { @@ -45,7 +138,7 @@ public static String append(String path1, String path2) { } if (path2 != null) { - String normalisedPath = expand(normalise(path2)); + String normalisedPath = expand(path2); if (path.length() > 0) { if (normalisedPath.startsWith(FILE_SEPARATOR)) { path.append(normalisedPath.substring(FILE_SEPARATOR.length())); @@ -64,7 +157,7 @@ public static String append(String path1, String path2) { public static String getParent(String path) { if (path != null) { - String normalisedPath = expand(normalise(path)); + String normalisedPath = expand(path); if (normalisedPath.endsWith(FILE_SEPARATOR)) { normalisedPath = normalisedPath.substring(0, normalisedPath.length() - FILE_SEPARATOR.length()); } @@ -74,12 +167,13 @@ public static String getParent(String path) { return normalisedPath.substring(0, index); } } + return null; } public static String getLeaf(String path) { if (path != null) { - String normalisedPath = expand(normalise(path)); + String normalisedPath = expand(path); if (normalisedPath.endsWith(FILE_SEPARATOR)) { normalisedPath = normalisedPath.substring(0, normalisedPath.length() - FILE_SEPARATOR.length()); } @@ -89,17 +183,41 @@ public static String getLeaf(String path) { return normalisedPath.substring(index + 1); } } + return null; } public static String normalise(String path) { if (path != null) { - if ("\\".equals(FILE_SEPARATOR)) { - return path.replace("/", "\\"); + String normalizedPath = path; + boolean removeSlash = false; + + try { + normalizedPath = normalizedPath.replace("\\", FILE_SEPARATOR); + removeSlash = !normalizedPath.startsWith(FILE_SEPARATOR); + + if (!normalizedPath.startsWith("file:")) { + if (normalizedPath.startsWith(FILE_SEPARATOR)) { + normalizedPath = "file:" + normalizedPath; + } + else { + normalizedPath = "file:/" + normalizedPath; + } + } + + URI uri = new URI(normalizedPath); + normalizedPath = uri.getPath(); } - else { - return path.replace("\\", "/"); + catch (URISyntaxException e) { + logger.warn(e, "Path is not valid URI: '%s'", normalizedPath); + } + + // Remove slash in the beginning of the path if it was added by the URI class. + if (removeSlash && normalizedPath.startsWith(FILE_SEPARATOR)) { + normalizedPath = normalizedPath.substring(FILE_SEPARATOR.length()); } + + return normalizedPath; } return null; } diff --git a/src/main/java/hrider/io/PathWatcher.java b/src/main/java/hrider/io/PathWatcher.java new file mode 100644 index 0000000..7d42a9d --- /dev/null +++ b/src/main/java/hrider/io/PathWatcher.java @@ -0,0 +1,42 @@ +package hrider.io; + +import java.io.Closeable; +import java.io.File; +import java.util.Date; +import java.util.Timer; +import java.util.TimerTask; + +public abstract class PathWatcher extends TimerTask implements Closeable { + + private long timeStamp; + private final File path; + private final Timer timer; + + protected PathWatcher(File path) { + this.path = path; + this.timeStamp = path.lastModified(); + this.timer = new Timer(); + } + + public void start(long period) { + this.timer.schedule(this, new Date(), period); + } + + @Override + public final void run() { + long timestamp = this.path.lastModified(); + + if (this.timeStamp != timestamp) { + this.timeStamp = timestamp; + + onChange(this.path); + } + } + + @Override + public void close() { + this.timer.cancel(); + } + + protected abstract void onChange(File file); +} diff --git a/src/main/java/hrider/reflection/Clazz.java b/src/main/java/hrider/reflection/Clazz.java index 458d0ce..2b07308 100644 --- a/src/main/java/hrider/reflection/Clazz.java +++ b/src/main/java/hrider/reflection/Clazz.java @@ -1,9 +1,12 @@ package hrider.reflection; +import hrider.config.GlobalConfig; + import java.lang.reflect.Field; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; +import java.text.DateFormat; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.*; /** * Copyright (C) 2012 NICE Systems ltd. @@ -27,8 +30,12 @@ */ public class Clazz { + //region Constructor private Clazz() { } + //endregion + + //region Public Methods /** * Converts a primitive value represented as {@link String} to object. @@ -38,7 +45,7 @@ private Clazz() { * @return A converted object. */ @SuppressWarnings("unchecked") - public static Object primitiveToObject(Class type, String value) { + public static Object fromPrimitive(Class type, String value) { if (isPrimitive(type)) { if (value != null && !value.isEmpty()) { String typeName = type.getSimpleName(); @@ -69,6 +76,19 @@ public static Object primitiveToObject(Class type, String value) { if ("String".equals(typeName)) { return value; } + if ("date".equalsIgnoreCase(typeName)) { + if (isNumber(value)) { + return new Date(Long.parseLong(value)); + } + + DateFormat df = new SimpleDateFormat(GlobalConfig.instance().getDateFormat(), Locale.ENGLISH); + try { + return df.parse(value); + } + catch (ParseException ignored) { + return null; + } + } if (type.isEnum()) { return Enum.valueOf(type, value); } @@ -78,44 +98,41 @@ public static Object primitiveToObject(Class type, String value) { } /** - * Sets a value to the field. + * Converts a {@link String} to a number represented by {@link Integer} or {@link Long}. * - * @param obj The instance which field is to be updated. - * @param field The field to be updated. - * @param value The value to be set. - * @throws IllegalAccessException Error accessing the field. + * @param value The value to converters. + * @return A converted number if a conversion was successful or null otherwise. */ - public static void setValue(Object obj, Field field, Object value) throws IllegalAccessException { - field.setAccessible(true); - - String typeName = field.getType().getSimpleName(); - if ("int".equals(typeName) || "Integer".equals(typeName)) { - field.setInt(obj, (Integer)value); - } - else if ("long".equalsIgnoreCase(typeName)) { - field.setLong(obj, (Long)value); + public static Object toNumber(String value) { + if (isNumber(value)) { + return Long.parseLong(value); } - else if ("boolean".equalsIgnoreCase(typeName)) { - field.setBoolean(obj, (Boolean)value); - } - else if ("byte".equalsIgnoreCase(typeName)) { - field.setByte(obj, (Byte)value); - } - else if ("char".equals(typeName) || "Character".equals(typeName)) { - field.setChar(obj, (Character)value); - } - else if ("short".equalsIgnoreCase(typeName)) { - field.setShort(obj, (Short)value); - } - else if ("float".equalsIgnoreCase(typeName)) { - field.setFloat(obj, (Float)value); - } - else if ("double".equalsIgnoreCase(typeName)) { - field.setDouble(obj, (Double)value); - } - else { - field.set(obj, value); + return null; + } + + /** + * Converts a {@link String} to a floating number represented by {@link Integer} or {@link Long}. + * + * @param value The value to converters. + * @return A converted number if a conversion was successful or null otherwise. + */ + public static Object toDecimal(String value) { + if (isDecimal(value)) { + return Double.parseDouble(value); } + return null; + } + + /** + * Gets a value of the field. + * + * @param obj The instance which field's value is to be retrieved. + * @param fieldName The name of the field that holds the value. + * @return A value extracted from the field. + * @throws IllegalAccessException Error accessing the field. + */ + public static Object getValue(Object obj, String fieldName) throws NoSuchFieldException, IllegalAccessException { + return getValue(obj, obj.getClass().getDeclaredField(fieldName)); } /** @@ -157,6 +174,59 @@ public static Object getValue(Object obj, Field field) throws IllegalAccessExcep return field.get(obj); } + /** + * Sets a value to the field. + * + * @param obj The instance which field is to be updated. + * @param fieldName The name of the field to be updated. + * @param value The value to be set. + * @throws IllegalAccessException Error accessing the field. + */ + public static void setValue(Object obj, String fieldName, Object value) throws NoSuchFieldException, IllegalAccessException { + setValue(obj, obj.getClass().getDeclaredField(fieldName), value); + } + + /** + * Sets a value to the field. + * + * @param obj The instance which field is to be updated. + * @param field The field to be updated. + * @param value The value to be set. + * @throws IllegalAccessException Error accessing the field. + */ + public static void setValue(Object obj, Field field, Object value) throws IllegalAccessException { + field.setAccessible(true); + + String typeName = field.getType().getSimpleName(); + if ("int".equals(typeName) || "Integer".equals(typeName)) { + field.setInt(obj, (Integer)value); + } + else if ("long".equalsIgnoreCase(typeName)) { + field.setLong(obj, (Long)value); + } + else if ("boolean".equalsIgnoreCase(typeName)) { + field.setBoolean(obj, (Boolean)value); + } + else if ("byte".equalsIgnoreCase(typeName)) { + field.setByte(obj, (Byte)value); + } + else if ("char".equals(typeName) || "Character".equals(typeName)) { + field.setChar(obj, (Character)value); + } + else if ("short".equalsIgnoreCase(typeName)) { + field.setShort(obj, (Short)value); + } + else if ("float".equalsIgnoreCase(typeName)) { + field.setFloat(obj, (Float)value); + } + else if ("double".equalsIgnoreCase(typeName)) { + field.setDouble(obj, (Double)value); + } + else { + field.set(obj, value); + } + } + /** * Returns all public/protected/private fields including super classes. * @@ -206,11 +276,90 @@ public static boolean isPrimitive(Class type) { * @return True if the string represents a number or false otherwise. */ public static boolean isNumber(String value) { - for (char letter : value.toCharArray()) { + if (value == null) { + return false; + } + + char[] array = value.toCharArray(); + for (int i = 0 ; i < array.length ; i++) { + char letter = array[i]; if (!Character.isDigit(letter)) { - return false; + if (i > 0 || letter != '-') { + return false; + } } } return true; } + + /** + * Checks whether the specified class represents a type that can hold a number. + * + * @param clazz The class to check. + * @return True if the specified type can hold the number or False otherwise. + */ + public static boolean isNumber(Class clazz) { + if (clazz == null) { + return false; + } + + return clazz.equals(Integer.class) || + clazz.equals(Long.class) || + clazz.equals(Byte.class) || + clazz.equals(Short.class); + } + + /** + * Checks whether the string represents a floating number. + * + * @param value The string to check. + * @return True if the string represents a number or false otherwise. + */ + public static boolean isDecimal(String value) { + if (value == null) { + return false; + } + + boolean dotFound = false; + + char[] array = value.toCharArray(); + for (int i = 0 ; i < array.length ; i++) { + char letter = array[i]; + if (!Character.isDigit(letter)) { + if (letter == '.') { + if (dotFound) { + return false; + } + if (i == 0 || i == array.length - 1) { + return false; + } + if (!Character.isDigit(array[i - 1]) || !Character.isDigit(array[i + 1])) { + return false; + } + dotFound = true; + } + if (letter == '-') { + if (i > 0) { + return false; + } + } + } + } + return dotFound; + } + + /** + * Checks whether the specified class represents a type that can hold a number. + * + * @param clazz The class to check. + * @return True if the specified type can hold the number or False otherwise. + */ + public static boolean isDecimal(Class clazz) { + if (clazz == null) { + return false; + } + + return clazz.equals(Float.class) || clazz.equals(Double.class); + } + //endregion } diff --git a/src/main/java/hrider/reflection/Compiler.java b/src/main/java/hrider/reflection/Compiler.java deleted file mode 100644 index f37719a..0000000 --- a/src/main/java/hrider/reflection/Compiler.java +++ /dev/null @@ -1,54 +0,0 @@ -package hrider.reflection; - -import hrider.config.GlobalConfig; -import hrider.io.PathHelper; - -import javax.tools.*; -import java.io.*; -import java.util.Arrays; - -public class Compiler { - - private Compiler() { - } - - public static Class compile(String name, String code) throws IOException, ClassNotFoundException { - String folder = GlobalConfig.instance().getCompilationFolder(); - - String path = name.replace('.', '/'); - File file = saveCode(PathHelper.append(folder, path), code); - - JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); - StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null); - - Iterable units = fileManager.getJavaFileObjectsFromFiles(Arrays.asList(file)); - boolean success = compiler.getTask(null, fileManager, null, null, null, units).call(); - if (success) { - ClassLoader classLoader = fileManager.getClassLoader(null); - return classLoader.loadClass(name); - } - - return null; - } - - private static File saveCode(String path, String code) throws IOException { - File file = new File(path); - if (!file.exists()) { - file.mkdirs(); - } - - FileOutputStream stream = null; - - try { - stream = new FileOutputStream(file); - stream.write(code.getBytes()); - - return file; - } - finally { - if (stream != null) { - stream.close(); - } - } - } -} diff --git a/src/main/java/hrider/reflection/JavaCompiler.java b/src/main/java/hrider/reflection/JavaCompiler.java new file mode 100644 index 0000000..71cd6de --- /dev/null +++ b/src/main/java/hrider/reflection/JavaCompiler.java @@ -0,0 +1,111 @@ +package hrider.reflection; + +import hrider.io.PathHelper; + +import javax.tools.*; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.*; + +/** + * Copyright (C) 2012 NICE Systems ltd. + *

+ * 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. + * + * @author Igor Cher + * @version %I%, %G% + *

+ * This class is responsible for compiling java code. + */ +public class JavaCompiler { + + //region Variables + private static List errors; + //endregion + + //region Constructor + static { + errors = new ArrayList(); + } + + private JavaCompiler() { + } + //endregion + + //region Public Properties + public static List getErrors() { + return errors; + } + //endregion + + //region Public Methods + public static boolean compile(File sourceCode, String outputFolder) throws IOException, FileNotFoundException { + + errors.clear(); + + // make sure all directories are created. + new File(outputFolder).mkdirs(); + + StandardJavaFileManager fileManager = null; + + try { + javax.tools.JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); + fileManager = compiler.getStandardFileManager(null, null, null); + + DiagnosticCollector diagnostics = new DiagnosticCollector(); + Iterable units = fileManager.getJavaFileObjectsFromFiles(Arrays.asList(sourceCode)); + + Collection options = new ArrayList(); + options.addAll(Arrays.asList("-classpath", System.getProperty("java.class.path"), "-d", outputFolder)); + + boolean success = compiler.getTask(null, fileManager, diagnostics, options, null, units).call(); + if (!success) { + for (Diagnostic diagnostic : diagnostics.getDiagnostics()) { + errors.add(diagnostic.getMessage(Locale.ENGLISH)); + } + } + + return success; + } + finally { + if (fileManager != null) { + fileManager.close(); + } + } + } + + public static File saveCode(String className, String code, String outputFolder) throws IOException, FileNotFoundException { + // make sure all directories are created. + new File(outputFolder).mkdirs(); + + String path = PathHelper.append(outputFolder, className + ".java"); + File file = new File(path); + + FileOutputStream stream = null; + + try { + stream = new FileOutputStream(file); + stream.write(code.getBytes()); + + return file; + } + finally { + if (stream != null) { + stream.close(); + } + } + } + //endregion +} diff --git a/src/main/java/hrider/reflection/JavaPackage.java b/src/main/java/hrider/reflection/JavaPackage.java new file mode 100644 index 0000000..b128171 --- /dev/null +++ b/src/main/java/hrider/reflection/JavaPackage.java @@ -0,0 +1,226 @@ +package hrider.reflection; + +import hrider.io.Log; +import hrider.io.PathHelper; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.net.URL; +import java.util.Collection; +import java.util.Enumeration; +import java.util.HashSet; +import java.util.jar.JarEntry; +import java.util.jar.JarInputStream; +import java.util.regex.Pattern; + +/** + * Copyright (C) 2012 NICE Systems ltd. + *

+ * 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. + * + * @author Igor Cher + * @version %I%, %G% + *

+ * This class is a helper class to work with java packages. + */ +public class JavaPackage { + + //region Constants + private static Log logger = Log.getLogger(JavaPackage.class); + private static final Pattern NUMBER_IN_CLASS_NAME = Pattern.compile("[$].*"); + //endregion + + //region Constructor + private JavaPackage() { + } + //endregion + + //region Public Methods + + /** + * Collects all classes located in the provided package. + * + * @param packageName The name of the package. + * @return A list of classes if found or an empty list otherwise. + * @throws IOException Error accessing classes on a file system. + * @throws ClassNotFoundException Class cannot be found. + * @throws FileNotFoundException File cannot be found. + */ + public static Collection> getClasses(String packageName) throws IOException, ClassNotFoundException, FileNotFoundException { + return getClasses(Thread.currentThread().getContextClassLoader(), packageName); + } + + /** + * Collects all classes located in the provided package. + * + * @param loader The class loader to use for classes. + * @param packageName The name of the package. + * @return A list of classes if found or an empty list otherwise. + * @throws IOException Error accessing classes on a file system. + * @throws ClassNotFoundException Class cannot be found. + * @throws FileNotFoundException File cannot be found. + */ + public static Collection> getClasses(ClassLoader loader, String packageName) throws IOException, ClassNotFoundException, FileNotFoundException { + logger.info("Loading classes from package '%s'", packageName); + + Collection> classes = new HashSet>(); + String path = packageName.replace(".", "/"); + + Enumeration resources = loader.getResources(path + '/'); + while (resources.hasMoreElements()) { + URL url = resources.nextElement(); + String filePath = getPath(url); + + logger.debug("Resource URL: %s", url.toString()); + logger.debug("Normalized path: %s", filePath); + + if (filePath.endsWith(".jar")) { + classes.addAll(getClassesFromJar(loader, new File(filePath), packageName)); + } + else { + classes.addAll(getClassesFromFolder(loader, new File(filePath), packageName)); + } + } + return classes; + } + + /** + * Collects all classes located in the provided directory under the specified package. + * + * @param folder The directory to search for the classes. + * @param packageName The name of the package. + * @return A list of classes if found or an empty list otherwise. + * @throws ClassNotFoundException Class cannot be found. + */ + public static Collection> getClassesFromFolder(File folder, String packageName) throws ClassNotFoundException { + return getClassesFromFolder(Thread.currentThread().getContextClassLoader(), folder, packageName); + } + + /** + * Collects all classes located in the provided directory under the specified package. + * + * @param loader The class loader to use for classes. + * @param folder The directory to search for the classes. + * @param packageName The name of the package. + * @return A list of classes if found or an empty list otherwise. + * @throws ClassNotFoundException Class cannot be found. + */ + public static Collection> getClassesFromFolder(ClassLoader loader, File folder, String packageName) throws ClassNotFoundException { + logger.info("Loading classes from folder '%s'", folder.getAbsolutePath()); + + Collection> classes = new HashSet>(); + Collection loadedClasses = new HashSet(); + + if (folder.exists()) { + for (String fileName : folder.list()) { + File file = new File(PathHelper.append(folder.getAbsolutePath(), fileName)); + if (file.isDirectory()) { + classes.addAll(getClassesFromFolder(loader, file, packageName)); + } + else { + if (fileName.endsWith(".class")) { + String className = packageName + '.' + + PathHelper.getPathWithoutExtension(NUMBER_IN_CLASS_NAME.matcher(fileName).replaceAll("")); + + if (!loadedClasses.contains(className)) { + logger.info("Loading class '%s'", className); + + loadedClasses.add(className); + classes.add(loader.loadClass(className)); + } + } + } + } + } + + return classes; + } + + /** + * Collects all classes located in the provided jar file under the specified package. + * + * @param jarFile The path to the jar file. + * @param packageName The name of the package to look into. + * @return A list of classes if found or an empty list otherwise. + * @throws IOException Error accessing classes on a file system. + * @throws ClassNotFoundException Class cannot be found. + * @throws FileNotFoundException File cannot be found. + */ + public static Collection> getClassesFromJar(File jarFile, String packageName) throws IOException, ClassNotFoundException, FileNotFoundException { + return getClassesFromJar(Thread.currentThread().getContextClassLoader(), jarFile, packageName); + } + + /** + * Collects all classes located in the provided jar file under the specified package. + * + * @param loader The class loader to use for classes. + * @param jarFile The path to the jar file. + * @param packageName The name of the package to look into. + * @return A list of classes if found or an empty list otherwise. + * @throws IOException Error accessing classes on a file system. + * @throws ClassNotFoundException Class cannot be found. + * @throws FileNotFoundException File cannot be found. + */ + public static Collection> getClassesFromJar(ClassLoader loader, File jarFile, String packageName) throws IOException, ClassNotFoundException, + FileNotFoundException { + + logger.info("Loading classes from jar '%s'", jarFile.getAbsolutePath()); + + Collection> classes = new HashSet>(); + Collection loadedClasses = new HashSet(); + + if (jarFile.exists()) { + JarInputStream stream = null; + try { + stream = new JarInputStream(new FileInputStream(jarFile)); + + JarEntry entry; + while ((entry = stream.getNextJarEntry()) != null) { + if (entry.getName().endsWith(".class")) { + String className = PathHelper.getPathWithoutExtension(NUMBER_IN_CLASS_NAME.matcher(entry.getName()).replaceAll("")).replace('/', '.'); + + if (className.startsWith(packageName) && !loadedClasses.contains(className)) { + logger.info("Loading class '%s'", className); + + loadedClasses.add(className); + classes.add(loader.loadClass(className)); + } + } + } + } + finally { + if (stream != null) { + stream.close(); + } + } + } + + return classes; + } + //endregion + + //region Private Methods + private static String getPath(URL url) { + String path = PathHelper.normalise(url.getFile()); + + int index = path.indexOf('!'); + if (index != -1) { + path = path.substring(0, index); + } + + return path; + } + //endregion +} diff --git a/src/main/java/hrider/system/Clipboard.java b/src/main/java/hrider/system/Clipboard.java index 9bbbee9..f4f920b 100644 --- a/src/main/java/hrider/system/Clipboard.java +++ b/src/main/java/hrider/system/Clipboard.java @@ -36,6 +36,7 @@ private Clipboard() { /** * Checks if the clipboard has text. + * * @return True if there is any text in the system clipboard or False otherwise. */ public static boolean hasText() { @@ -51,6 +52,7 @@ public static boolean hasText() { /** * Sets a provided text to the system clipboard. + * * @param text The text to set. */ public static void setText(String text) { @@ -59,12 +61,13 @@ public static void setText(String text) { /** * Gets a text from the system clipboard. + * * @return A text if there is any or a null. */ public static String getText() { Transferable data = Toolkit.getDefaultToolkit().getSystemClipboard().getContents(null); try { - return (String)data.getTransferData(DataFlavor.stringFlavor); + return (String)data.getTransferData(DataFlavor.stringFlavor); } catch (Exception ignore) { return null; diff --git a/src/main/java/hrider/system/Version.java b/src/main/java/hrider/system/Version.java new file mode 100644 index 0000000..29363d1 --- /dev/null +++ b/src/main/java/hrider/system/Version.java @@ -0,0 +1,77 @@ +package hrider.system; + +import java.util.Arrays; +import java.util.regex.Pattern; + +/** + * Copyright (C) 2012 NICE Systems ltd. + *

+ * 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. + * + * @author Igor Cher + * @version %I%, %G% + */ +public class Version implements Comparable { + + //region Constants + private static final Pattern DELIMITER = Pattern.compile("\\."); + //endregion + + //region Variables + private int[] version; + //endregion + + //region Public Methods + public static int compare(String ver1, String ver2) { + Version version1 = new Version(ver1); + Version version2 = new Version(ver2); + + return version1.compareTo(version2); + } + + public Version(String version) { + String[] parts = DELIMITER.split(version); + if (parts.length < 2 && parts.length > 4) { + throw new IllegalArgumentException(String.format("incorrect version format: %s", version)); + } + + this.version = new int[] {0, 0, 0, 0}; + for (int i = 0; i < parts.length; i++) { + this.version[i] = Integer.parseInt(parts[i]); + } + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof Version) { + return Arrays.equals(version, ((Version)obj).version); + } + return false; + } + + @Override + public int hashCode() { + return Arrays.hashCode(version); + } + + @Override + public int compareTo(Version o) { + for (int i = 0; i < version.length && i < o.version.length; i++) { + if (version[i] != o.version[i]) { + return version[i] - o.version[i]; + } + } + return 0; + } + //endregion +} diff --git a/src/main/java/hrider/ui/ChangeTracker.java b/src/main/java/hrider/ui/ChangeTracker.java index 3597690..cd31ac1 100644 --- a/src/main/java/hrider/ui/ChangeTracker.java +++ b/src/main/java/hrider/ui/ChangeTracker.java @@ -3,6 +3,7 @@ import hrider.data.DataCell; import hrider.data.DataRow; +import java.io.Serializable; import java.util.ArrayList; import java.util.Collection; @@ -26,7 +27,11 @@ *

* This class represents a tracker for all changes performed on cells. */ -public class ChangeTracker { +public class ChangeTracker implements Serializable { + + //region Constants + private static final long serialVersionUID = -7106669090052759327L; + //endregion //region Variables /** diff --git a/src/main/java/hrider/ui/MessageHandler.java b/src/main/java/hrider/ui/MessageHandler.java index 70ac7c1..8e74374 100644 --- a/src/main/java/hrider/ui/MessageHandler.java +++ b/src/main/java/hrider/ui/MessageHandler.java @@ -1,5 +1,7 @@ package hrider.ui; +import hrider.io.Log; + import java.util.ArrayList; import java.util.Collection; @@ -26,6 +28,8 @@ */ public class MessageHandler { + private final static Log logger = Log.getLogger(MessageHandler.class); + /** * A list of registered listeners. */ @@ -45,6 +49,10 @@ private MessageHandler() { * @param ex The exception. */ public static void addError(String message, Exception ex) { + if (message != null && !message.isEmpty()) { + logger.error(ex, message); + } + for (MessageHandlerListener listener : listeners) { listener.onError(message, ex); } @@ -57,11 +65,27 @@ public static void addError(String message, Exception ex) { * @param message The message to report. */ public static void addInfo(String message) { + if (message != null && !message.isEmpty()) { + logger.info(message); + } + for (MessageHandlerListener listener : listeners) { listener.onInfo(message); } } + /** + * This method is called by the component that wants to report to the user additional information with the option to perform + * an action. + * + * @param action The action to execute if clicked. + */ + public static void addAction(UIAction action) { + for (MessageHandlerListener listener : listeners) { + listener.onAction(action); + } + } + /** * Adds a listener to the list of registered listeners. * diff --git a/src/main/java/hrider/ui/MessageHandlerListener.java b/src/main/java/hrider/ui/MessageHandlerListener.java index 312ca1e..466a4c8 100644 --- a/src/main/java/hrider/ui/MessageHandlerListener.java +++ b/src/main/java/hrider/ui/MessageHandlerListener.java @@ -36,4 +36,11 @@ public interface MessageHandlerListener { * @param ex An exception. */ void onError(String message, Exception ex); + + /** + * The method is called when a component wants to show the user message with the action. + * + * @param action The action to execute if clicked. + */ + void onAction(UIAction action); } diff --git a/src/main/java/hrider/ui/UIAction.java b/src/main/java/hrider/ui/UIAction.java new file mode 100644 index 0000000..d9ff024 --- /dev/null +++ b/src/main/java/hrider/ui/UIAction.java @@ -0,0 +1,44 @@ +package hrider.ui; + +/** + * Copyright (C) 2012 NICE Systems ltd. + *

+ * 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. + * + * @author Igor Cher + * @version %I%, %G% + */ +public interface UIAction { + + /** + * Executes the UI action. + */ + void execute(); + + /** + * Gets a formatted message. The array might contain 1-3 elements where: + * 1. The first part of the message if exist. + * 2. The word/expression that describes the action. + * 3. The last part of the message if exist. + * + * For example: + * new String[]{ + * "The selected table is disabled, do you want to", + * "enable", + * "it?" + * }; + * + * @return A three part message. + */ + String[] getFormattedMessage(); +} diff --git a/src/main/java/hrider/ui/controls/WideComboBox.java b/src/main/java/hrider/ui/controls/WideComboBox.java new file mode 100644 index 0000000..9aba6a9 --- /dev/null +++ b/src/main/java/hrider/ui/controls/WideComboBox.java @@ -0,0 +1,50 @@ +package hrider.ui.controls; + +import javax.swing.*; +import java.awt.*; + +public class WideComboBox extends JComboBox { + + //region Constants + private static final long serialVersionUID = -2142335227903444963L; + //endregion + + //region Variables + private boolean layingOut; + //endregion + + //region Constructor + public WideComboBox() { + } + + public WideComboBox(Object[] items) { + super(items); + } + + public WideComboBox(ComboBoxModel model) { + super(model); + } + //endregion + + //region Public Methods + @Override + public void doLayout() { + try { + layingOut = true; + super.doLayout(); + } + finally { + layingOut = false; + } + } + + @Override + public Dimension getSize() { + Dimension dim = super.getSize(); + if (!layingOut) { + dim.width = Math.max(dim.width, getPreferredSize().width); + } + return dim; + } + //endregion +} diff --git a/src/main/java/hrider/ui/controls/json/JsonEditor.java b/src/main/java/hrider/ui/controls/json/JsonEditor.java index d791995..26d5390 100644 --- a/src/main/java/hrider/ui/controls/json/JsonEditor.java +++ b/src/main/java/hrider/ui/controls/json/JsonEditor.java @@ -50,7 +50,7 @@ public class JsonEditor extends JPanel { /** * Initializes a new instance of the {@link JsonEditor} class. */ - public JsonEditor() { + public JsonEditor(final CellEditor cellEditor) { this.textPane = new JsonTextPane(); this.textPane.setLayout(new BorderLayout()); @@ -70,8 +70,8 @@ public JsonEditor() { JPanel buttonsPanel = new JPanel(); buttonsPanel.setBorder(new EmptyBorder(0, 0, 0, 0)); - JButton saveButton = new JButton("Save"); - saveButton.setPreferredSize(new Dimension(75, 24)); + JButton saveButton = new JButton("Mark for save"); + saveButton.setPreferredSize(new Dimension(115, 24)); saveButton.addActionListener( new ActionListener() { @Override @@ -81,6 +81,7 @@ public void actionPerformed(ActionEvent e) { JsonEditor.this.textPane.validateJson(); JsonEditor.this.textField.setText(JsonEditor.this.textPane.getText()); + cellEditor.getCellEditorValue(); } catch (JsonSyntaxException ex) { JOptionPane.showMessageDialog(JsonEditor.this, ex.getMessage(), "Invalid JSON", JOptionPane.ERROR_MESSAGE); diff --git a/src/main/java/hrider/ui/controls/json/JsonTextPane.java b/src/main/java/hrider/ui/controls/json/JsonTextPane.java index c0078eb..7041259 100644 --- a/src/main/java/hrider/ui/controls/json/JsonTextPane.java +++ b/src/main/java/hrider/ui/controls/json/JsonTextPane.java @@ -3,6 +3,7 @@ import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.google.gson.JsonParser; +import hrider.io.Log; import javax.swing.*; @@ -29,6 +30,7 @@ public class JsonTextPane extends JTextPane { //region Constants + private static final Log logger = Log.getLogger(JsonTextPane.class); private static final long serialVersionUID = 6270183148379328084L; //endregion @@ -41,6 +43,7 @@ public JsonTextPane() { /** * Replaces the content of the text pane with the provided text. + * * @param t The text to set. */ @Override @@ -65,6 +68,7 @@ public void validateJson() { /** * Formats JSON. + * * @param json A JSON to format. * @return A formatted JSON. */ @@ -73,7 +77,8 @@ private static String formatJson(String json) { Gson gson = new GsonBuilder().disableHtmlEscaping().setPrettyPrinting().create(); return gson.toJson(new JsonParser().parse(json)); } - catch (Exception ignore) { + catch (Exception e) { + logger.error(e, "Failed to format json '%s'.", json); return json; } } diff --git a/src/main/java/hrider/ui/controls/xml/XmlEditor.java b/src/main/java/hrider/ui/controls/xml/XmlEditor.java index 37f3f4e..2a5fdef 100644 --- a/src/main/java/hrider/ui/controls/xml/XmlEditor.java +++ b/src/main/java/hrider/ui/controls/xml/XmlEditor.java @@ -48,7 +48,7 @@ public class XmlEditor extends JPanel { /** * Initializes a new instance of the {@link XmlEditor} class. */ - public XmlEditor() { + public XmlEditor(final CellEditor cellEditor) { this.textPane = new XmlTextPane(); this.textPane.setLayout(new BorderLayout()); @@ -68,8 +68,8 @@ public XmlEditor() { JPanel buttonsPanel = new JPanel(); buttonsPanel.setBorder(new EmptyBorder(0, 0, 0, 0)); - JButton saveButton = new JButton("Save"); - saveButton.setPreferredSize(new Dimension(75, 24)); + JButton saveButton = new JButton("Mark for save"); + saveButton.setPreferredSize(new Dimension(115, 24)); saveButton.addActionListener( new ActionListener() { @Override @@ -79,6 +79,7 @@ public void actionPerformed(ActionEvent e) { XmlEditor.this.textPane.validateXml(); XmlEditor.this.textField.setText(XmlEditor.this.textPane.getText()); + cellEditor.getCellEditorValue(); } catch (Exception ex) { JOptionPane.showMessageDialog(XmlEditor.this, ex.getMessage(), "Invalid XML", JOptionPane.ERROR_MESSAGE); diff --git a/src/main/java/hrider/ui/controls/xml/XmlTextPane.java b/src/main/java/hrider/ui/controls/xml/XmlTextPane.java index 525322c..6485a84 100644 --- a/src/main/java/hrider/ui/controls/xml/XmlTextPane.java +++ b/src/main/java/hrider/ui/controls/xml/XmlTextPane.java @@ -2,6 +2,7 @@ import com.sun.org.apache.xml.internal.serialize.OutputFormat; import com.sun.org.apache.xml.internal.serialize.XMLSerializer; +import hrider.io.Log; import org.w3c.dom.Document; import org.xml.sax.InputSource; import org.xml.sax.SAXException; @@ -13,7 +14,6 @@ import java.io.IOException; import java.io.StringReader; import java.io.StringWriter; -import java.io.Writer; /** * Copyright (C) 2012 NICE Systems ltd. @@ -38,7 +38,9 @@ public class XmlTextPane extends JTextPane { //region Constants + private static final Log logger = Log.getLogger(XmlTextPane.class); private static final long serialVersionUID = 6270183148379328084L; + private static final int LINE_WIDTH = 500; //endregion //region Constructor @@ -99,16 +101,17 @@ private static String formatXml(String xml) { OutputFormat format = new OutputFormat(document); format.setIndenting(true); format.setIndent(4); - format.setLineWidth(500); + format.setLineWidth(LINE_WIDTH); - Writer out = new StringWriter(); + StringWriter out = new StringWriter(); XMLSerializer serializer = new XMLSerializer(out, format); serializer.serialize(document); return out.toString(); } - catch (Exception ignore) { + catch (Exception e) { + logger.error(e, "Failed to format XML '%s'.", xml); return xml; } } diff --git a/src/main/java/hrider/ui/design/JCellEditor.java b/src/main/java/hrider/ui/design/JCellEditor.java index d9210e7..017d8e7 100644 --- a/src/main/java/hrider/ui/design/JCellEditor.java +++ b/src/main/java/hrider/ui/design/JCellEditor.java @@ -2,8 +2,9 @@ import com.michaelbaranov.microba.calendar.DatePicker; import hrider.config.GlobalConfig; +import hrider.data.ColumnType; import hrider.data.DataCell; -import hrider.data.ObjectType; +import hrider.io.Log; import hrider.ui.ChangeTracker; import hrider.ui.controls.json.JsonEditor; import hrider.ui.controls.xml.XmlEditor; @@ -11,7 +12,6 @@ import javax.swing.*; import javax.swing.table.TableCellEditor; import java.awt.*; -import java.beans.PropertyVetoException; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.Date; @@ -41,6 +41,7 @@ public class JCellEditor extends AbstractCellEditor implements TableCellEditor { //region Constants + private static final Log logger = Log.getLogger(JCellEditor.class); private static final long serialVersionUID = -2190137522499893284L; //endregion @@ -70,9 +71,9 @@ public JCellEditor(ChangeTracker changeTracker, int typeColumn, boolean canEdit) this.dateEditor.setBorder(BorderFactory.createEmptyBorder()); this.dateEditor.setDateFormat(new SimpleDateFormat(GlobalConfig.instance().getDateFormat(), Locale.ENGLISH)); this.dateEditor.setFieldEditable(canEdit); - this.xmlEditor = new XmlEditor(); + this.xmlEditor = new XmlEditor(this); this.xmlEditor.setEditable(canEdit); - this.jsonEditor = new JsonEditor(); + this.jsonEditor = new JsonEditor(this); this.jsonEditor.setEditable(canEdit); } //endregion @@ -105,28 +106,28 @@ public void setEditable(boolean editable) { public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) { this.editorType = EditorType.Text; - ObjectType type = ObjectType.String; + ColumnType type = ColumnType.String; // Check if the value contains information regarding its type. if (value instanceof DataCell) { this.cell = (DataCell)value; - type = this.cell.getTypedValue().getType(); + type = this.cell.getType(); } else { this.cell = null; } if (this.typeColumn != -1) { - type = (ObjectType)table.getValueAt(row, this.typeColumn); + type = (ColumnType)table.getValueAt(row, this.typeColumn); } - if (type == ObjectType.DateAsString || type == ObjectType.DateAsLong) { + if (type.equals(ColumnType.DateAsString) || type.equals(ColumnType.DateAsLong)) { this.editorType = EditorType.Date; } - else if (type == ObjectType.Xml) { + else if (type.equals(ColumnType.Xml)) { this.editorType = EditorType.Xml; } - else if (type == ObjectType.Json) { + else if (type.equals(ColumnType.Json)) { this.editorType = EditorType.Json; } @@ -169,9 +170,8 @@ public Object getCellEditorValue() { } if (this.cell != null) { - Object value = this.cell.toObject(text); - if (value == null || !this.cell.contains(value)) { - this.cell.getTypedValue().setValue(value); + if (!this.cell.hasValue(text)) { + this.cell.setValue(text); if (this.changeTracker != null) { this.changeTracker.addChange(this.cell); @@ -194,12 +194,15 @@ public Object getCellEditorValue() { private void initializeEditor(Object value) { switch (this.editorType) { case Date: - try { - if (this.cell != null) { - this.dateEditor.setDate((Date)this.cell.getTypedValue().getValue()); + if (this.cell != null) { + DateFormat df = new SimpleDateFormat(GlobalConfig.instance().getDateFormat(), Locale.ENGLISH); + try { + Date date = df.parse(this.cell.getValue()); + this.dateEditor.setDate(date); + } + catch (Exception e) { + logger.error(e, "Failed to convert value to Date.", value); } - } - catch (PropertyVetoException ignore) { } break; case Text: diff --git a/src/main/java/hrider/ui/design/JListRenderer.java b/src/main/java/hrider/ui/design/JListRenderer.java new file mode 100644 index 0000000..20b9944 --- /dev/null +++ b/src/main/java/hrider/ui/design/JListRenderer.java @@ -0,0 +1,34 @@ +package hrider.ui.design; + +import hrider.hbase.Connection; + +import javax.swing.*; +import java.awt.*; +import java.io.IOException; + +public class JListRenderer extends DefaultListCellRenderer { + + private static final long serialVersionUID = 8219559461829225540L; + + private Connection connection; + + public JListRenderer(Connection connection) { + this.connection = connection; + } + + @Override + public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { + Component component = super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); + if (value instanceof String) { + try { + boolean enabled = connection.tableEnabled((String)value); + if (!enabled) { + setForeground(Color.gray); + } + } + catch (IOException ignore) { + } + } + return component; + } +} diff --git a/src/main/java/hrider/ui/forms/AddColumnDialog.java b/src/main/java/hrider/ui/forms/AddColumnDialog.java index 632c2e9..f2817e2 100644 --- a/src/main/java/hrider/ui/forms/AddColumnDialog.java +++ b/src/main/java/hrider/ui/forms/AddColumnDialog.java @@ -4,6 +4,7 @@ import com.intellij.uiDesigner.core.GridLayoutManager; import hrider.data.ColumnFamily; import hrider.data.ColumnQualifier; +import hrider.data.ColumnType; import hrider.ui.design.JCellEditor; import hrider.ui.design.JTableModel; @@ -13,7 +14,6 @@ import javax.swing.event.TableModelEvent; import javax.swing.event.TableModelListener; import javax.swing.table.DefaultTableModel; -import javax.swing.table.TableCellEditor; import java.awt.*; import java.awt.event.*; import java.util.Map; @@ -256,7 +256,8 @@ public boolean showDialog(Component owner) { public ColumnQualifier getColumn() { if (this.okPressed) { - return new ColumnQualifier(this.columnNameTextField.getText().trim(), (ColumnFamily)this.comboBox.getSelectedItem()); + return new ColumnQualifier( + this.columnNameTextField.getText(), (ColumnFamily)this.comboBox.getSelectedItem(), ColumnType.BinaryString.getConverter()); } return null; } diff --git a/src/main/java/hrider/ui/forms/AddRowDialog.java b/src/main/java/hrider/ui/forms/AddRowDialog.java index ff1030c..c43408c 100644 --- a/src/main/java/hrider/ui/forms/AddRowDialog.java +++ b/src/main/java/hrider/ui/forms/AddRowDialog.java @@ -3,13 +3,13 @@ import com.intellij.uiDesigner.core.GridConstraints; import com.intellij.uiDesigner.core.GridLayoutManager; import hrider.data.*; +import hrider.ui.controls.WideComboBox; import hrider.ui.design.JCellEditor; import hrider.ui.design.JCheckBoxRenderer; import hrider.ui.design.JTableModel; import javax.swing.*; import javax.swing.table.DefaultTableModel; -import javax.swing.table.TableCellEditor; import java.awt.*; import java.awt.event.*; @@ -65,10 +65,10 @@ public AddRowDialog(Iterable columns, final Iterable this.rowsTable.getColumn("Use").setCellEditor(new JCheckBoxRenderer(new CheckedRow(1, ColumnQualifier.KEY))); this.rowsTable.getColumn("Use").setPreferredWidth(20); - JComboBox comboBox = new JComboBox(); + JComboBox comboBox = new WideComboBox(); - for (ObjectType objectType : ObjectType.values()) { - comboBox.addItem(objectType); + for (ColumnType columnType : ColumnType.getTypes()) { + comboBox.addItem(columnType); } this.rowsTable.getColumn("Column Type").setCellEditor(new DefaultCellEditor(comboBox)); @@ -128,7 +128,7 @@ public void actionPerformed(ActionEvent e) { int rowIndex = getRowIndex(rowsTable, 1, column); if (rowIndex == -1) { - tableModel.addRow(new Object[]{Boolean.TRUE, column, ObjectType.String, null}); + tableModel.addRow(new Object[]{Boolean.TRUE, column, ColumnType.String, null}); rowIndex = tableModel.getRowCount() - 1; } @@ -156,14 +156,14 @@ public DataRow getRow() { boolean use = (Boolean)this.rowsTable.getValueAt(i, 0); if (use) { ColumnQualifier columnQualifier = (ColumnQualifier)this.rowsTable.getValueAt(i, 1); - ObjectType columnType = (ObjectType)this.rowsTable.getValueAt(i, 2); - Object value = columnType.fromString((String)this.rowsTable.getValueAt(i, 3)); + ColumnType columnType = (ColumnType)this.rowsTable.getValueAt(i, 2); + byte[] value = columnType.toBytes((String)this.rowsTable.getValueAt(i, 3)); if (columnQualifier.isKey()) { - row.setKey(new TypedObject(columnType, value)); + row.setKey(new ConvertibleObject(columnType, value)); } - row.addCell(new DataCell(row, columnQualifier, new TypedObject(columnType, value))); + row.addCell(new DataCell(row, columnQualifier, new ConvertibleObject(columnType, value))); } } return row; @@ -209,9 +209,9 @@ private void onOK() { if (use) { value = (String)this.rowsTable.getValueAt(i, 3); qualifier = (ColumnQualifier)this.rowsTable.getValueAt(i, 1); - ObjectType valueType = (ObjectType)this.rowsTable.getValueAt(i, 2); + ColumnType valueType = (ColumnType)this.rowsTable.getValueAt(i, 2); - valueType.toObject(value); + valueType.toBytes(value); } } diff --git a/src/main/java/hrider/ui/forms/ConnectionDetailsDialog.form b/src/main/java/hrider/ui/forms/ConnectionDetailsDialog.form index f211e3f..d598d46 100644 --- a/src/main/java/hrider/ui/forms/ConnectionDetailsDialog.form +++ b/src/main/java/hrider/ui/forms/ConnectionDetailsDialog.form @@ -8,7 +8,7 @@ - + @@ -16,10 +16,10 @@ - + - + @@ -44,7 +44,7 @@ - + diff --git a/src/main/java/hrider/ui/forms/ConnectionDetailsDialog.java b/src/main/java/hrider/ui/forms/ConnectionDetailsDialog.java index df37a21..e8cccbf 100644 --- a/src/main/java/hrider/ui/forms/ConnectionDetailsDialog.java +++ b/src/main/java/hrider/ui/forms/ConnectionDetailsDialog.java @@ -32,6 +32,8 @@ public class ConnectionDetailsDialog extends JDialog { //region Variables + private static final long serialVersionUID = 4254076848011995814L; + private JPanel contentPane; private JButton buttonConnect; private JButton buttonCancel; @@ -104,34 +106,34 @@ public ConnectionDetails getConnectionDetails() { //region Private Methods private void onOK() { - this.contentPane.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); - this.connectionDetails = new ConnectionDetails() {{ setZookeeper( new ServerDetails( ConnectionDetailsDialog.this.zooKeeperServer.getText(), ConnectionDetailsDialog.this.zooKeeperPort.getValue().toString())); }}; - try { + this.contentPane.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); - ConnectionManager.create(this.connectionDetails); + boolean canConnect = false; - GlobalConfig.instance().set("connection.zookeeper.defaultPort", this.zooKeeperPort.getValue().toString()); - GlobalConfig.instance().save(); + try { + canConnect = this.connectionDetails.canConnect(); + } + finally { + this.contentPane.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); + } + if (canConnect) { dispose(); } - catch (Exception ex) { + else { JOptionPane.showMessageDialog( - this, String.format( - "%s\n\nMake sure you have access to all nodes of the cluster you try\nto connect to. In case you don't, map the nodes in your hosts file.", - ex.getMessage()), "Failed to connect...", JOptionPane.ERROR_MESSAGE); + ConnectionDetailsDialog.this, + "Failed to connect to hbase.\n\nMake sure you have access to all hadoop nodes\nIn case you don't, map the nodes in your hosts file.", + "Connection failed...", JOptionPane.ERROR_MESSAGE); - ConnectionManager.release(this.connectionDetails); - this.connectionDetails = null; - } - finally { - this.contentPane.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); + ConnectionManager.release(connectionDetails); + connectionDetails = null; } } @@ -159,16 +161,16 @@ private void onCancel() { contentPane = new JPanel(); contentPane.setLayout(new GridLayoutManager(2, 1, new Insets(10, 10, 10, 10), -1, -1)); final JPanel panel1 = new JPanel(); - panel1.setLayout(new GridLayoutManager(2, 1, new Insets(5, 0, 0, 0), -1, -1)); + panel1.setLayout(new GridLayoutManager(2, 2, new Insets(5, 0, 0, 0), -1, -1)); contentPane.add( panel1, new GridConstraints( 1, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, 1, null, null, null, 0, false)); final JPanel panel2 = new JPanel(); - panel2.setLayout(new GridLayoutManager(1, 2, new Insets(0, 0, 0, 0), -1, -1, true, false)); + panel2.setLayout(new GridLayoutManager(1, 2, new Insets(0, 0, 0, 0), -1, -1)); panel1.add( panel2, new GridConstraints( - 1, 0, 1, 1, GridConstraints.ANCHOR_EAST, GridConstraints.FILL_VERTICAL, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, + 1, 1, 1, 1, GridConstraints.ANCHOR_EAST, GridConstraints.FILL_VERTICAL, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, null, null, 0, false)); buttonConnect = new JButton(); buttonConnect.setText("Connect"); @@ -185,7 +187,7 @@ buttonCancel, new GridConstraints( final JSeparator separator1 = new JSeparator(); panel1.add( separator1, new GridConstraints( - 0, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_WANT_GROW, + 0, 0, 1, 2, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_WANT_GROW, null, null, null, 0, false)); final JPanel panel3 = new JPanel(); panel3.setLayout(new GridLayoutManager(1, 4, new Insets(0, 0, 0, 0), -1, -1)); diff --git a/src/main/java/hrider/ui/forms/CustomConverterDialog.form b/src/main/java/hrider/ui/forms/CustomConverterDialog.form new file mode 100644 index 0000000..f2e4e1c --- /dev/null +++ b/src/main/java/hrider/ui/forms/CustomConverterDialog.form @@ -0,0 +1,202 @@ + +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/src/main/java/hrider/ui/forms/CustomConverterDialog.java b/src/main/java/hrider/ui/forms/CustomConverterDialog.java new file mode 100644 index 0000000..e30847e --- /dev/null +++ b/src/main/java/hrider/ui/forms/CustomConverterDialog.java @@ -0,0 +1,449 @@ +package hrider.ui.forms; + +import com.intellij.uiDesigner.core.GridConstraints; +import com.intellij.uiDesigner.core.GridLayoutManager; +import com.intellij.uiDesigner.core.Spacer; +import hrider.config.GlobalConfig; +import hrider.converters.TypeConverter; +import hrider.io.Log; +import hrider.io.PathHelper; +import hrider.reflection.JavaCompiler; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.*; +import java.io.File; +import java.util.regex.Pattern; + +public class CustomConverterDialog extends JDialog { + + //region Constants + private final static String IMPORTS_PLACE_HOLDER = "$$IMPORTS_PLACE_HOLDER$$"; + private final static String CONVERTER_NAME_PLACE_HOLDER = "$$CONVERTER_NAME_PLACE_HOLDER$$"; + private final static String BYTES_TO_STRING_CODE_PLACE_HOLDER = "$$BYTES_TO_STRING_CODE_PLACE_HOLDER$$"; + private final static String STRING_TO_BYTES_CODE_PLACE_HOLDER = "$$STRING_TO_BYTES_CODE_PLACE_HOLDER$$"; + private final static String IS_VALID_FOR_NAME_CONVERSION_PLACE_HOLDER = "$$IS_VALID_FOR_NAME_CONVERSION_PLACE_HOLDER$$"; + + private final static String TEMPLATE = "package hrider.converters.custom;\n" + + '\n' + + "$$IMPORTS_PLACE_HOLDER$$\n" + + '\n' + + "public class $$CONVERTER_NAME_PLACE_HOLDER$$ extends hrider.converters.TypeConverter {\n" + + '\n' + + " /**\n" + + " * Converts an array of bytes to String.\n" + + " */\n" + + " @Override\n" + + " public String toString(byte[] value) {\n" + + " if (value == null) {\n" + + " return null;\n" + + " }\n" + + "$$BYTES_TO_STRING_CODE_PLACE_HOLDER$$\n" + + " }\n" + + '\n' + + " /**\n" + + " * Converts a String to an array of bytes.\n" + + " */\n" + + " @Override\n" + + " public byte[] toBytes(String value) {\n" + + " if (value == null) {\n" + + " return EMPTY_BYTES_ARRAY;\n" + + " }\n" + + "$$STRING_TO_BYTES_CODE_PLACE_HOLDER$$\n" + + " }\n" + + '\n' + + " /**\n" + + " * Indicates whether the type converter can be used for column name conversions.\n" + + " */\n" + + " @Override\n" + + " public boolean isValidForNameConversion() {\n" + + " return $$IS_VALID_FOR_NAME_CONVERSION_PLACE_HOLDER$$;\n" + + " }" + + "}\n"; + + private static final long serialVersionUID = -8239690036592242474L; + private final static Log logger = Log.getLogger(PathHelper.class); + //endregion + + //region Variables + private JPanel contentPane; + private JButton buttonOK; + private JButton buttonCancel; + private JTextField tfConverterName; + private JTextArea taBytesToObjectMethod; + private JTextArea taImports; + private JTextArea taObjectToBytesMethod; + private JCheckBox chbNameConverter; + private boolean okPressed; + //endregion + + //region Constructor + public CustomConverterDialog(TypeConverter converter) { + setContentPane(contentPane); + setModal(true); + setTitle(converter == null ? "Create custom type converter" : "Edit custom type converter"); + getRootPane().setDefaultButton(buttonOK); + + if (converter != null) { + tfConverterName.setText(converter.getClass().getSimpleName()); + chbNameConverter.setSelected(converter.isValidForNameConversion()); + + loadImports(converter.getCode()); + loadMethods(converter.getCode()); + } + else { + tfConverterName.select(0, 6); + } + + buttonOK.addActionListener( + new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + onOK(); + } + }); + + buttonCancel.addActionListener( + new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + onCancel(); + } + }); + + // call onCancel() when cross is clicked + setDefaultCloseOperation(DO_NOTHING_ON_CLOSE); + addWindowListener( + new WindowAdapter() { + @Override + public void windowClosing(WindowEvent e) { + onCancel(); + } + }); + + // call onCancel() on ESCAPE + contentPane.registerKeyboardAction( + new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + onCancel(); + } + }, KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT); + } + //endregion + + //region Public Methods + public boolean showDialog(Component owner) { + this.setComponentOrientation(owner.getComponentOrientation()); + this.pack(); + this.setResizable(false); + this.setLocationRelativeTo(owner); + this.setVisible(true); + + return this.okPressed; + } + + public String getConverterName() { + return tfConverterName.getText().replace("Converter", ""); + } + //endregion + + //region Private Methods + private void loadImports(String code) { + StringBuilder imports = new StringBuilder(); + + for (String line : code.replace(" ", " ").split(Pattern.quote(PathHelper.LINE_SEPARATOR))) { + String trimmedLine = line.trim(); + if (trimmedLine.startsWith("import")) { + imports.append(trimmedLine); + imports.append(PathHelper.LINE_SEPARATOR); + } + } + + taImports.setText(imports.toString()); + } + + private void loadMethods(String code) { + Methods method = Methods.None; + + int skipCount = 0; + int bracketsCount = 0; + + StringBuilder body = new StringBuilder(); + + for (String line : code.replace(" ", " ").split(Pattern.quote(PathHelper.LINE_SEPARATOR))) { + String trimmedLine = line.trim(); + + if (trimmedLine.startsWith("public String toString(byte[] value) {")) { + bracketsCount++; + method = Methods.BytesToString; + + continue; + } + + if (trimmedLine.startsWith("public byte[] toBytes(String value) {")) { + bracketsCount++; + method = Methods.StringToBytes; + + continue; + } + + if (method != Methods.None) { + if (trimmedLine.endsWith("{")) { + bracketsCount++; + } + + if (trimmedLine.endsWith("}")) { + bracketsCount--; + } + + if (trimmedLine.startsWith("if (value == null) {")) { + skipCount++; + } + + if (bracketsCount == 0) { + setMethodBody(method, body); + + method = Methods.None; + skipCount = 0; + } + else { + if (skipCount == 0 || skipCount == 4) { + if (body.length() > 0) { + body.append(PathHelper.LINE_SEPARATOR); + } + + body.append(indent(bracketsCount)); + body.append(trimmedLine); + } + else { + skipCount++; + } + } + } + } + } + + private void setMethodBody(Methods method, StringBuilder body) { + if (method != Methods.None) { + switch (method) { + case None: + break; + case BytesToString: + taBytesToObjectMethod.setText(body.toString()); + break; + case StringToBytes: + taObjectToBytesMethod.setText(body.toString()); + break; + } + + body.setLength(0); + } + } + + private static String indent(int length) { + StringBuilder sb = new StringBuilder(); + for (int i = 0 ; i < length ; i++) { + sb.append(" "); + } + return sb.toString(); + } + + private void onOK() { + // add your code here + + String code = TEMPLATE; + + code = code.replace(IMPORTS_PLACE_HOLDER, taImports.getText()); + code = code.replace(CONVERTER_NAME_PLACE_HOLDER, tfConverterName.getText()); + code = code.replace(BYTES_TO_STRING_CODE_PLACE_HOLDER, taBytesToObjectMethod.getText()); + code = code.replace(STRING_TO_BYTES_CODE_PLACE_HOLDER, taObjectToBytesMethod.getText()); + code = code.replace(IS_VALID_FOR_NAME_CONVERSION_PLACE_HOLDER, chbNameConverter.isSelected() ? "true" : "false"); + + contentPane.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); + try { + File sourceCode = JavaCompiler.saveCode(tfConverterName.getText(), code, GlobalConfig.instance().getConvertersCodeFolder()); + if (JavaCompiler.compile(sourceCode, GlobalConfig.instance().getConvertersClassesFolder())) { + this.okPressed = true; + + dispose(); + } + else { + StringBuilder message = new StringBuilder(); + for (String error : JavaCompiler.getErrors()) { + message.append(error); + } + + JOptionPane.showMessageDialog(contentPane, message, "Compilation Errors", JOptionPane.ERROR_MESSAGE); + } + } + catch (Exception e) { + logger.error(e, "Failed to compile code."); + JOptionPane.showMessageDialog(contentPane, e.getMessage(), "Error", JOptionPane.ERROR_MESSAGE); + } + finally { + contentPane.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); + } + } + + private void onCancel() { + // add your code here if necessary + dispose(); + } + + { + // GUI initializer generated by IntelliJ IDEA GUI Designer + // >>> IMPORTANT!! <<< + // DO NOT EDIT OR ADD ANY CODE HERE! + $$$setupUI$$$(); + } + + /** + * Method generated by IntelliJ IDEA GUI Designer + * >>> IMPORTANT!! <<< + * DO NOT edit this method OR call it in your code! + * + * @noinspection ALL + */ + private void $$$setupUI$$$() { + contentPane = new JPanel(); + contentPane.setLayout(new GridLayoutManager(4, 1, new Insets(10, 10, 10, 10), -1, -1)); + final JPanel panel1 = new JPanel(); + panel1.setLayout(new GridLayoutManager(1, 2, new Insets(0, 0, 0, 0), -1, -1)); + contentPane.add( + panel1, new GridConstraints( + 3, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, + 1, null, null, null, 0, false)); + final Spacer spacer1 = new Spacer(); + panel1.add( + spacer1, new GridConstraints( + 0, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_WANT_GROW, 1, null, null, null, 0, false)); + final JPanel panel2 = new JPanel(); + panel2.setLayout(new GridLayoutManager(1, 2, new Insets(0, 0, 0, 0), -1, -1, true, false)); + panel1.add( + panel2, new GridConstraints( + 0, 1, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, + GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, null, null, 0, false)); + buttonOK = new JButton(); + buttonOK.setText("Save"); + panel2.add( + buttonOK, new GridConstraints( + 0, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, + GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); + buttonCancel = new JButton(); + buttonCancel.setText("Cancel"); + panel2.add( + buttonCancel, new GridConstraints( + 0, 1, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, + GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); + final JPanel panel3 = new JPanel(); + panel3.setLayout(new GridLayoutManager(9, 1, new Insets(0, 0, 0, 0), -1, -1)); + contentPane.add( + panel3, new GridConstraints( + 1, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, + GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, null, null, 0, false)); + final JScrollPane scrollPane1 = new JScrollPane(); + panel3.add( + scrollPane1, new GridConstraints( + 3, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_WANT_GROW, + GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_WANT_GROW, null, new Dimension(400, 70), null, 0, false)); + taBytesToObjectMethod = new JTextArea(); + taBytesToObjectMethod.setText(" return Bytes.toString(value); // replace with your code"); + scrollPane1.setViewportView(taBytesToObjectMethod); + final JLabel label1 = new JLabel(); + label1.setText("Imports:"); + panel3.add( + label1, new GridConstraints( + 0, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, + null, 0, false)); + final JScrollPane scrollPane2 = new JScrollPane(); + panel3.add( + scrollPane2, new GridConstraints( + 1, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_WANT_GROW, + GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_WANT_GROW, null, new Dimension(400, 40), null, 0, false)); + taImports = new JTextArea(); + taImports.setText("import org.apache.hadoop.hbase.util.Bytes;"); + scrollPane2.setViewportView(taImports); + final JLabel label2 = new JLabel(); + label2.setText("public String toString(byte[] value) { // bytes to string conversion"); + panel3.add( + label2, new GridConstraints( + 2, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, + null, 0, false)); + final JLabel label3 = new JLabel(); + label3.setText("}"); + panel3.add( + label3, new GridConstraints( + 4, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, + null, 0, false)); + final JLabel label4 = new JLabel(); + label4.setText("public byte[] toBytes(String value) { // string to bytes conversion"); + panel3.add( + label4, new GridConstraints( + 5, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, + null, 0, false)); + final JScrollPane scrollPane3 = new JScrollPane(); + panel3.add( + scrollPane3, new GridConstraints( + 6, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_WANT_GROW, + GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_WANT_GROW, null, new Dimension(400, 70), null, 0, false)); + taObjectToBytesMethod = new JTextArea(); + taObjectToBytesMethod.setText(" return Bytes.toBytes(value); // replace with your code"); + scrollPane3.setViewportView(taObjectToBytesMethod); + final JLabel label5 = new JLabel(); + label5.setText("}"); + panel3.add( + label5, new GridConstraints( + 7, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, + null, 0, false)); + chbNameConverter = new JCheckBox(); + chbNameConverter.setSelected(true); + chbNameConverter.setText("The converter can be used for column name conversions as well."); + panel3.add( + chbNameConverter, new GridConstraints( + 8, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, + GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); + final JPanel panel4 = new JPanel(); + panel4.setLayout(new GridLayoutManager(1, 2, new Insets(0, 0, 0, 0), -1, -1)); + contentPane.add( + panel4, new GridConstraints( + 0, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, + GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, null, null, 0, false)); + final JLabel label6 = new JLabel(); + label6.setText("Converter Name:"); + panel4.add( + label6, new GridConstraints( + 0, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, + null, 0, false)); + tfConverterName = new JTextField(); + tfConverterName.setText("CustomConverter"); + panel4.add( + tfConverterName, new GridConstraints( + 0, 1, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_FIXED, + null, new Dimension(150, 24), null, 0, false)); + final JSeparator separator1 = new JSeparator(); + contentPane.add( + separator1, new GridConstraints( + 2, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_WANT_GROW, + null, null, null, 0, false)); + label1.setLabelFor(taImports); + label2.setLabelFor(taBytesToObjectMethod); + label4.setLabelFor(taObjectToBytesMethod); + label6.setLabelFor(tfConverterName); + } + + /** + * @noinspection ALL + */ + public JComponent $$$getRootComponent$$$() { + return contentPane; + } + + //endregion + + private enum Methods { + None, + BytesToString, + StringToBytes + } +} diff --git a/src/main/java/hrider/ui/forms/ExportTableDialog.java b/src/main/java/hrider/ui/forms/ExportTableDialog.java index 4fdb03a..2369df6 100644 --- a/src/main/java/hrider/ui/forms/ExportTableDialog.java +++ b/src/main/java/hrider/ui/forms/ExportTableDialog.java @@ -3,13 +3,19 @@ import com.intellij.uiDesigner.core.GridConstraints; import com.intellij.uiDesigner.core.GridLayoutManager; import com.intellij.uiDesigner.core.Spacer; +import hrider.actions.*; +import hrider.actions.Action; import hrider.config.GlobalConfig; import hrider.data.ColumnQualifier; +import hrider.data.ColumnType; import hrider.data.DataRow; -import hrider.data.ObjectType; import hrider.data.TypedColumn; import hrider.export.FileExporter; -import hrider.hbase.*; +import hrider.hbase.Connection; +import hrider.hbase.HbaseActionListener; +import hrider.hbase.QueryScanner; +import hrider.hbase.Scanner; +import hrider.io.Log; import org.apache.hadoop.hbase.client.Put; import org.apache.hadoop.hbase.client.Result; @@ -43,6 +49,10 @@ */ public class ExportTableDialog extends JDialog { + //region Constants + private static final Log logger = Log.getLogger(ExportTableDialog.class); + //endregion + //region Variables private JPanel contentPane; private JButton btExport; @@ -56,7 +66,7 @@ public class ExportTableDialog extends JDialog { private JButton btClose; private JButton btExportWithQueryButton; private JComboBox cmbFileType; - private JLabel labelDelimiter; + private JLabel labelDelimiter; private String filePath; private boolean canceled; //endregion @@ -90,7 +100,7 @@ public void actionPerformed(ActionEvent e) { try { Collection columns = new ArrayList(); for (ColumnQualifier column : scanner.getColumns(100)) { - columns.add(new TypedColumn(column, ObjectType.String)); + columns.add(new TypedColumn(column, ColumnType.String)); } ScanDialog dialog = new ScanDialog(null, columns); @@ -202,17 +212,25 @@ public void actionPerformed(ActionEvent e) { } }, KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT); - new Thread( - new Runnable() { - @Override - public void run() { - try { - totalRowsCount.setText(String.valueOf(scanner.getRowsCount())); - } - catch (Exception ignore) { - } + RunnableAction.run( + "export table rows count", new Action() { + @Override + public Object run() throws IOException { + long totalNumberOfRows = scanner.getRowsCount(GlobalConfig.instance().getRowCountTimeout()); + if (scanner.isRowsCountPartiallyCalculated()) { + totalRowsCount.setText("more than " + totalNumberOfRows); } - }).start(); + else { + totalRowsCount.setText(String.valueOf(totalNumberOfRows)); + } + return null; + } + + @Override + public void onError(Exception ex) { + logger.error(ex, "Failed to count rows number."); + } + }); } //endregion diff --git a/src/main/java/hrider/ui/forms/ImportTableDialog.java b/src/main/java/hrider/ui/forms/ImportTableDialog.java index 4756633..59465d6 100644 --- a/src/main/java/hrider/ui/forms/ImportTableDialog.java +++ b/src/main/java/hrider/ui/forms/ImportTableDialog.java @@ -4,9 +4,11 @@ import com.intellij.uiDesigner.core.GridLayoutManager; import com.intellij.uiDesigner.core.Spacer; import hrider.config.GlobalConfig; +import hrider.converters.TypeConverter; import hrider.data.*; import hrider.hbase.Connection; import hrider.hbase.HbaseActionListener; +import hrider.ui.controls.WideComboBox; import hrider.ui.design.JTableModel; import org.apache.hadoop.hbase.TableNotFoundException; import org.apache.hadoop.hbase.client.Put; @@ -64,16 +66,21 @@ public class ImportTableDialog extends JDialog { private JLabel labelDelimiter; private DefaultTableModel tableModel; private boolean canceled; + private TypeConverter nameConverter; //endregion //region Constructor public ImportTableDialog( - final Connection connection, final String tableName, Iterable columns, final Collection columnFamilies) { + final Connection connection, final String tableName, TypeConverter nameConverter, Iterable columns, + final Collection columnFamilies) { + setContentPane(this.contentPane); setModal(true); setTitle("Import table from file"); getRootPane().setDefaultButton(this.btImport); + this.nameConverter = nameConverter; + if (tableName != null) { this.tfTableName.setText(tableName); this.tfFilePath.setText(String.format("%s.hfile", tableName)); @@ -87,10 +94,10 @@ public ImportTableDialog( this.rowsTable.setRowHeight(this.rowsTable.getFont().getSize() + 8); this.rowsTable.setAutoResizeMode(JTable.AUTO_RESIZE_SUBSEQUENT_COLUMNS); - JComboBox comboBox = new JComboBox(); + JComboBox comboBox = new WideComboBox(); - for (ObjectType objectType : ObjectType.values()) { - comboBox.addItem(objectType); + for (ColumnType columnType : ColumnType.getTypes()) { + comboBox.addItem(columnType); } this.rowsTable.getColumn("Column Type").setCellEditor(new DefaultCellEditor(comboBox)); @@ -172,7 +179,7 @@ public void actionPerformed(ActionEvent e) { int rowIndex = getRowIndex(rowsTable, 1, column); if (rowIndex == -1) { - tableModel.addRow(new Object[]{column, ObjectType.String, null}); + tableModel.addRow(new Object[]{column, ColumnType.String, null}); rowIndex = tableModel.getRowCount() - 1; } @@ -342,7 +349,7 @@ private void importDelimitedFile(Connection connection, String table, String fil throw new IllegalArgumentException("Column 'key' is missing in the file."); } - Map columnTypes = getColumnTypes(); + Map columnTypes = getColumnTypes(); Map columnQualifiers = getColumnQualifiers(); long readCount = 1; @@ -362,21 +369,21 @@ private void importDelimitedFile(Connection connection, String table, String fil String column = columns.get(i).trim(); String value = values[i].trim(); - ObjectType type = columnTypes.get(column); + ColumnType type = columnTypes.get(column); if (type == null) { - type = ObjectType.String; + type = ColumnType.String; } if (ColumnQualifier.isKey(column)) { - row.setKey(new TypedObject(type, type.fromString(value))); + row.setKey(new ConvertibleObject(type, type.toBytes(value))); } ColumnQualifier qualifier = columnQualifiers.get(column); if (qualifier == null) { - qualifier = new ColumnQualifier(column); + qualifier = new ColumnQualifier(column, nameConverter); } - row.addCell(new DataCell(row, qualifier, new TypedObject(type, type.fromString(value)))); + row.addCell(new DataCell(row, qualifier, new ConvertibleObject(type, type.toBytes(value)))); } if (row.getKey() != null) { @@ -461,11 +468,11 @@ public void columnOperation(String tableName, String column, String operation) { } } - private Map getColumnTypes() { - Map columnTypes = new HashMap(); + private Map getColumnTypes() { + Map columnTypes = new HashMap(); for (int i = 0 ; i < this.rowsTable.getRowCount() ; i++) { String columnName = (String)this.rowsTable.getValueAt(i, 0); - ObjectType columnType = (ObjectType)this.rowsTable.getValueAt(i, 1); + ColumnType columnType = (ColumnType)this.rowsTable.getValueAt(i, 1); columnTypes.put(columnName, columnType); } diff --git a/src/main/java/hrider/ui/forms/PasteDialog.java b/src/main/java/hrider/ui/forms/PasteDialog.java index c8632e7..19ed308 100644 --- a/src/main/java/hrider/ui/forms/PasteDialog.java +++ b/src/main/java/hrider/ui/forms/PasteDialog.java @@ -3,7 +3,7 @@ import com.intellij.uiDesigner.core.GridConstraints; import com.intellij.uiDesigner.core.GridLayoutManager; import hrider.data.DataRow; -import hrider.data.TypedObject; +import hrider.data.ConvertibleObject; import hrider.ui.ChangeTracker; import hrider.ui.design.JCellEditor; @@ -44,12 +44,12 @@ public class PasteDialog extends JDialog { private JButton buttonCancel; private JTable table; private boolean okPressed; - private Map rows; + private Map rows; //endregion //region Constructor public PasteDialog(ChangeTracker changeTracker, Iterable rows) { - this.rows = new HashMap(); + this.rows = new HashMap(); setContentPane(this.contentPane); setModal(true); @@ -132,20 +132,20 @@ public Collection getRows() { private void onOK() { boolean isValid = true; - Map validKeys = new HashMap(); + Map validKeys = new HashMap(); for (int i = 0 ; i < this.table.getRowCount() ; i++) { String value = (String)this.table.getValueAt(i, 1); if (value != null) { - TypedObject key = (TypedObject)this.table.getValueAt(i, 0); + ConvertibleObject key = (ConvertibleObject)this.table.getValueAt(i, 0); try { - Object obj = key.getType().toObject(value); - validKeys.put(new TypedObject(key.getType(), obj), key); + byte[] bytes = key.getType().toBytes(value); + validKeys.put(new ConvertibleObject(key.getType(), bytes), key); } catch (Exception e) { JOptionPane.showMessageDialog( this.contentPane, String.format( - "The new key for '%s' is in a wrong format; expected %s type.%sError: %s", key.getValue(), key.getType(), "\n", e.getMessage()), + "The new key for '%s' is in a wrong format; expected %s type.%sError: %s", key.getValueAsString(), key.getType(), "\n", e.getMessage()), "Error", JOptionPane.ERROR_MESSAGE); isValid = false; @@ -155,7 +155,7 @@ private void onOK() { } if (isValid) { - for (Map.Entry entry : validKeys.entrySet()) { + for (Map.Entry entry : validKeys.entrySet()) { DataRow row = this.rows.get(entry.getValue()); row.setKey(entry.getKey()); } diff --git a/src/main/java/hrider/ui/forms/ScanDialog.java b/src/main/java/hrider/ui/forms/ScanDialog.java index c8e8542..60afa35 100644 --- a/src/main/java/hrider/ui/forms/ScanDialog.java +++ b/src/main/java/hrider/ui/forms/ScanDialog.java @@ -5,7 +5,7 @@ import com.michaelbaranov.microba.calendar.DatePicker; import hrider.config.GlobalConfig; import hrider.data.ColumnQualifier; -import hrider.data.ObjectType; +import hrider.data.ColumnType; import hrider.data.TypedColumn; import hrider.hbase.Operator; import hrider.hbase.Query; @@ -75,12 +75,16 @@ public ScanDialog(Query query, Iterable columns) { this.comboBoxOperator.addItem(operator); } - for (ObjectType objectType : ObjectType.values()) { - this.comboBoxStartKeyType.addItem(objectType); - this.comboBoxEndKeyType.addItem(objectType); - this.comboBoxWordType.addItem(objectType); + for (ColumnType columnType : ColumnType.getTypes()) { + this.comboBoxStartKeyType.addItem(columnType); + this.comboBoxEndKeyType.addItem(columnType); + this.comboBoxWordType.addItem(columnType); } + this.comboBoxStartKeyType.setSelectedItem(ColumnType.BinaryString); + this.comboBoxEndKeyType.setSelectedItem(ColumnType.BinaryString); + this.comboBoxWordType.setSelectedItem(ColumnType.String); + fillForm(query); this.buttonRun.addActionListener( @@ -136,11 +140,11 @@ public Query getQuery() { Query query = new Query(); if (!this.textFieldStartKey.getText().trim().isEmpty()) { - query.setStartKey((ObjectType)this.comboBoxStartKeyType.getSelectedItem(), this.textFieldStartKey.getText().trim()); + query.setStartKey((ColumnType)this.comboBoxStartKeyType.getSelectedItem(), this.textFieldStartKey.getText().trim()); } if (!this.textFieldEndKey.getText().trim().isEmpty()) { - query.setEndKey((ObjectType)this.comboBoxEndKeyType.getSelectedItem(), this.textFieldEndKey.getText().trim()); + query.setEndKey((ColumnType)this.comboBoxEndKeyType.getSelectedItem(), this.textFieldEndKey.getText().trim()); } if (this.checkBoxUseDates.isSelected()) { @@ -156,7 +160,7 @@ public Query getQuery() { if (!this.textFieldWord.getText().trim().isEmpty()) { query.setWord(this.textFieldWord.getText().trim()); - query.setWordType((ObjectType)this.comboBoxWordType.getSelectedItem()); + query.setWordType((ColumnType)this.comboBoxWordType.getSelectedItem()); } return query; diff --git a/src/main/java/hrider/ui/forms/UpdateDialog.form b/src/main/java/hrider/ui/forms/UpdateDialog.form new file mode 100644 index 0000000..a33208b --- /dev/null +++ b/src/main/java/hrider/ui/forms/UpdateDialog.form @@ -0,0 +1,127 @@ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/src/main/java/hrider/ui/forms/UpdateDialog.java b/src/main/java/hrider/ui/forms/UpdateDialog.java new file mode 100644 index 0000000..9c832f5 --- /dev/null +++ b/src/main/java/hrider/ui/forms/UpdateDialog.java @@ -0,0 +1,238 @@ +package hrider.ui.forms; + +import com.intellij.uiDesigner.core.GridConstraints; +import com.intellij.uiDesigner.core.GridLayoutManager; +import com.intellij.uiDesigner.core.Spacer; +import hrider.ui.controls.JLinkButton; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.*; +import java.net.URI; + +/** + * Copyright (C) 2012 NICE Systems ltd. + *

+ * 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. + * + * @author Igor Cher + * @version %I%, %G% + */ +public class UpdateDialog extends JDialog { + + //region Constants + private static final long serialVersionUID = 8225590140219980523L; + //endregion + + //region Variables + private JPanel contentPane; + private JButton buttonOK; + private JButton buttonCancel; + private JTextField hriderVersion; + private JTextField hbaseVersion; + private JLinkButton seeChangesButton; + private boolean okPressed; + //endregion + + //region Constructor + public UpdateDialog(String hriderVersion, String hbaseVersion) { + setContentPane(contentPane); + setModal(true); + setTitle("New version available"); + getRootPane().setDefaultButton(buttonOK); + + this.hriderVersion.setText(hriderVersion); + this.hbaseVersion.setText(hbaseVersion); + + buttonOK.addActionListener( + new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + onOK(); + } + }); + + buttonCancel.addActionListener( + new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + onCancel(); + } + }); + + // call onCancel() when cross is clicked + setDefaultCloseOperation(DO_NOTHING_ON_CLOSE); + + addWindowListener( + new WindowAdapter() { + @Override + public void windowClosing(WindowEvent e) { + onCancel(); + } + }); + + // call onCancel() on ESCAPE + contentPane.registerKeyboardAction( + new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + onCancel(); + } + }, KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT); + + seeChangesButton.addActionListener( + new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + try { + Desktop.getDesktop().browse(new URI("https://github.com/NiceSystems/hrider/wiki#news")); + } + catch (Exception ex) { + JOptionPane.showMessageDialog(contentPane, ex.getMessage(), "Error", JOptionPane.ERROR_MESSAGE); + } + } + }); + } + //endregion + + //region Public Methods + public boolean showDialog(Component owner) { + this.setComponentOrientation(owner.getComponentOrientation()); + this.pack(); + this.setResizable(false); + this.setLocationRelativeTo(owner); + this.setVisible(true); + + return this.okPressed; + } + //endregion + + //region Private Methods + private void onOK() { + this.okPressed = true; + + dispose(); + } + + private void onCancel() { + // add your code here if necessary + dispose(); + } + //endregion + + { + // GUI initializer generated by IntelliJ IDEA GUI Designer + // >>> IMPORTANT!! <<< + // DO NOT EDIT OR ADD ANY CODE HERE! + $$$setupUI$$$(); + } + + /** + * Method generated by IntelliJ IDEA GUI Designer + * >>> IMPORTANT!! <<< + * DO NOT edit this method OR call it in your code! + * + * @noinspection ALL + */ + private void $$$setupUI$$$() { + contentPane = new JPanel(); + contentPane.setLayout(new GridLayoutManager(4, 2, new Insets(10, 10, 10, 10), -1, -1)); + final JPanel panel1 = new JPanel(); + panel1.setLayout(new GridLayoutManager(1, 2, new Insets(0, 0, 0, 0), -1, -1)); + contentPane.add( + panel1, new GridConstraints( + 3, 0, 1, 2, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, + 1, null, null, null, 0, false)); + final Spacer spacer1 = new Spacer(); + panel1.add( + spacer1, new GridConstraints( + 0, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_WANT_GROW, 1, null, null, null, 0, false)); + final JPanel panel2 = new JPanel(); + panel2.setLayout(new GridLayoutManager(1, 2, new Insets(0, 0, 0, 0), -1, -1, true, false)); + panel1.add( + panel2, new GridConstraints( + 0, 1, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, + GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, null, null, 0, false)); + buttonOK = new JButton(); + buttonOK.setText("Update"); + panel2.add( + buttonOK, new GridConstraints( + 0, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, + GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); + buttonCancel = new JButton(); + buttonCancel.setText("Cancel"); + panel2.add( + buttonCancel, new GridConstraints( + 0, 1, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, + GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); + final JPanel panel3 = new JPanel(); + panel3.setLayout(new GridLayoutManager(2, 2, new Insets(0, 0, 0, 0), -1, -1)); + contentPane.add( + panel3, new GridConstraints( + 0, 0, 1, 2, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, + GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, null, null, 0, false)); + final JLabel label1 = new JLabel(); + label1.setText("h-rider version:"); + panel3.add( + label1, new GridConstraints( + 0, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, + null, 0, false)); + final JLabel label2 = new JLabel(); + label2.setText("hbase version:"); + panel3.add( + label2, new GridConstraints( + 1, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, + null, 0, false)); + hriderVersion = new JTextField(); + hriderVersion.setEditable(false); + panel3.add( + hriderVersion, new GridConstraints( + 0, 1, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_FIXED, + null, new Dimension(150, -1), null, 0, false)); + hbaseVersion = new JTextField(); + hbaseVersion.setEditable(false); + panel3.add( + hbaseVersion, new GridConstraints( + 1, 1, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_FIXED, + null, new Dimension(150, -1), null, 0, false)); + final JSeparator separator1 = new JSeparator(); + contentPane.add( + separator1, new GridConstraints( + 2, 0, 1, 2, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_WANT_GROW, + null, null, null, 0, false)); + final JLabel label3 = new JLabel(); + label3.setForeground(new Color(-16777216)); + label3.setText("* The h-rider will be closed on update..."); + contentPane.add( + label3, new GridConstraints( + 1, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, + null, 0, false)); + seeChangesButton = new JLinkButton(); + seeChangesButton.setActionCommand(""); + seeChangesButton.setLabel("See changes..."); + seeChangesButton.setText("See changes..."); + contentPane.add( + seeChangesButton, new GridConstraints( + 1, 1, 1, 1, GridConstraints.ANCHOR_EAST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, + GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, null, null, 0, false)); + label1.setLabelFor(hriderVersion); + label2.setLabelFor(hbaseVersion); + } + + /** + * @noinspection ALL + */ + public JComponent $$$getRootComponent$$$() { + return contentPane; + } +} diff --git a/src/main/java/hrider/ui/forms/Window.form b/src/main/java/hrider/ui/forms/Window.form index c7d9c21..77389ad 100644 --- a/src/main/java/hrider/ui/forms/Window.form +++ b/src/main/java/hrider/ui/forms/Window.form @@ -3,7 +3,7 @@ - + @@ -11,7 +11,8 @@ - + + @@ -23,51 +24,65 @@ - + + + - - + + + + + + + + + + + + + + + + + + + + + + + + + - + - - + + - - - + + + + + - - + + - + + + + + + - - - - - - - - - - - - - - - - - - - + @@ -91,29 +106,49 @@ - + - - + + + - - - - + - + + + + + + + + + + + + + + - - - - + + + + + + + + + + - + diff --git a/src/main/java/hrider/ui/forms/Window.java b/src/main/java/hrider/ui/forms/Window.java index 9f4fdf4..54d4959 100644 --- a/src/main/java/hrider/ui/forms/Window.java +++ b/src/main/java/hrider/ui/forms/Window.java @@ -2,6 +2,8 @@ import com.intellij.uiDesigner.core.GridConstraints; import com.intellij.uiDesigner.core.GridLayoutManager; +import hrider.actions.Action; +import hrider.actions.RunnableAction; import hrider.config.ClusterConfig; import hrider.config.ConnectionDetails; import hrider.config.PropertiesConfig; @@ -9,11 +11,18 @@ import hrider.data.DataTable; import hrider.hbase.Connection; import hrider.hbase.ConnectionManager; +import hrider.io.CloseableHelper; +import hrider.io.Downloader; +import hrider.io.FileHelper; +import hrider.io.Log; +import hrider.system.Clipboard; import hrider.system.ClipboardData; import hrider.system.InMemoryClipboard; +import hrider.system.Version; import hrider.ui.MessageHandler; import hrider.ui.MessageHandlerListener; import hrider.ui.TabClosedListener; +import hrider.ui.UIAction; import hrider.ui.controls.JCloseButton; import hrider.ui.controls.JLinkButton; import hrider.ui.views.DesignerView; @@ -22,13 +31,12 @@ import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashMap; -import java.util.Map; -import java.util.jar.Attributes; -import java.util.jar.JarFile; +import java.awt.event.WindowEvent; +import java.io.*; +import java.net.URL; +import java.util.*; +import java.util.jar.Manifest; +import java.util.regex.Pattern; /** * Copyright (C) 2012 NICE Systems ltd. @@ -50,36 +58,114 @@ */ public class Window { + //region Constants + private static final String UPDATE_FILE_URL = "https://raw.github.com/NiceSystems/hrider/master/update.properties"; + private static final Log logger = Log.getLogger(Window.class); + //endregion + //region Variables private JPanel topPanel; - private JTextArea statusLabel; private JTabbedPane tabbedPane; private JLinkButton connectToCluster; + private JPanel actionPanel; + private JLabel actionLabel1; + private JLinkButton actionLabel2; + private JLabel actionLabel3; + private JButton copyToClipboard; + private JLinkButton newVersionAvailable; private boolean canceled; + private UIAction uiAction; + private String lastError; private Map viewMap; + private Properties updateInfo; + private JFrame frame; //endregion //region Constructor public Window() { + this.updateInfo = new Properties(); this.viewMap = new HashMap(); + Font font = this.actionLabel1.getFont(); + this.actionLabel1.setFont(font.deriveFont(font.getStyle() | ~Font.BOLD)); + this.actionLabel2.setFont(font.deriveFont(font.getStyle() | ~Font.BOLD)); + this.actionLabel3.setFont(font.deriveFont(font.getStyle() | ~Font.BOLD)); + MessageHandler.addListener( new MessageHandlerListener() { @Override public void onInfo(String message) { - statusLabel.setText(message); - statusLabel.paintImmediately(statusLabel.getBounds()); + lastError = null; + uiAction = null; + + copyToClipboard.setVisible(false); + + actionLabel1.setText(message); + + // this trick allows to keep all labels on the same level. The empty text on the link button moves all labels up for a couple of pixels. + actionLabel2.setText(" "); + actionLabel2.setLinkColor(new Color(240, 240, 240)); // the control color which is not visible. + + actionLabel3.setText(""); } @Override public void onError(String message, Exception ex) { - String error = message; + uiAction = null; + + copyToClipboard.setVisible(true); + + String error = ' ' + message; + lastError = error; + if (ex != null) { - error += ": " + ex.toString(); + StringWriter sw = new StringWriter(); + PrintWriter pw = new PrintWriter(sw); + + ex.printStackTrace(pw); + + lastError += ": " + sw.toString(); + error += ": " + ex.getMessage(); + + pw.close(); } - statusLabel.setText(error); - statusLabel.paintImmediately(statusLabel.getBounds()); + actionLabel1.setText(error); + actionLabel2.setText(" "); + actionLabel2.setLinkColor(new Color(240, 240, 240)); + actionLabel3.setText(""); + } + + @Override + public void onAction(UIAction action) { + lastError = null; + uiAction = action; + + copyToClipboard.setVisible(false); + + String[] messageParts = action.getFormattedMessage(); + if (messageParts.length > 0) { + actionLabel1.setText(messageParts[0]); + } + else { + actionLabel1.setText(""); + } + + if (messageParts.length > 1) { + actionLabel2.setText(messageParts[1]); + actionLabel2.setLinkColor(new Color(0, 0, 255)); // the blue link color. + } + else { + actionLabel2.setText(" "); + actionLabel2.setLinkColor(new Color(240, 240, 240)); + } + + if (messageParts.length > 2) { + actionLabel3.setText(messageParts[2]); + } + else { + actionLabel3.setText(""); + } } }); @@ -99,6 +185,66 @@ public void actionPerformed(ActionEvent e) { } } }); + + this.actionLabel2.addActionListener( + new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + if (uiAction != null) { + uiAction.execute(); + } + } + }); + + this.copyToClipboard.addActionListener( + new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + if (lastError != null) { + Clipboard.setText(lastError); + } + } + }); + + this.newVersionAvailable.addActionListener( + new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + UpdateDialog dialog = new UpdateDialog(updateInfo.getProperty("hrider.version"), getHbaseVersion()); + if (dialog.showDialog(topPanel)) { + try { + File jar = FileHelper.findFile( + new File("."), Pattern.compile("h-rider-updater-?[0-9]{0,4}\\.?[0-9]{0,4}\\.?[0-9]{0,4}\\.?[0-9]{0,4}\\.jar")); + + File temporary = File.createTempFile("h-rider-updater-", ".jar"); + FileHelper.copy(jar, temporary); + + String url = updateInfo.getProperty("hbase." + getHbaseVersion() + ".reduced"); + if (url == null) { + url = updateInfo.getProperty("hbase." + getHbaseVersion()); + } + + Runtime.getRuntime().exec( + String.format("java -jar %s \"%s\" \"%s\"", temporary.getName(), url, jar.getParentFile().getAbsolutePath()), null, + temporary.getParentFile()); + + frame.dispatchEvent(new WindowEvent(frame, WindowEvent.WINDOW_CLOSING)); + } + catch (IOException ex) { + JOptionPane.showMessageDialog(topPanel, ex.getMessage(), "Error", JOptionPane.ERROR_MESSAGE); + } + } + } + }); + + RunnableAction.run( + "compare-versions", new Action() { + @Override + public Object run() throws Exception { + compareVersions(); + return null; + } + }); } //endregion @@ -128,10 +274,11 @@ private static void createAndShowGUI() { window.loadViews(new Splash()); if (!window.canceled) { - JFrame frame = new JFrame("H-Rider - " + getVersion()); + JFrame frame = new JFrame("h-rider - " + getVersion()); + window.frame = frame; frame.setContentPane(window.topPanel); - frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); frame.setLocationByPlatform(true); frame.setIconImage(new ImageIcon(Thread.currentThread().getContextClassLoader().getResource("images/h-rider.png")).getImage()); @@ -142,14 +289,69 @@ private static void createAndShowGUI() { } private static String getVersion() { + InputStream stream = null; + + try { + stream = Thread.currentThread().getContextClassLoader().getResourceAsStream("META-INF/MANIFEST.MF"); + + Manifest manifest = new Manifest(stream); + return manifest.getMainAttributes().getValue("version"); + } + catch (Exception ignore) { + return ""; + } + finally { + CloseableHelper.closeSilently(stream); + } + } + + private static String getHbaseVersion() { + InputStream stream = null; + try { - String jarPath = Window.class.getProtectionDomain().getCodeSource().getLocation().getPath(); + stream = Thread.currentThread().getContextClassLoader().getResourceAsStream("META-INF/MANIFEST.MF"); + + Manifest manifest = new Manifest(stream); + return manifest.getMainAttributes().getValue("hbaseVersion"); + } + catch (Exception ignore) { + return ""; + } + finally { + CloseableHelper.closeSilently(stream); + } + } + + private void compareVersions() { + FileInputStream stream = null; - JarFile file = new JarFile(jarPath); - return file.getManifest().getMainAttributes().get(new Attributes.Name("version")).toString(); + try { + File updateFile = Downloader.download(new URL(UPDATE_FILE_URL)); + stream = new FileInputStream(updateFile); + + updateInfo.clear(); + updateInfo.load(stream); + + String newVersion = updateInfo.getProperty("hrider.version"); + String oldVersion = getVersion(); + + if (newVersion != null && oldVersion != null && Version.compare(newVersion, oldVersion) > 0) { + String hbaseVersions = updateInfo.getProperty("hbase.versions"); + if (hbaseVersions != null) { + for (String version : hbaseVersions.split(";")) { + if (Version.compare(version, getHbaseVersion()) == 0) { + newVersionAvailable.setVisible(true); + break; + } + } + } + } + } + catch (Exception e) { + logger.error(e, "Failed to retrieve update information from the URL: %s", UPDATE_FILE_URL); } - catch (IOException ignore) { - return "Unknown Version"; + finally { + CloseableHelper.closeSilently(stream); } } @@ -241,49 +443,43 @@ public void run() { private void loadView(Connection connection) { DesignerView view = new DesignerView(this.topPanel, connection); - int index = this.tabbedPane.indexOfTab(connection.getServerName()); - if (index == -1) { - index = this.tabbedPane.getTabCount(); - - JCloseButton closeButton = new JCloseButton(connection.getServerName(), this.tabbedPane); - this.viewMap.put(closeButton, view); - - closeButton.addTabClosedListener( - new TabClosedListener() { - @Override - public void onTabClosed(Component component) { - DesignerView designerView = viewMap.get(component); - - ClipboardData data = InMemoryClipboard.getData(); - if (data != null) { - Connection helper = data.getData().getConnection(); - if (helper != null) { - if (helper.equals(designerView.getConnection())) { - InMemoryClipboard.setData(null); - } + int index = this.tabbedPane.getTabCount(); + + JCloseButton closeButton = new JCloseButton(connection.getServerName(), this.tabbedPane); + this.viewMap.put(closeButton, view); + + closeButton.addTabClosedListener( + new TabClosedListener() { + @Override + public void onTabClosed(Component component) { + DesignerView designerView = viewMap.get(component); + + ClipboardData data = InMemoryClipboard.getData(); + if (data != null) { + Connection helper = data.getData().getConnection(); + if (helper != null) { + if (helper.equals(designerView.getConnection())) { + InMemoryClipboard.setData(null); } } + } - ViewConfig.instance().removeCluster(designerView.getConnection().getServerName()); + ViewConfig.instance().removeCluster(designerView.getConnection().getServerName()); - PropertiesConfig.fileRemove(designerView.getConnection().getServerName()); + PropertiesConfig.fileRemove(designerView.getConnection().getServerName()); - ConnectionManager.release(designerView.getConnection().getConnectionDetails()); - viewMap.remove(component); - } - }); + ConnectionManager.release(designerView.getConnection().getConnectionDetails()); + viewMap.remove(component); + } + }); - this.tabbedPane.addTab(connection.getServerName(), view.getView()); - this.tabbedPane.setSelectedIndex(index); - this.tabbedPane.setTabComponentAt(index, closeButton); + this.tabbedPane.addTab(connection.getServerName(), view.getView()); + this.tabbedPane.setSelectedIndex(index); + this.tabbedPane.setTabComponentAt(index, closeButton); - ViewConfig.instance().addCluster(connection.getServerName()); + ViewConfig.instance().addCluster(connection.getServerName()); - view.populate(); - } - else { - this.tabbedPane.setSelectedIndex(index); - } + view.populate(); } /** @@ -320,7 +516,7 @@ private ConnectionDetails showDialog() { topPanel.setMinimumSize(new Dimension(650, 450)); topPanel.setPreferredSize(new Dimension(1200, 550)); final JPanel panel1 = new JPanel(); - panel1.setLayout(new BorderLayout(0, 0)); + panel1.setLayout(new GridLayoutManager(1, 4, new Insets(0, 0, 0, 0), -1, -1)); panel1.setBackground(new Color(-1)); topPanel.add( panel1, new GridConstraints( @@ -331,52 +527,86 @@ panel1, new GridConstraints( final JLabel label1 = new JLabel(); label1.setIcon(new ImageIcon(getClass().getResource("/images/h-rider.png"))); label1.setText(""); - panel1.add(label1, BorderLayout.WEST); - final JPanel panel2 = new JPanel(); - panel2.setLayout(new BorderLayout(0, 0)); - panel2.setBackground(new Color(-1)); - panel1.add(panel2, BorderLayout.CENTER); + panel1.add( + label1, new GridConstraints( + 0, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, + GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, null, null, 0, false)); final JLabel label2 = new JLabel(); label2.setFont(new Font("Curlz MT", Font.BOLD, 28)); label2.setForeground(new Color(-13408513)); label2.setText(" H-Rider"); - panel2.add(label2, BorderLayout.WEST); - final JPanel panel3 = new JPanel(); - panel3.setLayout(new BorderLayout(0, 0)); - panel3.setBackground(new Color(-1)); - panel2.add(panel3, BorderLayout.CENTER); + panel1.add( + label2, new GridConstraints( + 0, 1, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_VERTICAL, GridConstraints.SIZEPOLICY_FIXED, + GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, null, null, 0, false)); final JLabel label3 = new JLabel(); label3.setFont(new Font("Curlz MT", label3.getFont().getStyle(), 20)); label3.setHorizontalAlignment(10); label3.setHorizontalTextPosition(11); label3.setText(" The ultimate Hbase viewer and editor..."); - panel3.add(label3, BorderLayout.WEST); + panel1.add( + label3, new GridConstraints( + 0, 2, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_VERTICAL, GridConstraints.SIZEPOLICY_FIXED, + GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, null, null, 0, false)); + final JPanel panel2 = new JPanel(); + panel2.setLayout(new FlowLayout(FlowLayout.RIGHT, 5, 5)); + panel2.setBackground(new Color(-1)); + panel1.add( + panel2, new GridConstraints( + 0, 3, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, + GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, null, null, 0, false)); + panel2.setBorder(BorderFactory.createTitledBorder(BorderFactory.createEmptyBorder(), null)); connectToCluster = new JLinkButton(); + connectToCluster.setFocusPainted(false); + connectToCluster.setFocusable(false); + connectToCluster.setHorizontalAlignment(0); + connectToCluster.setRequestFocusEnabled(false); connectToCluster.setText("Connect to a cluster..."); - panel3.add(connectToCluster, BorderLayout.EAST); - final JPanel panel4 = new JPanel(); - panel4.setLayout(new BorderLayout(0, 0)); + panel2.add(connectToCluster); + newVersionAvailable = new JLinkButton(); + newVersionAvailable.setFocusPainted(false); + newVersionAvailable.setFocusable(false); + newVersionAvailable.setHorizontalAlignment(4); + newVersionAvailable.setRequestFocusEnabled(false); + newVersionAvailable.setText("New version is available..."); + newVersionAvailable.setVisible(false); + panel2.add(newVersionAvailable); + final JPanel panel3 = new JPanel(); + panel3.setLayout(new BorderLayout(0, 0)); topPanel.add( - panel4, new GridConstraints( + panel3, new GridConstraints( 1, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, new Dimension(566, 377), null, 0, false)); tabbedPane = new JTabbedPane(); tabbedPane.setTabLayoutPolicy(1); - panel4.add(tabbedPane, BorderLayout.CENTER); - final JScrollPane scrollPane1 = new JScrollPane(); - scrollPane1.setHorizontalScrollBarPolicy(30); - scrollPane1.setVerticalScrollBarPolicy(20); + panel3.add(tabbedPane, BorderLayout.CENTER); + actionPanel = new JPanel(); + actionPanel.setLayout(new FlowLayout(FlowLayout.LEFT, 2, 0)); topPanel.add( - scrollPane1, new GridConstraints( + actionPanel, new GridConstraints( 2, 0, 1, 1, GridConstraints.ANCHOR_SOUTH, GridConstraints.FILL_HORIZONTAL, - GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_WANT_GROW, 1, null, new Dimension(-1, 24), null, 0, false)); - scrollPane1.setBorder(BorderFactory.createTitledBorder(BorderFactory.createEtchedBorder(), null)); - statusLabel = new JTextArea(); - statusLabel.setBackground(new Color(-986896)); - statusLabel.setEditable(false); - statusLabel.setLineWrap(true); - statusLabel.setWrapStyleWord(true); - scrollPane1.setViewportView(statusLabel); + GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, new Dimension(-1, 34), + new Dimension(-1, 34), null, 0, false)); + actionPanel.setBorder(BorderFactory.createTitledBorder(BorderFactory.createEtchedBorder(), null)); + copyToClipboard = new JButton(); + copyToClipboard.setIcon(new ImageIcon(getClass().getResource("/images/copy.png"))); + copyToClipboard.setLabel(""); + copyToClipboard.setMargin(new Insets(0, 0, 0, 0)); + copyToClipboard.setText(""); + actionPanel.add(copyToClipboard); + actionLabel1 = new JLabel(); + actionLabel1.setFocusable(false); + actionLabel1.setText(""); + actionPanel.add(actionLabel1); + actionLabel2 = new JLinkButton(); + actionLabel2.setFocusPainted(false); + actionLabel2.setFocusable(false); + actionLabel2.setMargin(new Insets(0, 0, 0, 0)); + actionPanel.add(actionLabel2); + actionLabel3 = new JLabel(); + actionLabel3.setFocusable(false); + actionLabel3.setText(""); + actionPanel.add(actionLabel3); } /** diff --git a/src/main/java/hrider/ui/views/DesignerView.form b/src/main/java/hrider/ui/views/DesignerView.form index 4f1b652..48e624a 100644 --- a/src/main/java/hrider/ui/views/DesignerView.form +++ b/src/main/java/hrider/ui/views/DesignerView.form @@ -55,7 +55,7 @@ - + @@ -72,7 +72,7 @@ - + @@ -132,10 +132,10 @@ - + - + @@ -145,7 +145,7 @@ - + @@ -158,7 +158,7 @@ - + @@ -171,7 +171,7 @@ - + @@ -183,7 +183,7 @@ - + @@ -195,7 +195,7 @@ - + @@ -207,10 +207,10 @@ - + - + @@ -219,7 +219,7 @@ - + @@ -229,10 +229,10 @@ - + - + @@ -328,7 +328,7 @@ - + @@ -345,7 +345,7 @@ - + @@ -356,6 +356,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -377,7 +414,7 @@ - + @@ -387,7 +424,7 @@ - + @@ -397,75 +434,82 @@ - + - + - + - + - - - - - - - - - - - - - - - + + + + + - - + + + + + + + + + + + + + + + + + - + - + - + + - + - + - + + - + @@ -551,7 +595,7 @@ - + @@ -562,7 +606,7 @@ - + @@ -573,7 +617,7 @@ - + @@ -584,7 +628,7 @@ - + @@ -596,7 +640,7 @@ - + @@ -608,7 +652,7 @@ - + @@ -633,7 +677,7 @@ - + @@ -646,7 +690,7 @@ - + @@ -666,17 +710,25 @@ - + + + + + + + + + - + @@ -688,7 +740,7 @@ - + diff --git a/src/main/java/hrider/ui/views/DesignerView.java b/src/main/java/hrider/ui/views/DesignerView.java index 3ec7cfd..f554643 100644 --- a/src/main/java/hrider/ui/views/DesignerView.java +++ b/src/main/java/hrider/ui/views/DesignerView.java @@ -3,8 +3,13 @@ import com.intellij.uiDesigner.core.GridConstraints; import com.intellij.uiDesigner.core.GridLayoutManager; import com.intellij.uiDesigner.core.Spacer; +import hrider.actions.Action; +import hrider.actions.RunnableAction; import hrider.config.ClusterConfig; import hrider.config.GlobalConfig; +import hrider.converters.ConvertersLoader; +import hrider.converters.ConvertersLoaderHandler; +import hrider.converters.TypeConverter; import hrider.data.*; import hrider.export.FileExporter; import hrider.filters.EmptyFilter; @@ -14,16 +19,16 @@ import hrider.hbase.HbaseActionListener; import hrider.hbase.Query; import hrider.hbase.QueryScanner; +import hrider.io.PathHelper; import hrider.system.ClipboardData; import hrider.system.ClipboardListener; import hrider.system.InMemoryClipboard; import hrider.ui.ChangeTracker; import hrider.ui.ChangeTrackerListener; import hrider.ui.MessageHandler; -import hrider.ui.design.JCellEditor; -import hrider.ui.design.JCheckBoxRenderer; -import hrider.ui.design.JTableModel; -import hrider.ui.design.ResizeableTableHeader; +import hrider.ui.UIAction; +import hrider.ui.controls.WideComboBox; +import hrider.ui.design.*; import hrider.ui.forms.*; import org.apache.hadoop.hbase.client.Put; import org.apache.hadoop.hbase.client.Result; @@ -66,58 +71,67 @@ *

* This class is a main view. */ +@SuppressWarnings({"MagicNumber", "AnonymousInnerClassWithTooManyMethods"}) public class DesignerView { //region Variables - private JPanel topPanel; - private JList tablesList; - private DefaultListModel tablesListModel; - private JTable rowsTable; - private JButton populateButton; - private JTable columnsTable; - private JButton updateRowButton; - private JButton deleteRowButton; - private JButton addRowButton; - private JButton truncateTableButton; - private JButton scanButton; - private JButton addTableButton; - private JButton deleteTableButton; - private JSpinner rowsNumberSpinner; - private JButton showPrevPageButton; - private JButton showNextPageButton; - private JLabel rowsNumberLabel; - private JLabel visibleRowsLabel; - private JButton checkAllButton; - private JButton copyRowButton; - private JButton pasteRowButton; - private JButton refreshTablesButton; - private JButton copyTableButton; - private JButton pasteTableButton; - private JButton uncheckAllButton; - private JSplitPane topSplitPane; - private JSplitPane innerSplitPane; - private JLabel columnsNumber; - private JLabel tablesNumber; - private JComboBox tablesFilter; - private DefaultComboBoxModel tablesFilterModel; - private JComboBox columnsFilter; - private DefaultComboBoxModel columnsFilterModel; - private JButton jumpButton; - private JButton openInViewerButton; - private JButton importTableButton; - private JButton exportTableButton; - private JButton refreshColumnsButton; - private JButton flushTableButton; - private JButton tableMetadataButton; - private DefaultTableModel columnsTableModel; - private DefaultTableModel rowsTableModel; - private Query lastQuery; - private QueryScanner scanner; - private JPanel owner; - private Connection connection; - private ChangeTracker changeTracker; - private Map rowsTableRemovedColumns; - private ClusterConfig clusterConfig; + private JPanel topPanel; + private JList tablesList; + private DefaultListModel tablesListModel; + private JTable rowsTable; + private JButton columnPopulate; + private JTable columnsTable; + private JButton rowSave; + private JButton rowDelete; + private JButton rowAdd; + private JButton tableTruncate; + private JButton columnScan; + private JButton tableAdd; + private JButton tableDelete; + private JSpinner rowsNumber; + private JButton rowsPrev; + private JButton rowsNext; + private JLabel rowsTotal; + private JLabel rowsVisible; + private JButton columnCheck; + private JButton rowCopy; + private JButton rowPaste; + private JButton tableRefresh; + private JButton tableCopy; + private JButton tablePaste; + private JButton columnUncheck; + private JSplitPane topSplitPane; + private JSplitPane innerSplitPane; + private JLabel columnsNumber; + private JLabel tablesNumber; + private JComboBox tableFilters; + private DefaultComboBoxModel tablesFilterModel; + private JComboBox columnFilters; + private DefaultComboBoxModel columnsFilterModel; + private ItemListener columnsFilterListener; + private JButton columnJump; + private JButton rowOpen; + private JButton tableImport; + private JButton tableExport; + private JButton columnRefresh; + private JButton tableFlush; + private JButton tableMetadata; + private JButton columnAddConverter; + private JButton columnEditConverter; + private JButton columnDeleteConverter; + private WideComboBox columnConverters; + private JLabel rowsNumberIcon; + private DefaultTableModel columnsTableModel; + private DefaultTableModel rowsTableModel; + private Query lastQuery; + private QueryScanner scanner; + private JPanel owner; + private Connection connection; + private ChangeTracker changeTracker; + private Map rowsTableRemovedColumns; + private ClusterConfig clusterConfig; + private JComboBox cmbColumnTypes; + private RunnableAction rowsCountAction; //endregion //region Constructor @@ -126,27 +140,27 @@ public DesignerView(final JPanel owner, final Connection connection) { this.owner = owner; this.connection = connection; this.changeTracker = new ChangeTracker(); - this.rowsTableRemovedColumns = new HashMap(); + this.rowsTableRemovedColumns = new HashMap(); this.clusterConfig = new ClusterConfig(this.connection.getServerName()); this.clusterConfig.setConnection(connection.getConnectionDetails()); this.tablesFilterModel = new DefaultComboBoxModel(); - this.tablesFilter.setModel(this.tablesFilterModel); + this.tableFilters.setModel(this.tablesFilterModel); this.columnsFilterModel = new DefaultComboBoxModel(); - this.columnsFilter.setModel(this.columnsFilterModel); + this.columnFilters.setModel(this.columnsFilterModel); - fillComboBox(tablesFilter, this.clusterConfig.getTableFilters()); + fillComboBox(tableFilters, null, this.clusterConfig.getTableFilters()); String tableFilter = this.clusterConfig.getSelectedTableFilter(); if (tableFilter != null) { - this.tablesFilter.setSelectedItem(tableFilter); + this.tableFilters.setSelectedItem(tableFilter); } InMemoryClipboard.addListener( new ClipboardListener() { @Override public void onChanged(ClipboardData data) { - pasteTableButton.setEnabled(hasTableInClipboard()); - pasteRowButton.setEnabled(hasRowsInClipboard()); + tablePaste.setEnabled(hasTableInClipboard()); + rowPaste.setEnabled(hasRowsInClipboard()); } }); @@ -163,9 +177,9 @@ public void onChanged(ClipboardData data) { dividerContainer.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0)); dividerContainer.setLayout(new BorderLayout()); - this.rowsNumberSpinner.setModel(new SpinnerNumberModel(100, 1, 10000, 100)); + this.rowsNumber.setModel(new SpinnerNumberModel(100, 1, 10000, 100)); - this.tablesFilter.addItemListener( + this.tableFilters.addItemListener( new ItemListener() { @Override public void itemStateChanged(ItemEvent e) { @@ -177,64 +191,89 @@ public void itemStateChanged(ItemEvent e) { } }); - this.tablesFilter.getEditor().addActionListener( + this.tableFilters.getEditor().addActionListener( new ActionListener() { @Override public void actionPerformed(ActionEvent e) { - String item = (String)tablesFilter.getEditor().getItem(); + String item = (String)tableFilters.getEditor().getItem(); if (item != null && !item.isEmpty()) { if (tablesFilterModel.getIndexOf(item) == -1) { - tablesFilter.addItem(item); + tableFilters.addItem(item); } - tablesFilter.setSelectedItem(item); - clusterConfig.setTablesFilter(getFilters(tablesFilter)); + tableFilters.setSelectedItem(item); + clusterConfig.setTablesFilter(getFilters(tableFilters)); } else { - Object selectedItem = tablesFilter.getSelectedItem(); + Object selectedItem = tableFilters.getSelectedItem(); if (selectedItem != null) { - tablesFilter.removeItem(selectedItem); - clusterConfig.setTablesFilter(getFilters(tablesFilter)); + tableFilters.removeItem(selectedItem); + clusterConfig.setTablesFilter(getFilters(tableFilters)); } } } }); - this.columnsFilter.addItemListener( - new ItemListener() { - @Override - public void itemStateChanged(ItemEvent e) { - if (e.getStateChange() == ItemEvent.SELECTED) { - clusterConfig.setSelectedColumnFilter(getSelectedTableName(), (String)e.getItem()); + this.columnsFilterListener = new ItemListener() { + @Override + public void itemStateChanged(ItemEvent e) { + if (e.getStateChange() == ItemEvent.SELECTED) { + clusterConfig.setSelectedColumnFilter(getSelectedTableName(), (String)e.getItem()); - populateColumnsTable(false); - } + populateColumnsTable(false); } - }); + } + }; + + this.columnFilters.addItemListener(this.columnsFilterListener); - this.columnsFilter.getEditor().addActionListener( + this.columnFilters.getEditor().addActionListener( new ActionListener() { @Override public void actionPerformed(ActionEvent e) { - String item = (String)columnsFilter.getEditor().getItem(); + String item = (String)columnFilters.getEditor().getItem(); if (item != null && !item.isEmpty()) { if (columnsFilterModel.getIndexOf(item) == -1) { - columnsFilter.addItem(item); + columnFilters.addItem(item); } - columnsFilter.setSelectedItem(item); - clusterConfig.setColumnsFilter(getSelectedTableName(), getFilters(columnsFilter)); + columnFilters.setSelectedItem(item); + clusterConfig.setColumnsFilter(getSelectedTableName(), getFilters(columnFilters)); } else { - Object selectedItem = columnsFilter.getSelectedItem(); + Object selectedItem = columnFilters.getSelectedItem(); if (selectedItem != null) { - columnsFilter.removeItem(selectedItem); - clusterConfig.setColumnsFilter(getSelectedTableName(), getFilters(columnsFilter)); + columnFilters.removeItem(selectedItem); + clusterConfig.setColumnsFilter(getSelectedTableName(), getFilters(columnFilters)); } } } }); + this.columnConverters.addItemListener( + new ItemListener() { + @Override + public void itemStateChanged(ItemEvent e) { + if (e.getStateChange() == ItemEvent.SELECTED) { + TypeConverter nameConverter = getColumnNameConverter(); + updateColumnNameConverter(nameConverter); + + boolean isEditable = nameConverter.isEditable(); + + int row = columnsTable.getSelectedRow(); + if (row != -1) { + ColumnType type = (ColumnType)columnsTable.getValueAt(row, 2); + isEditable |= type.isEditable(); + } + + clusterConfig.setTableConfig(getSelectedTableName(), "nameConverter", nameConverter.getName()); + + columnEditConverter.setEnabled(isEditable); + columnDeleteConverter.setEnabled(isEditable); + } + } + }); + this.connection.addListener( new HbaseActionListener() { @Override @@ -274,7 +313,7 @@ public void columnOperation(String tableName, String column, String operation) { } }); - this.jumpButton.addActionListener( + this.columnJump.addActionListener( new ActionListener() { @Override public void actionPerformed(ActionEvent e) { @@ -298,7 +337,7 @@ public void actionPerformed(ActionEvent e) { } }); - this.populateButton.addActionListener( + this.columnPopulate.addActionListener( new ActionListener() { @Override public void actionPerformed(ActionEvent e) { @@ -310,7 +349,7 @@ public void actionPerformed(ActionEvent e) { } }); - this.scanButton.addActionListener( + this.columnScan.addActionListener( new ActionListener() { @Override public void actionPerformed(ActionEvent e) { @@ -326,7 +365,7 @@ public void actionPerformed(ActionEvent e) { } }); - this.openInViewerButton.addActionListener( + this.rowOpen.addActionListener( new ActionListener() { @Override public void actionPerformed(ActionEvent e) { @@ -368,7 +407,7 @@ public void actionPerformed(ActionEvent e) { } }); - this.addRowButton.addActionListener( + this.rowAdd.addActionListener( new ActionListener() { @Override public void actionPerformed(ActionEvent e) { @@ -395,7 +434,7 @@ public void actionPerformed(ActionEvent e) { // Update the column types according to the added row. for (DataCell cell : row.getCells()) { clusterConfig.setTableConfig( - getSelectedTableName(), cell.getColumn().getFullName(), cell.getTypedValue().getType().toString()); + getSelectedTableName(), cell.getColumn().getFullName(), cell.getType().toString()); } populateColumnsTable(true, row); @@ -415,7 +454,7 @@ public void actionPerformed(ActionEvent e) { } }); - this.deleteRowButton.addActionListener( + this.rowDelete.addActionListener( new ActionListener() { @Override public void actionPerformed(ActionEvent e) { @@ -455,7 +494,7 @@ public void actionPerformed(ActionEvent e) { }); - this.copyRowButton.addActionListener( + this.rowCopy.addActionListener( new ActionListener() { @Override public void actionPerformed(ActionEvent e) { @@ -467,7 +506,7 @@ public void actionPerformed(ActionEvent e) { } }); - this.pasteRowButton.addActionListener( + this.rowPaste.addActionListener( new ActionListener() { @Override public void actionPerformed(ActionEvent e) { @@ -479,7 +518,7 @@ public void actionPerformed(ActionEvent e) { } }); - this.updateRowButton.addActionListener( + this.rowSave.addActionListener( new ActionListener() { @Override public void actionPerformed(ActionEvent e) { @@ -505,7 +544,7 @@ public void actionPerformed(ActionEvent e) { } changeTracker.clear(); - updateRowButton.setEnabled(changeTracker.hasChanges()); + rowSave.setEnabled(changeTracker.hasChanges()); } finally { owner.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); @@ -514,7 +553,7 @@ public void actionPerformed(ActionEvent e) { } }); - this.addTableButton.addActionListener( + this.tableAdd.addActionListener( new ActionListener() { @Override public void actionPerformed(ActionEvent e) { @@ -528,7 +567,7 @@ public void actionPerformed(ActionEvent e) { Filter filter; - String value = (String)tablesFilter.getSelectedItem(); + String value = (String)tableFilters.getSelectedItem(); if (value == null || value.isEmpty()) { filter = new EmptyFilter(); } @@ -551,7 +590,7 @@ public void actionPerformed(ActionEvent e) { } }); - this.deleteTableButton.addActionListener( + this.tableDelete.addActionListener( new ActionListener() { @Override public void actionPerformed(ActionEvent e) { @@ -580,7 +619,7 @@ public void actionPerformed(ActionEvent e) { } }); - this.truncateTableButton.addActionListener( + this.tableTruncate.addActionListener( new ActionListener() { @Override public void actionPerformed(ActionEvent e) { @@ -615,7 +654,7 @@ public void actionPerformed(ActionEvent e) { } }); - this.showPrevPageButton.addActionListener( + this.rowsPrev.addActionListener( new ActionListener() { @Override public void actionPerformed(ActionEvent e) { @@ -625,7 +664,7 @@ public void actionPerformed(ActionEvent e) { } }); - this.showNextPageButton.addActionListener( + this.rowsNext.addActionListener( new ActionListener() { @Override public void actionPerformed(ActionEvent e) { @@ -635,7 +674,7 @@ public void actionPerformed(ActionEvent e) { } }); - this.checkAllButton.addActionListener( + this.columnCheck.addActionListener( new ActionListener() { @Override public void actionPerformed(ActionEvent e) { @@ -653,7 +692,7 @@ public void actionPerformed(ActionEvent e) { } }); - this.uncheckAllButton.addActionListener( + this.columnUncheck.addActionListener( new ActionListener() { @Override public void actionPerformed(ActionEvent e) { @@ -671,7 +710,7 @@ public void actionPerformed(ActionEvent e) { } }); - this.flushTableButton.addActionListener( + this.tableFlush.addActionListener( new ActionListener() { @Override public void actionPerformed(ActionEvent e) { @@ -699,7 +738,7 @@ public void actionPerformed(ActionEvent e) { } }); - this.refreshTablesButton.addActionListener( + this.tableRefresh.addActionListener( new ActionListener() { @Override public void actionPerformed(ActionEvent e) { @@ -709,7 +748,7 @@ public void actionPerformed(ActionEvent e) { } }); - this.copyTableButton.addActionListener( + this.tableCopy.addActionListener( new ActionListener() { @Override public void actionPerformed(ActionEvent e) { @@ -719,7 +758,7 @@ public void actionPerformed(ActionEvent e) { } }); - this.pasteTableButton.addActionListener( + this.tablePaste.addActionListener( new ActionListener() { @Override public void actionPerformed(ActionEvent e) { @@ -729,7 +768,7 @@ public void actionPerformed(ActionEvent e) { } }); - this.exportTableButton.addActionListener( + this.tableExport.addActionListener( new ActionListener() { @Override public void actionPerformed(ActionEvent e) { @@ -755,7 +794,7 @@ public void actionPerformed(ActionEvent e) { } }); - this.importTableButton.addActionListener( + this.tableImport.addActionListener( new ActionListener() { @Override public void actionPerformed(ActionEvent e) { @@ -773,7 +812,9 @@ public void actionPerformed(ActionEvent e) { columnFamilies = new ArrayList(); } - ImportTableDialog dialog = new ImportTableDialog(connection, tableName, getShownTypedColumns(), columnFamilies); + ImportTableDialog dialog = new ImportTableDialog( + connection, tableName, getColumnNameConverter(), getShownTypedColumns(), columnFamilies); + dialog.showDialog(topPanel); } catch (Exception ex) { @@ -785,7 +826,7 @@ public void actionPerformed(ActionEvent e) { } }); - this.tableMetadataButton.addActionListener( + this.tableMetadata.addActionListener( new ActionListener() { @Override public void actionPerformed(ActionEvent e) { @@ -817,7 +858,7 @@ public void actionPerformed(ActionEvent e) { } }); - this.refreshColumnsButton.addActionListener( + this.columnRefresh.addActionListener( new ActionListener() { @Override public void actionPerformed(ActionEvent e) { @@ -827,6 +868,128 @@ public void actionPerformed(ActionEvent e) { populateColumnsTable(true); } }); + + this.columnAddConverter.addActionListener( + new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + clearError(); + + JTableModel.stopCellEditing(columnsTable); + + CustomConverterDialog dialog = new CustomConverterDialog(null); + if (dialog.showDialog(topPanel)) { + ConvertersLoader.reload(); + } + } + }); + + this.columnEditConverter.addActionListener( + new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + clearError(); + + JTableModel.stopCellEditing(columnsTable); + + ColumnType columnType = getSelectedEditableColumnType(); + if (columnType != null) { + CustomConverterDialog dialog = new CustomConverterDialog(columnType.getConverter()); + if (dialog.showDialog(topPanel)) { + ConvertersLoader.editConverter(columnType.getName(), dialog.getConverterName()); + } + } + } + }); + + this.columnDeleteConverter.addActionListener( + new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + clearError(); + + JTableModel.stopCellEditing(columnsTable); + + ColumnType columnType = getSelectedEditableColumnType(); + if (columnType != null) { + int decision = JOptionPane.showConfirmDialog( + topPanel, String.format( + "Are you sure you want to delete '%s' type?%sIt will be lost for good!", columnType.getName(), PathHelper.LINE_SEPARATOR), + "Confirmation", JOptionPane.OK_CANCEL_OPTION); + + if (decision == JOptionPane.OK_OPTION) { + ConvertersLoader.removeConverter(columnType.getName()); + } + } + } + }); + + ConvertersLoader.addHandler( + new ConvertersLoaderHandler() { + @Override + public void onLoad() { + reloadColumnTypes(); + } + + @Override + public void onEdit(String oldName, String newName) { + ColumnType newType = ColumnType.fromName(newName); + columnConverters.setSelectedItem(newType); + + for (int row = 0 ; row < columnsTable.getRowCount() ; row++) { + ColumnType type = (ColumnType)columnsTable.getValueAt(row, 2); + if (type.getName().equals(oldName)) { + columnsTable.setValueAt(newType, row, 2); + + ColumnQualifier qualifier = (ColumnQualifier)columnsTable.getValueAt(row, 1); + updateColumnType(qualifier, newType); + } + } + + if (lastQuery != null) { + if (oldName.equals(lastQuery.getStartKeyType().getName())) { + lastQuery.setStartKeyType(newType); + } + + if (oldName.equals(lastQuery.getEndKeyType().getName())) { + lastQuery.setEndKeyType(newType); + } + + if (lastQuery.getWordType() != null && oldName.equals(lastQuery.getWordType().getName())) { + lastQuery.setWordType(ColumnType.fromName(lastQuery.getWordType().getName())); + } + } + } + + @Override + public void onRemove(String name) { + for (int row = 0 ; row < columnsTable.getRowCount() ; row++) { + ColumnType type = (ColumnType)columnsTable.getValueAt(row, 2); + if (type.getName().equals(name)) { + ColumnQualifier qualifier = (ColumnQualifier)columnsTable.getValueAt(row, 1); + + type = ColumnType.fromColumn(qualifier.getName()); + columnsTable.setValueAt(type, row, 2); + + updateColumnType(qualifier, type); + } + } + + if (lastQuery != null) { + if (lastQuery.getStartKeyType() != null) { + lastQuery.setStartKeyType(ColumnType.fromNameOrDefault(lastQuery.getStartKeyType().getName(), ColumnType.BinaryString)); + } + + if (lastQuery.getEndKeyType() != null) { + lastQuery.setEndKeyType(ColumnType.fromNameOrDefault(lastQuery.getEndKeyType().getName(), ColumnType.BinaryString)); + } + + if (lastQuery.getWordType() != null) { + lastQuery.setWordType(ColumnType.fromNameOrDefault(lastQuery.getWordType().getName(), ColumnType.String)); + } + } + } + }); } //endregion @@ -934,7 +1097,7 @@ private static TableColumn getColumn(String columnName, JTable table) { * Clears the previously set error messages. */ private static void clearError() { - MessageHandler.addError("", null); + MessageHandler.addInfo(""); } /** @@ -956,6 +1119,15 @@ private static void setInfo(String message) { MessageHandler.addInfo(message); } + /** + * Sets the action. + * + * @param action The action to set. + */ + private static void setAction(UIAction action) { + MessageHandler.addAction(action); + } + /** * Loads the table names. */ @@ -964,15 +1136,6 @@ private void loadTables() { clearError(); - this.refreshTablesButton.setEnabled(false); - this.addTableButton.setEnabled(false); - this.deleteTableButton.setEnabled(false); - this.truncateTableButton.setEnabled(false); - this.copyTableButton.setEnabled(false); - this.exportTableButton.setEnabled(false); - this.tableMetadataButton.setEnabled(false); - this.importTableButton.setEnabled(false); - this.owner.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); try { @@ -980,7 +1143,7 @@ private void loadTables() { Filter filter; - String value = (String)tablesFilter.getSelectedItem(); + String value = (String)tableFilters.getSelectedItem(); if (value == null || value.isEmpty()) { filter = new EmptyFilter(); } @@ -996,8 +1159,8 @@ private void loadTables() { } } - this.addTableButton.setEnabled(true); - this.importTableButton.setEnabled(true); + toggleTableControls(); + this.tablesNumber.setText(String.format("%s of %s", this.tablesListModel.getSize(), tables.size())); this.tablesList.setSelectedValue(selectedTable, true); } @@ -1005,7 +1168,6 @@ private void loadTables() { setError("Failed to connect to hadoop: ", ex); } finally { - this.refreshTablesButton.setEnabled(true); this.owner.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); } } @@ -1014,80 +1176,114 @@ private void loadTables() { * Initializes a table list used to present all available tables in the hbase cluster. */ private void initializeTablesList() { - this.pasteTableButton.setEnabled(hasTableInClipboard()); - this.tablesListModel = new DefaultListModel(); this.tablesList.setModel(this.tablesListModel); + this.tablesList.setCellRenderer(new JListRenderer(this.connection)); this.tablesList.addListSelectionListener( new ListSelectionListener() { @Override public void valueChanged(ListSelectionEvent e) { - int selectedIndices = tablesList.getSelectedIndices().length; + if (!e.getValueIsAdjusting()) { + if (rowsCountAction != null) { + rowsCountAction.abort(); + } - flushTableButton.setEnabled(selectedIndices > 0); - deleteTableButton.setEnabled(selectedIndices > 0); - truncateTableButton.setEnabled(selectedIndices > 0); + toggleTableControls(); - if (selectedIndices == 1) { - copyTableButton.setEnabled(true); - exportTableButton.setEnabled(true); - tableMetadataButton.setEnabled(true); + boolean populate = false; + int[] selectedIndices = tablesList.getSelectedIndices(); - scanner = null; + if (selectedIndices.length == 1) { + populate = tableEnabled(getSelectedTableName()); + } - String currentFilter = clusterConfig.getSelectedColumnFilter(getSelectedTableName()); + if (populate) { + scanner = null; - fillComboBox(columnsFilter, clusterConfig.getColumnFilters(getSelectedTableName())); - setFilter(columnsFilter, currentFilter); + String currentFilter = clusterConfig.getSelectedColumnFilter(getSelectedTableName()); - populateColumnsTable(true); - } - else { - clearRows(columnsTable); - clearTable(rowsTable); - - enableDisablePagingButtons(); - - copyTableButton.setEnabled(false); - exportTableButton.setEnabled(false); - tableMetadataButton.setEnabled(false); - rowsNumberLabel.setText("?"); - visibleRowsLabel.setText("?"); - pasteRowButton.setEnabled(false); - populateButton.setEnabled(false); - jumpButton.setEnabled(false); - scanButton.setEnabled(false); - addRowButton.setEnabled(false); - checkAllButton.setEnabled(false); - uncheckAllButton.setEnabled(false); - refreshColumnsButton.setEnabled(false); + fillComboBox(columnFilters, columnsFilterListener, clusterConfig.getColumnFilters(getSelectedTableName())); + setFilter(columnFilters, columnsFilterListener, currentFilter); + + String converterType = clusterConfig.getTableConfig(String.class, getSelectedTableName(), "nameConverter"); + if (converterType != null) { + columnConverters.setSelectedItem(ColumnType.fromNameOrDefault(converterType, ColumnType.BinaryString)); + } + else { + columnConverters.setSelectedItem(ColumnType.BinaryString); + } + + populateColumnsTable(true); + } + else { + clearRows(columnsTable); + clearTable(rowsTable); + + togglePagingControls(); + toggleColumnControls(false); + toggleRowControls(false); + + rowsTotal.setText("?"); + rowsVisible.setText("?"); + + if (selectedIndices.length == 1) { + setAction( + new UIAction() { + @Override + public void execute() { + String tableName = getSelectedTableName(); + + owner.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); + try { + connection.enableTable(tableName); + + tablesList.clearSelection(); + tablesList.setSelectedValue(tableName, true); + + setInfo(String.format("The '%s' table has been successfully enabled.", tableName)); + } + catch (Exception ex) { + setError(String.format("Failed to enable table '%s'", tableName), ex); + } + finally { + owner.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); + } + } + + @Override + public String[] getFormattedMessage() { + return new String[]{ + "The selected table is disabled, do you want to", "enable", "it?" + }; + } + }); + } + } } } }); this.tablesList.addKeyListener( - new - - KeyAdapter() { - @Override - public void keyReleased(KeyEvent e) { - if (e.isControlDown()) { - if (e.getKeyCode() == KeyEvent.VK_C) { - clearError(); - if (copyTableButton.isEnabled()) { - copyTableToClipboard(); - } + new KeyAdapter() { + @Override + public void keyReleased(KeyEvent e) { + if (e.isControlDown()) { + if (e.getKeyCode() == KeyEvent.VK_C) { + clearError(); + if (tableCopy.isEnabled()) { + copyTableToClipboard(); } - else if (e.getKeyCode() == KeyEvent.VK_V) { - clearError(); - if (pasteTableButton.isEnabled()) { - pasteTableFromClipboard(); - } + } + else if (e.getKeyCode() == KeyEvent.VK_V) { + clearError(); + if (tablePaste.isEnabled()) { + pasteTableFromClipboard(); } } } - }); + } + }); } /** @@ -1104,16 +1300,25 @@ private void initializeColumnsTable() { this.columnsTable.getColumn("Show").setCellRenderer(new JCheckBoxRenderer(new CheckedRow(1, ColumnQualifier.KEY))); this.columnsTable.getColumn("Show").setCellEditor(new JCheckBoxRenderer(new CheckedRow(1, ColumnQualifier.KEY))); - this.columnsTable.getColumn("Show").setPreferredWidth(40); - this.columnsTable.getColumn("Column Name").setPreferredWidth(110); + this.columnsTable.getColumn("Show").setPreferredWidth(50); + this.columnsTable.getColumn("Show").setMaxWidth(50); + this.columnsTable.getColumn("Show").setMinWidth(50); + + this.cmbColumnTypes = new WideComboBox(); - JComboBox comboBox = new JComboBox(); + for (ColumnType columnType : ColumnType.getTypes()) { + this.cmbColumnTypes.addItem(columnType); + } - for (ObjectType objectType : ObjectType.values()) { - comboBox.addItem(objectType); + for (ColumnType columnType : ColumnType.getNameTypes()) { + this.columnConverters.addItem(columnType); } - this.columnsTable.getColumn("Column Type").setCellEditor(new DefaultCellEditor(comboBox)); + this.columnConverters.setSelectedItem(ColumnType.BinaryString); + this.columnsTable.getColumn("Column Type").setCellEditor(new DefaultCellEditor(this.cmbColumnTypes)); + this.columnsTable.getColumn("Column Type").setPreferredWidth(82); + this.columnsTable.getColumn("Column Type").setMaxWidth(300); + this.columnsTable.getColumn("Column Type").setMinWidth(82); this.columnsTable.getModel().addTableModelListener( new TableModelListener() { @@ -1132,26 +1337,18 @@ public void tableChanged(TableModelEvent e) { clusterConfig.setTableConfig(getSelectedTableName(), qualifier.getFullName(), "isShown", Boolean.toString(isShown)); - setRowsTableColumnVisible(qualifier.getFullName(), isShown); + setRowsTableColumnVisible(qualifier, isShown); } else if ("Column Type".equals(columnName)) { ColumnQualifier qualifier = (ColumnQualifier)model.getValueAt(e.getFirstRow(), 1); - ObjectType type = (ObjectType)model.getValueAt(e.getFirstRow(), column); - - clusterConfig.setTableConfig(getSelectedTableName(), qualifier.getFullName(), type.toString()); + ColumnType type = (ColumnType)model.getValueAt(e.getFirstRow(), column); - JTableModel.stopCellEditing(rowsTable); + TypeConverter nameConverter = getColumnNameConverter(); - if (scanner != null) { - try { - scanner.updateColumnType(qualifier.getFullName(), type); - } - catch (Exception ex) { - setError(String.format("The selected type '%s' does not match the data.", type), ex); - } + columnEditConverter.setEnabled(type.isEditable() || nameConverter.isEditable()); + columnDeleteConverter.setEnabled(type.isEditable() || nameConverter.isEditable()); - rowsTable.updateUI(); - } + updateColumnType(qualifier, type); } } }); @@ -1168,6 +1365,23 @@ public void mouseMoved(MouseEvent e) { } } }); + + this.columnsTable.getSelectionModel().addListSelectionListener( + new ListSelectionListener() { + @Override + public void valueChanged(ListSelectionEvent e) { + boolean isEditable = getColumnNameConverter().isEditable(); + + int row = columnsTable.getSelectedRow(); + if (row != -1) { + ColumnType type = (ColumnType)columnsTable.getValueAt(row, 2); + isEditable |= type.isEditable(); + } + + columnEditConverter.setEnabled(isEditable); + columnDeleteConverter.setEnabled(isEditable); + } + }); } /** @@ -1185,8 +1399,7 @@ private void initializeRowsTable() { new ListSelectionListener() { @Override public void valueChanged(ListSelectionEvent e) { - deleteRowButton.setEnabled(true); - copyRowButton.setEnabled(true); + toggleRowControls(true); } }); @@ -1211,7 +1424,7 @@ public void keyReleased(KeyEvent e) { if (e.getKeyCode() == KeyEvent.VK_C) { clearError(); - if (copyRowButton.isEnabled()) { + if (rowCopy.isEnabled()) { JTableModel.stopCellEditing(rowsTable); copySelectedRowsToClipboard(); } @@ -1219,7 +1432,7 @@ public void keyReleased(KeyEvent e) { else if (e.getKeyCode() == KeyEvent.VK_V) { clearError(); - if (pasteRowButton.isEnabled()) { + if (rowPaste.isEnabled()) { JTableModel.stopCellEditing(rowsTable); pasteRowsFromClipboard(); } @@ -1234,9 +1447,8 @@ else if (e.getKeyCode() == KeyEvent.VK_V) { public void propertyChange(PropertyChangeEvent evt) { if ("model".equals(evt.getPropertyName())) { changeTracker.clear(); - updateRowButton.setEnabled(changeTracker.hasChanges()); - deleteRowButton.setEnabled(false); - copyRowButton.setEnabled(false); + + toggleRowControls(false); } } }); @@ -1275,7 +1487,7 @@ public void columnSelectionChanged(ListSelectionEvent e) { public void onCellChanged(DataCell cell) { clearError(); - updateRowButton.setEnabled(changeTracker.hasChanges()); + rowSave.setEnabled(changeTracker.hasChanges()); } }); } @@ -1305,37 +1517,19 @@ private void populateColumnsTable(boolean clearRows, DataRow row) { if (clearRows) { clearTable(rowsTable); - this.rowsNumberLabel.setText("?"); - this.visibleRowsLabel.setText("?"); - this.rowsNumberSpinner.setEnabled(true); - this.pasteRowButton.setEnabled(hasRowsInClipboard()); + this.rowsTotal.setText("?"); + this.rowsVisible.setText("?"); + this.rowsNumber.setEnabled(true); } - enableDisablePagingButtons(); - String tableName = getSelectedTableName(); if (tableName != null) { loadColumns(tableName, row); } - if (this.columnsTableModel.getRowCount() > 0) { - this.populateButton.setEnabled(true); - this.jumpButton.setEnabled(true); - this.scanButton.setEnabled(true); - this.addRowButton.setEnabled(true); - this.checkAllButton.setEnabled(true); - this.uncheckAllButton.setEnabled(true); - this.refreshColumnsButton.setEnabled(true); - } - else { - this.populateButton.setEnabled(false); - this.jumpButton.setEnabled(false); - this.scanButton.setEnabled(false); - this.addRowButton.setEnabled(false); - this.checkAllButton.setEnabled(false); - this.uncheckAllButton.setEnabled(false); - this.refreshColumnsButton.setEnabled(false); - } + togglePagingControls(); + toggleColumnControls(columnsTableModel.getRowCount() > 0); + toggleRowControls(rowsTableModel.getRowCount() > 0); } finally { this.owner.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); @@ -1344,7 +1538,7 @@ private void populateColumnsTable(boolean clearRows, DataRow row) { /** * Populates a rows table. The method loads the table content. The number of loaded rows depends on the parameter defined by the user - * in the {@link hrider.ui.views.DesignerView#rowsNumberSpinner} control. + * in the {@link hrider.ui.views.DesignerView#rowsNumber} control. * * @param direction Defines what rows should be presented to the user. {@link Direction#Current}, * {@link Direction#Forward} or {@link Direction#Backward}. @@ -1355,7 +1549,7 @@ private void populateRowsTable(Direction direction) { /** * Populates a rows table. The method loads the table content. The number of loaded rows depends on the parameter defined by the user - * in the {@link hrider.ui.views.DesignerView#rowsNumberSpinner} control. + * in the {@link hrider.ui.views.DesignerView#rowsNumber} control. * * @param offset The first row to start loading from. * @param direction Defines what rows should be presented to the user. {@link Direction#Current}, @@ -1367,13 +1561,11 @@ private void populateRowsTable(long offset, Direction direction) { try { JTableModel.stopCellEditing(columnsTable); - this.rowsNumberSpinner.setEnabled(true); - String tableName = getSelectedTableName(); if (tableName != null) { clearTable(rowsTable); - Map columnTypes = getColumnTypes(); + Map columnTypes = getColumnTypes(); this.scanner.setColumnTypes(columnTypes); this.scanner.setQuery(this.lastQuery); @@ -1392,33 +1584,45 @@ else if (direction == Direction.Forward) { loadColumns(tableName, null); loadRowsTableColumns(tableName); - loadRows(tableName, rows); + loadRows(rows); - this.enableDisablePagingButtons(); + this.togglePagingControls(); - this.visibleRowsLabel.setText(String.format("%s - %s", this.scanner.getLastRow() - rows.size() + 1, this.scanner.getLastRow())); - this.rowsNumberSpinner.setEnabled(this.scanner.getLastRow() <= getPageSize()); + this.rowsVisible.setText(String.format("%s - %s", this.scanner.getLastRow() - rows.size() + 1, this.scanner.getLastRow())); + this.rowsNumber.setEnabled(this.scanner.getLastRow() <= getPageSize()); - // To get the number of rows can take the time. - Thread thread = new Thread( - new Runnable() { - @Override - public void run() { - try { - long totalNumberOfRows = scanner.getRowsCount(); - rowsNumberLabel.setText(String.valueOf(totalNumberOfRows)); + this.rowsCountAction = RunnableAction.run( + tableName + "-rowsCount", new Action() { - enableDisablePagingButtons(); - } - catch (Exception e) { - setError("Failed to get the number of rows in the table.", e); - } + @Override + public Boolean run() throws IOException { + rowsNumberIcon.setVisible(true); + rowsTotal.setVisible(false); + + long totalNumberOfRows = scanner.getRowsCount(GlobalConfig.instance().getRowCountTimeout()); + if (scanner.isRowsCountPartiallyCalculated()) { + rowsTotal.setText("more than " + totalNumberOfRows); + } + else { + rowsTotal.setText(String.valueOf(totalNumberOfRows)); } - }); - thread.start(); + + rowsNumberIcon.setVisible(false); + rowsTotal.setVisible(true); + + togglePagingControls(); + + return true; + } + + @Override + public void onError(Exception ex) { + setError("Failed to get the number of rows in the table.", ex); + } + }); } - this.openInViewerButton.setEnabled(this.rowsTable.getRowCount() > 0); + toggleRowControls(rowsTableModel.getRowCount() > 0); } catch (Exception ex) { setError("Failed to fill rows: ", ex); @@ -1431,21 +1635,20 @@ public void run() { /** * Populates rows of the specified column. This method is used when the column which wasn't initially populated is checked. * - * @param tableName The name of the table the column belongs to. - * @param columnName The name of the column which rows are need to be populated. - * @param rows The data to populate. + * @param qualifier The name of the column which rows are need to be populated. + * @param rows The data to populate. */ - private void populateColumnOnRowsTable(String tableName, String columnName, Iterable rows) { + private void populateColumnOnRowsTable(ColumnQualifier qualifier, Iterable rows) { this.owner.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); try { int rowIndex = 0; - int columnIndex = this.rowsTable.getColumnModel().getColumnIndex(columnName); + int columnIndex = this.rowsTable.getColumnModel().getColumnIndex(qualifier); for (DataRow row : rows) { - DataCell cell = row.getCell(columnName); + DataCell cell = row.getCell(qualifier); if (cell == null) { - cell = new DataCell(row, new ColumnQualifier(columnName), new TypedObject(getColumnType(tableName, columnName), null)); + cell = new DataCell(row, qualifier, new ConvertibleObject(getColumnType(qualifier.getFullName()), null)); } if (rowIndex >= this.rowsTable.getRowCount()) { @@ -1472,11 +1675,12 @@ private void loadColumns(String tableName, DataRow row) { try { if (this.scanner == null) { this.scanner = connection.getScanner(tableName, null); + this.updateColumnNameConverter(getColumnNameConverter()); } Filter filter; - String value = (String)columnsFilter.getSelectedItem(); + String value = (String)columnFilters.getSelectedItem(); if (value == null || value.isEmpty()) { filter = new EmptyFilter(); } @@ -1486,8 +1690,8 @@ private void loadColumns(String tableName, DataRow row) { if (row == null) { this.scanner.setColumnTypes( - new HashMap() {{ - put(ColumnQualifier.KEY.getName(), ObjectType.String); + new HashMap() {{ + put(ColumnQualifier.KEY.getName(), ColumnType.String); }}); row = this.scanner.getFirstRow(); @@ -1500,7 +1704,7 @@ private void loadColumns(String tableName, DataRow row) { addColumnToColumnsTable(tableName, column, row); } - setRowsTableColumnVisible(column.getFullName(), isColumnVisible && isShown(tableName, column.getFullName())); + setRowsTableColumnVisible(column, isColumnVisible && isShown(tableName, column.getFullName())); } this.columnsNumber.setText(String.format("%s of %s", this.columnsTableModel.getRowCount(), columns.size())); @@ -1518,7 +1722,7 @@ private void loadColumns(String tableName, DataRow row) { * @param row The row that might contain column type information. */ private void addColumnToColumnsTable(String tableName, ColumnQualifier qualifier, DataRow row) { - ObjectType columnType = getColumnType(tableName, qualifier.getFullName()); + ColumnType columnType = getSavedColumnType(tableName, qualifier.getFullName()); if (columnType == null && row != null) { DataCell cell = row.getCell(qualifier); @@ -1528,7 +1732,7 @@ private void addColumnToColumnsTable(String tableName, ColumnQualifier qualifier } if (columnType == null) { - columnType = ObjectType.fromColumn(qualifier.getFullName()); + columnType = ColumnType.fromColumn(qualifier.getFullName()); } boolean isShown = isShown(tableName, qualifier.getFullName()); @@ -1548,7 +1752,7 @@ private void loadRowsTableColumns(String tableName) { boolean isShown = (Boolean)this.columnsTableModel.getValueAt(i, 0); if (isShown) { - addColumnToRowsTable(tableName, qualifier.getFullName(), j++); + addColumnToRowsTable(tableName, qualifier, j++); } } } @@ -1556,36 +1760,36 @@ private void loadRowsTableColumns(String tableName) { /** * Shows or hides a column in rows table. * - * @param columnName The name of the column. - * @param isVisible Indicates if the columns should be shown or hidden. + * @param qualifier The name of the column. + * @param isVisible Indicates if the columns should be shown or hidden. */ - private void setRowsTableColumnVisible(String columnName, boolean isVisible) { + private void setRowsTableColumnVisible(ColumnQualifier qualifier, boolean isVisible) { if (this.rowsTable.getRowCount() > 0) { if (isVisible) { - TableColumn tableColumn = this.rowsTableRemovedColumns.get(columnName); + TableColumn tableColumn = this.rowsTableRemovedColumns.get(qualifier); if (tableColumn != null) { this.rowsTable.addColumn(tableColumn); - this.rowsTableRemovedColumns.remove(columnName); + this.rowsTableRemovedColumns.remove(qualifier); - this.rowsTable.moveColumn(this.rowsTable.getColumnCount() - 1, getColumnIndex(columnName)); + this.rowsTable.moveColumn(this.rowsTable.getColumnCount() - 1, getColumnIndex(qualifier.getFullName())); } else { - if (getColumn(columnName, this.rowsTable) == null) { - addColumnToRowsTable(getSelectedTableName(), columnName, this.rowsTable.getColumnCount()); + if (getColumn(qualifier.getFullName(), this.rowsTable) == null) { + addColumnToRowsTable(getSelectedTableName(), qualifier, this.rowsTable.getColumnCount()); if (this.scanner != null) { - populateColumnOnRowsTable(getSelectedTableName(), columnName, this.scanner.current()); + populateColumnOnRowsTable(qualifier, this.scanner.current()); } - this.rowsTable.moveColumn(this.rowsTable.getColumnCount() - 1, getColumnIndex(columnName)); + this.rowsTable.moveColumn(this.rowsTable.getColumnCount() - 1, getColumnIndex(qualifier.getFullName())); } } } else { - TableColumn tableColumn = getColumn(columnName, this.rowsTable); + TableColumn tableColumn = getColumn(qualifier.getFullName(), this.rowsTable); if (tableColumn != null) { this.rowsTable.removeColumn(tableColumn); - this.rowsTableRemovedColumns.put(columnName, tableColumn); + this.rowsTableRemovedColumns.put(qualifier, tableColumn); } } } @@ -1595,17 +1799,17 @@ private void setRowsTableColumnVisible(String columnName, boolean isVisible) { * Adds a single column to the rows table. * * @param tableName The name of the table the column belongs to. - * @param columnName The name of the column to add. + * @param qualifier The name of the column to add. * @param columnIndex The index the column should be added at. */ - private void addColumnToRowsTable(String tableName, String columnName, int columnIndex) { + private void addColumnToRowsTable(String tableName, ColumnQualifier qualifier, int columnIndex) { TableColumn tableColumn = new TableColumn(columnIndex); - tableColumn.setIdentifier(columnName); - tableColumn.setHeaderValue(columnName); - tableColumn.setCellEditor(new JCellEditor(this.changeTracker, !ColumnQualifier.isKey(columnName))); + tableColumn.setIdentifier(qualifier); + tableColumn.setHeaderValue(qualifier); + tableColumn.setCellEditor(new JCellEditor(this.changeTracker, !ColumnQualifier.isKey(qualifier.getFullName()))); try { - Integer width = this.clusterConfig.getTableConfig(Integer.class, tableName, columnName, "size"); + Integer width = this.clusterConfig.getTableConfig(Integer.class, tableName, qualifier.getFullName(), "size"); if (width != null) { tableColumn.setPreferredWidth(width); } @@ -1614,7 +1818,7 @@ private void addColumnToRowsTable(String tableName, String columnName, int colum } this.rowsTable.addColumn(tableColumn); - this.rowsTableModel.addColumn(columnName); + this.rowsTableModel.addColumn(qualifier); } /** @@ -1622,16 +1826,18 @@ private void addColumnToRowsTable(String tableName, String columnName, int colum * * @param rows A list of rows to add. */ - private void loadRows(String tableName, Iterable rows) { + private void loadRows(Iterable rows) { clearRows(this.rowsTable); + TypeConverter nameConverter = getColumnNameConverter(); + for (DataRow row : rows) { Collection values = new ArrayList(rowsTable.getColumnCount()); for (int i = 0 ; i < rowsTable.getColumnCount() ; i++) { String columnName = rowsTable.getColumnName(i); DataCell cell = row.getCell(columnName); if (cell == null) { - cell = new DataCell(row, new ColumnQualifier(columnName), new TypedObject(getColumnType(tableName, columnName), null)); + cell = new DataCell(row, new ColumnQualifier(columnName, nameConverter), new ConvertibleObject(getColumnType(columnName), null)); } values.add(cell); } @@ -1680,9 +1886,8 @@ private void copyTableToClipboard() { /** * Pastes rows from the clipboard into the rows table and into the hbase table that the rows table represents. */ + @SuppressWarnings("OverlyNestedMethod") private void pasteRowsFromClipboard() { - this.pasteRowButton.setEnabled(false); - ClipboardData clipboardData = InMemoryClipboard.getData(); if (clipboardData != null) { DataTable table = clipboardData.getData(); @@ -1699,7 +1904,7 @@ private void pasteRowsFromClipboard() { // Update the column types according to the added row. for (DataCell cell : row.getCells()) { this.clusterConfig.setTableConfig( - getSelectedTableName(), cell.getColumn().getFullName(), cell.getTypedValue().getType().toString()); + getSelectedTableName(), cell.getColumn().getFullName(), cell.getType().toString()); } populateColumnsTable(true, row); @@ -1715,8 +1920,8 @@ private void pasteRowsFromClipboard() { sortedRows, new Comparator() { @Override public int compare(DataRow o1, DataRow o2) { - byte[] a1 = o1.getKey().toByteArray(); - byte[] a2 = o2.getKey().toByteArray(); + byte[] a1 = o1.getKey().getValue(); + byte[] a2 = o2.getKey().getValue(); if (a1.length != a2.length) { return a1.length - a2.length; @@ -1750,8 +1955,9 @@ public int compare(DataRow o1, DataRow o2) { /** * Pastes a table from the clipboard into the application and an hbase cluster. */ + @SuppressWarnings("OverlyNestedMethod") private void pasteTableFromClipboard() { - this.pasteTableButton.setEnabled(false); + this.tablePaste.setEnabled(false); ClipboardData clipboardData = InMemoryClipboard.getData(); if (clipboardData != null) { @@ -1769,7 +1975,7 @@ private void pasteTableFromClipboard() { Filter filter; - String value = (String)tablesFilter.getSelectedItem(); + String value = (String)tableFilters.getSelectedItem(); if (value == null || value.isEmpty()) { filter = new EmptyFilter(); } @@ -1806,11 +2012,11 @@ private void pasteTableFromClipboard() { * * @return A column names to column types map. */ - private Map getColumnTypes() { - Map columnTypes = new HashMap(); + private Map getColumnTypes() { + Map columnTypes = new HashMap(); for (int i = 0 ; i < columnsTable.getRowCount() ; i++) { ColumnQualifier qualifier = (ColumnQualifier)columnsTableModel.getValueAt(i, 1); - columnTypes.put(qualifier.getFullName(), (ObjectType)columnsTableModel.getValueAt(i, 2)); + columnTypes.put(qualifier.getFullName(), (ColumnType)columnsTableModel.getValueAt(i, 2)); } return columnTypes; } @@ -1827,7 +2033,7 @@ private Collection getShownTypedColumns() { if (isShown) { typedColumns.add( new TypedColumn( - (ColumnQualifier)this.columnsTableModel.getValueAt(i, 1), (ObjectType)this.columnsTableModel.getValueAt(i, 2))); + (ColumnQualifier)this.columnsTableModel.getValueAt(i, 1), (ColumnType)this.columnsTableModel.getValueAt(i, 2))); } } return typedColumns; @@ -1855,7 +2061,7 @@ private List getShownColumns() { * @return The size of the page. */ private int getPageSize() { - return (Integer)this.rowsNumberSpinner.getValue(); + return (Integer)this.rowsNumber.getValue(); } /** @@ -1903,27 +2109,69 @@ private List getSelectedTables() { } /** - * Gets a list of populated tables. + * Gets the type of the column from the configuration. * - * @return A list of tables. + * @param tableName The name of the table that contains the column. + * @param columnName The name of the column. + * @return The column type. */ - private List getTables() { - List tables = new ArrayList(); - for (int i = 0 ; i < this.tablesListModel.size() ; i++) { - tables.add(this.tablesListModel.getElementAt(i).toString()); + private ColumnType getSavedColumnType(String tableName, String columnName) { + String columnType = this.clusterConfig.getTableConfig(String.class, tableName, columnName); + if (columnType != null) { + return ColumnType.fromName(columnType); } - return tables; + return null; } /** - * Gets the type of the column from the configuration. + * Gets the type of the column from the columns table. * - * @param tableName The name of the table that contains the column. * @param columnName The name of the column. - * @return The column type. + * @return The columns type */ - private ObjectType getColumnType(String tableName, String columnName) { - return this.clusterConfig.getTableConfig(ObjectType.class, tableName, columnName); + private ColumnType getColumnType(String columnName) { + for (int row = 0 ; row < columnsTable.getRowCount() ; row++) { + ColumnQualifier qualifier = (ColumnQualifier)columnsTable.getValueAt(row, 1); + if (qualifier.getFullName().endsWith(columnName)) { + return (ColumnType)columnsTable.getValueAt(row, 2); + } + } + return getSavedColumnType(getSelectedTableName(), columnName); + } + + /** + * Gets type converter for the column names. + * + * @return A selected type converter. + */ + private TypeConverter getColumnNameConverter() { + ColumnType type = (ColumnType)this.columnConverters.getSelectedItem(); + if (type != null) { + return type.getConverter(); + } + return ColumnType.BinaryString.getConverter(); + } + + /** + * Gets a column type that is currently selected and can be edited. The column type of the column name gets higher priority. + * + * @return A selected and editable column type if exists or null otherwise. + */ + private ColumnType getSelectedEditableColumnType() { + ColumnType type = (ColumnType)this.columnConverters.getSelectedItem(); + if (type != null && type.isEditable()) { + return type; + } + + int row = columnsTable.getSelectedRow(); + if (row != -1) { + type = (ColumnType)columnsTable.getValueAt(row, 2); + if (type.isEditable()) { + return type; + } + } + + return null; } /** @@ -1938,12 +2186,174 @@ private boolean isShown(String tableName, String columnName) { return isShown == null || isShown; } + /** + * Reloads column types. + */ + private void reloadColumnTypes() { + ColumnType selectedNameType = (ColumnType)this.columnConverters.getSelectedItem(); + + this.cmbColumnTypes.removeAllItems(); + this.columnConverters.removeAllItems(); + + for (ColumnType columnType : ColumnType.getTypes()) { + this.cmbColumnTypes.addItem(columnType); + } + + for (ColumnType columnType : ColumnType.getNameTypes()) { + this.columnConverters.addItem(columnType); + } + + if (selectedNameType != null) { + this.columnConverters.setSelectedItem(selectedNameType); + } + } + + /** + * Updates the type of the specified column including already loaded values to the rows table. + * + * @param qualifier The column to update. + * @param type The new column type. + */ + private void updateColumnType(ColumnQualifier qualifier, ColumnType type) { + clusterConfig.setTableConfig(getSelectedTableName(), qualifier.getFullName(), type.toString()); + + JTableModel.stopCellEditing(rowsTable); + + if (scanner != null) { + try { + scanner.updateColumnType(qualifier.getFullName(), type); + } + catch (Exception ex) { + setError(String.format("The selected type '%s' does not match the data.", type), ex); + } + + rowsTable.updateUI(); + } + } + + /** + * Updates converter for the column name. + * + * @param converter The new column name converter. + */ + private void updateColumnNameConverter(TypeConverter converter) { + for (int col = 0 ; col < rowsTable.getColumnCount() ; col++) { + TableColumn column = rowsTable.getColumnModel().getColumn(col); + + ColumnQualifier qualifier = (ColumnQualifier)column.getIdentifier(); + qualifier.setNameConverter(converter); + } + + rowsTable.updateUI(); + + for (int row = 0 ; row < columnsTable.getRowCount() ; row++) { + ColumnQualifier qualifier = (ColumnQualifier)columnsTable.getValueAt(row, 1); + qualifier.setNameConverter(converter); + } + + if (scanner != null) { + scanner.updateColumnNameConverter(converter); + } + + columnsTable.updateUI(); + + if (!rowsTableRemovedColumns.isEmpty()) { + Map removedColumns = new HashMap(); + for (TableColumn column : rowsTableRemovedColumns.values()) { + ColumnQualifier qualifier = (ColumnQualifier)column.getIdentifier(); + qualifier.setNameConverter(converter); + + removedColumns.put(qualifier, column); + } + rowsTableRemovedColumns = removedColumns; + } + } + /** * Enables or disables the paging buttons. */ - private void enableDisablePagingButtons() { - this.showPrevPageButton.setEnabled(this.scanner != null && this.scanner.hasPrev()); - this.showNextPageButton.setEnabled(this.scanner != null && this.scanner.hasNext()); + private void togglePagingControls() { + rowsPrev.setEnabled(this.scanner != null && this.scanner.hasPrev()); + rowsNext.setEnabled(this.scanner != null && this.scanner.hasNext()); + } + + private void toggleTableControls() { + if (tablesListModel.getSize() > 0) { + int[] selectedIndices = tablesList.getSelectedIndices(); + if (selectedIndices.length > 0) { + tableDelete.setEnabled(true); + tableTruncate.setEnabled(true); + + boolean tableEnabled = false; + + if (selectedIndices.length == 1) { + String tableName = (String)tablesListModel.getElementAt(selectedIndices[0]); + tableEnabled = tableEnabled(tableName); + } + + tableCopy.setEnabled(tableEnabled); + tablePaste.setEnabled(tableEnabled && hasTableInClipboard()); + tableExport.setEnabled(tableEnabled); + tableFlush.setEnabled(tableEnabled || selectedIndices.length > 1); + tableMetadata.setEnabled(tableEnabled); + } + } + else { + tableDelete.setEnabled(false); + tableTruncate.setEnabled(false); + tableCopy.setEnabled(false); + tablePaste.setEnabled(false); + tableExport.setEnabled(false); + tableFlush.setEnabled(false); + tableMetadata.setEnabled(false); + } + } + + private void toggleColumnControls(boolean enabled) { + columnRefresh.setEnabled(enabled); + columnFilters.setEnabled(enabled); + columnScan.setEnabled(enabled); + columnJump.setEnabled(enabled); + columnPopulate.setEnabled(enabled); + columnCheck.setEnabled(enabled); + columnUncheck.setEnabled(enabled); + + boolean isEditable = getColumnNameConverter().isEditable(); + + int row = columnsTable.getSelectedRow(); + if (row != -1) { + ColumnType type = (ColumnType)columnsTable.getValueAt(row, 2); + isEditable |= type.isEditable(); + } + + columnEditConverter.setEnabled(enabled && isEditable); + columnDeleteConverter.setEnabled(enabled && isEditable); + } + + private void toggleRowControls(boolean enabled) { + int count = rowsTable.getSelectedRowCount(); + + rowOpen.setEnabled(enabled && rowsTable.getRowCount() > 0); + rowDelete.setEnabled(enabled && count > 0); + rowCopy.setEnabled(enabled && count > 0); + rowPaste.setEnabled(hasRowsInClipboard()); + rowSave.setEnabled(changeTracker.hasChanges()); + + String tableName = getSelectedTableName(); + rowAdd.setEnabled(tableName != null && tableEnabled(tableName)); + } + + private boolean tableEnabled(String tableName) { + boolean enabled = false; + + try { + enabled = connection.tableEnabled(tableName); + } + catch (Exception ex) { + setError(String.format("Failed to access table '%s' information.", tableName), ex); + } + + return enabled; } /** @@ -1952,9 +2362,17 @@ private void enableDisablePagingButtons() { * @param comboBox A combo box to be set. * @param value A value to be selected on the combo box. */ - private static void setFilter(JComboBox comboBox, String value) { + private static void setFilter(JComboBox comboBox, ItemListener listener, String value) { + if (listener != null) { + comboBox.removeItemListener(listener); + } + comboBox.setSelectedItem(value); comboBox.setToolTipText(value); + + if (listener != null) { + comboBox.addItemListener(listener); + } } /** @@ -1963,8 +2381,12 @@ private static void setFilter(JComboBox comboBox, String value) { * @param comboBox A combo box to fill. * @param values A list of values to add. */ - private static void fillComboBox(JComboBox comboBox, Iterable values) { + private static void fillComboBox(JComboBox comboBox, ItemListener listener, Iterable values) { if (values != null) { + if (listener != null) { + comboBox.removeItemListener(listener); + } + comboBox.removeAllItems(); comboBox.addItem(""); @@ -1972,6 +2394,10 @@ private static void fillComboBox(JComboBox comboBox, Iterable values) { for (String value : values) { comboBox.addItem(value); } + + if (listener != null) { + comboBox.addItemListener(listener); + } } } @@ -2042,27 +2468,27 @@ panel2, new GridConstraints( toolBar1, new GridConstraints( 0, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_FIXED, null, new Dimension(-1, 20), null, 0, false)); - refreshTablesButton = new JButton(); - refreshTablesButton.setEnabled(true); - refreshTablesButton.setHorizontalAlignment(0); - refreshTablesButton.setIcon(new ImageIcon(getClass().getResource("/images/db-refresh.png"))); - refreshTablesButton.setMaximumSize(new Dimension(24, 24)); - refreshTablesButton.setMinimumSize(new Dimension(24, 24)); - refreshTablesButton.setPreferredSize(new Dimension(24, 24)); - refreshTablesButton.setText(""); - refreshTablesButton.setToolTipText("Refresh tables"); - toolBar1.add(refreshTablesButton); + tableRefresh = new JButton(); + tableRefresh.setEnabled(true); + tableRefresh.setHorizontalAlignment(0); + tableRefresh.setIcon(new ImageIcon(getClass().getResource("/images/db-refresh.png"))); + tableRefresh.setMaximumSize(new Dimension(24, 24)); + tableRefresh.setMinimumSize(new Dimension(24, 24)); + tableRefresh.setPreferredSize(new Dimension(24, 24)); + tableRefresh.setText(""); + tableRefresh.setToolTipText("Refresh tables"); + toolBar1.add(tableRefresh); final JToolBar.Separator toolBar$Separator1 = new JToolBar.Separator(); toolBar1.add(toolBar$Separator1); - tablesFilter = new JComboBox(); - tablesFilter.setAlignmentX(0.0f); - tablesFilter.setAlignmentY(0.6f); - tablesFilter.setEditable(true); - tablesFilter.setFont(new Font(tablesFilter.getFont().getName(), tablesFilter.getFont().getStyle(), tablesFilter.getFont().getSize())); - tablesFilter.setMaximumSize(new Dimension(32767, 26)); - tablesFilter.setMinimumSize(new Dimension(50, 20)); - tablesFilter.setPreferredSize(new Dimension(-1, -1)); - toolBar1.add(tablesFilter); + tableFilters = new JComboBox(); + tableFilters.setAlignmentX(0.0f); + tableFilters.setAlignmentY(0.6f); + tableFilters.setEditable(true); + tableFilters.setFont(new Font(tableFilters.getFont().getName(), tableFilters.getFont().getStyle(), tableFilters.getFont().getSize())); + tableFilters.setMaximumSize(new Dimension(32767, 26)); + tableFilters.setMinimumSize(new Dimension(50, 20)); + tableFilters.setPreferredSize(new Dimension(-1, -1)); + toolBar1.add(tableFilters); final JScrollPane scrollPane1 = new JScrollPane(); scrollPane1.setAlignmentX(0.5f); scrollPane1.setAlignmentY(0.5f); @@ -2088,91 +2514,91 @@ panel3, new GridConstraints( toolBar2, new GridConstraints( 0, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_FIXED, null, new Dimension(-1, 20), null, 0, false)); - addTableButton = new JButton(); - addTableButton.setEnabled(false); - addTableButton.setHorizontalAlignment(0); - addTableButton.setIcon(new ImageIcon(getClass().getResource("/images/add-table.png"))); - addTableButton.setMaximumSize(new Dimension(24, 24)); - addTableButton.setMinimumSize(new Dimension(24, 24)); - addTableButton.setPreferredSize(new Dimension(24, 24)); - addTableButton.setText(""); - addTableButton.setToolTipText("Create a new table"); - toolBar2.add(addTableButton); - deleteTableButton = new JButton(); - deleteTableButton.setEnabled(false); - deleteTableButton.setHorizontalAlignment(0); - deleteTableButton.setIcon(new ImageIcon(getClass().getResource("/images/delete-table.png"))); - deleteTableButton.setMaximumSize(new Dimension(24, 24)); - deleteTableButton.setMinimumSize(new Dimension(24, 24)); - deleteTableButton.setPreferredSize(new Dimension(24, 24)); - deleteTableButton.setText(""); - deleteTableButton.setToolTipText("Delete selected table(s)"); - toolBar2.add(deleteTableButton); - truncateTableButton = new JButton(); - truncateTableButton.setEnabled(false); - truncateTableButton.setHorizontalAlignment(0); - truncateTableButton.setIcon(new ImageIcon(getClass().getResource("/images/truncate-table.png"))); - truncateTableButton.setMaximumSize(new Dimension(24, 24)); - truncateTableButton.setMinimumSize(new Dimension(24, 24)); - truncateTableButton.setPreferredSize(new Dimension(24, 24)); - truncateTableButton.setText(""); - truncateTableButton.setToolTipText("Truncate selected table(s)"); - toolBar2.add(truncateTableButton); - copyTableButton = new JButton(); - copyTableButton.setEnabled(false); - copyTableButton.setIcon(new ImageIcon(getClass().getResource("/images/db-copy.png"))); - copyTableButton.setMaximumSize(new Dimension(24, 24)); - copyTableButton.setMinimumSize(new Dimension(24, 24)); - copyTableButton.setPreferredSize(new Dimension(24, 24)); - copyTableButton.setText(""); - copyTableButton.setToolTipText("Copy table information to the clipboard"); - toolBar2.add(copyTableButton); - pasteTableButton = new JButton(); - pasteTableButton.setEnabled(false); - pasteTableButton.setIcon(new ImageIcon(getClass().getResource("/images/db-paste.png"))); - pasteTableButton.setMaximumSize(new Dimension(24, 24)); - pasteTableButton.setMinimumSize(new Dimension(24, 24)); - pasteTableButton.setPreferredSize(new Dimension(24, 24)); - pasteTableButton.setText(""); - pasteTableButton.setToolTipText("Paste table from the clipboard"); - toolBar2.add(pasteTableButton); - exportTableButton = new JButton(); - exportTableButton.setEnabled(false); - exportTableButton.setIcon(new ImageIcon(getClass().getResource("/images/db-export.png"))); - exportTableButton.setMaximumSize(new Dimension(24, 24)); - exportTableButton.setMinimumSize(new Dimension(24, 24)); - exportTableButton.setPreferredSize(new Dimension(24, 24)); - exportTableButton.setText(""); - exportTableButton.setToolTipText("Export table to the file"); - toolBar2.add(exportTableButton); - importTableButton = new JButton(); - importTableButton.setEnabled(false); - importTableButton.setIcon(new ImageIcon(getClass().getResource("/images/db-import.png"))); - importTableButton.setMaximumSize(new Dimension(24, 24)); - importTableButton.setMinimumSize(new Dimension(24, 24)); - importTableButton.setPreferredSize(new Dimension(24, 24)); - importTableButton.setText(""); - importTableButton.setToolTipText("Import table from the file"); - toolBar2.add(importTableButton); - flushTableButton = new JButton(); - flushTableButton.setEnabled(false); - flushTableButton.setHorizontalAlignment(0); - flushTableButton.setIcon(new ImageIcon(getClass().getResource("/images/db-flush.png"))); - flushTableButton.setMaximumSize(new Dimension(24, 24)); - flushTableButton.setMinimumSize(new Dimension(24, 24)); - flushTableButton.setPreferredSize(new Dimension(24, 24)); - flushTableButton.setText(""); - flushTableButton.setToolTipText("Forse hbase to flush table to HFile"); - toolBar2.add(flushTableButton); - tableMetadataButton = new JButton(); - tableMetadataButton.setEnabled(false); - tableMetadataButton.setIcon(new ImageIcon(getClass().getResource("/images/db-metadata.png"))); - tableMetadataButton.setMaximumSize(new Dimension(24, 24)); - tableMetadataButton.setMinimumSize(new Dimension(24, 24)); - tableMetadataButton.setPreferredSize(new Dimension(24, 24)); - tableMetadataButton.setText(""); - tableMetadataButton.setToolTipText("Show table's metadata"); - toolBar2.add(tableMetadataButton); + tableAdd = new JButton(); + tableAdd.setEnabled(true); + tableAdd.setHorizontalAlignment(0); + tableAdd.setIcon(new ImageIcon(getClass().getResource("/images/add-table.png"))); + tableAdd.setMaximumSize(new Dimension(24, 24)); + tableAdd.setMinimumSize(new Dimension(24, 24)); + tableAdd.setPreferredSize(new Dimension(24, 24)); + tableAdd.setText(""); + tableAdd.setToolTipText("Create a new table"); + toolBar2.add(tableAdd); + tableDelete = new JButton(); + tableDelete.setEnabled(false); + tableDelete.setHorizontalAlignment(0); + tableDelete.setIcon(new ImageIcon(getClass().getResource("/images/delete-table.png"))); + tableDelete.setMaximumSize(new Dimension(24, 24)); + tableDelete.setMinimumSize(new Dimension(24, 24)); + tableDelete.setPreferredSize(new Dimension(24, 24)); + tableDelete.setText(""); + tableDelete.setToolTipText("Delete selected table(s)"); + toolBar2.add(tableDelete); + tableTruncate = new JButton(); + tableTruncate.setEnabled(false); + tableTruncate.setHorizontalAlignment(0); + tableTruncate.setIcon(new ImageIcon(getClass().getResource("/images/truncate-table.png"))); + tableTruncate.setMaximumSize(new Dimension(24, 24)); + tableTruncate.setMinimumSize(new Dimension(24, 24)); + tableTruncate.setPreferredSize(new Dimension(24, 24)); + tableTruncate.setText(""); + tableTruncate.setToolTipText("Truncate selected table(s)"); + toolBar2.add(tableTruncate); + tableCopy = new JButton(); + tableCopy.setEnabled(false); + tableCopy.setIcon(new ImageIcon(getClass().getResource("/images/db-copy.png"))); + tableCopy.setMaximumSize(new Dimension(24, 24)); + tableCopy.setMinimumSize(new Dimension(24, 24)); + tableCopy.setPreferredSize(new Dimension(24, 24)); + tableCopy.setText(""); + tableCopy.setToolTipText("Copy table information to the clipboard"); + toolBar2.add(tableCopy); + tablePaste = new JButton(); + tablePaste.setEnabled(false); + tablePaste.setIcon(new ImageIcon(getClass().getResource("/images/db-paste.png"))); + tablePaste.setMaximumSize(new Dimension(24, 24)); + tablePaste.setMinimumSize(new Dimension(24, 24)); + tablePaste.setPreferredSize(new Dimension(24, 24)); + tablePaste.setText(""); + tablePaste.setToolTipText("Paste table from the clipboard"); + toolBar2.add(tablePaste); + tableExport = new JButton(); + tableExport.setEnabled(false); + tableExport.setIcon(new ImageIcon(getClass().getResource("/images/db-export.png"))); + tableExport.setMaximumSize(new Dimension(24, 24)); + tableExport.setMinimumSize(new Dimension(24, 24)); + tableExport.setPreferredSize(new Dimension(24, 24)); + tableExport.setText(""); + tableExport.setToolTipText("Export table to the file"); + toolBar2.add(tableExport); + tableImport = new JButton(); + tableImport.setEnabled(true); + tableImport.setIcon(new ImageIcon(getClass().getResource("/images/db-import.png"))); + tableImport.setMaximumSize(new Dimension(24, 24)); + tableImport.setMinimumSize(new Dimension(24, 24)); + tableImport.setPreferredSize(new Dimension(24, 24)); + tableImport.setText(""); + tableImport.setToolTipText("Import table from the file"); + toolBar2.add(tableImport); + tableFlush = new JButton(); + tableFlush.setEnabled(false); + tableFlush.setHorizontalAlignment(0); + tableFlush.setIcon(new ImageIcon(getClass().getResource("/images/db-flush.png"))); + tableFlush.setMaximumSize(new Dimension(24, 24)); + tableFlush.setMinimumSize(new Dimension(24, 24)); + tableFlush.setPreferredSize(new Dimension(24, 24)); + tableFlush.setText(""); + tableFlush.setToolTipText("Force hbase to flush table to HFile"); + toolBar2.add(tableFlush); + tableMetadata = new JButton(); + tableMetadata.setEnabled(false); + tableMetadata.setIcon(new ImageIcon(getClass().getResource("/images/db-metadata.png"))); + tableMetadata.setMaximumSize(new Dimension(24, 24)); + tableMetadata.setMinimumSize(new Dimension(24, 24)); + tableMetadata.setPreferredSize(new Dimension(24, 24)); + tableMetadata.setText(""); + tableMetadata.setToolTipText("Show table's metadata"); + toolBar2.add(tableMetadata); final JPanel panel4 = new JPanel(); panel4.setLayout(new GridLayoutManager(1, 3, new Insets(0, 0, 0, 0), -1, -1)); panel1.add( @@ -2221,26 +2647,52 @@ panel6, new GridConstraints( toolBar3, new GridConstraints( 0, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_FIXED, null, new Dimension(-1, 20), null, 0, false)); - refreshColumnsButton = new JButton(); - refreshColumnsButton.setEnabled(false); - refreshColumnsButton.setHorizontalAlignment(0); - refreshColumnsButton.setIcon(new ImageIcon(getClass().getResource("/images/refresh.png"))); - refreshColumnsButton.setMaximumSize(new Dimension(24, 24)); - refreshColumnsButton.setMinimumSize(new Dimension(24, 24)); - refreshColumnsButton.setPreferredSize(new Dimension(24, 24)); - refreshColumnsButton.setText(""); - refreshColumnsButton.setToolTipText("Refresh columns"); - toolBar3.add(refreshColumnsButton); + columnRefresh = new JButton(); + columnRefresh.setEnabled(false); + columnRefresh.setHorizontalAlignment(0); + columnRefresh.setIcon(new ImageIcon(getClass().getResource("/images/refresh.png"))); + columnRefresh.setMaximumSize(new Dimension(24, 24)); + columnRefresh.setMinimumSize(new Dimension(24, 24)); + columnRefresh.setPreferredSize(new Dimension(24, 24)); + columnRefresh.setText(""); + columnRefresh.setToolTipText("Refresh columns"); + toolBar3.add(columnRefresh); final JToolBar.Separator toolBar$Separator2 = new JToolBar.Separator(); toolBar3.add(toolBar$Separator2); - columnsFilter = new JComboBox(); - columnsFilter.setAlignmentX(0.0f); - columnsFilter.setAlignmentY(0.6f); - columnsFilter.setEditable(true); - columnsFilter.setMaximumSize(new Dimension(32767, 26)); - columnsFilter.setMinimumSize(new Dimension(50, 20)); - columnsFilter.setPreferredSize(new Dimension(-1, -1)); - toolBar3.add(columnsFilter); + columnFilters = new JComboBox(); + columnFilters.setAlignmentX(0.0f); + columnFilters.setAlignmentY(0.6f); + columnFilters.setEditable(true); + columnFilters.setMaximumSize(new Dimension(32767, 26)); + columnFilters.setMinimumSize(new Dimension(50, 20)); + columnFilters.setPreferredSize(new Dimension(-1, -1)); + toolBar3.add(columnFilters); + final JToolBar.Separator toolBar$Separator3 = new JToolBar.Separator(); + toolBar3.add(toolBar$Separator3); + columnScan = new JButton(); + columnScan.setEnabled(false); + columnScan.setIcon(new ImageIcon(getClass().getResource("/images/search.png"))); + columnScan.setMinimumSize(new Dimension(24, 24)); + columnScan.setPreferredSize(new Dimension(24, 24)); + columnScan.setText(""); + columnScan.setToolTipText("Perform an advanced scan..."); + toolBar3.add(columnScan); + columnJump = new JButton(); + columnJump.setEnabled(false); + columnJump.setIcon(new ImageIcon(getClass().getResource("/images/jump.png"))); + columnJump.setMinimumSize(new Dimension(24, 24)); + columnJump.setPreferredSize(new Dimension(24, 24)); + columnJump.setText(""); + columnJump.setToolTipText("Jump to a specific row number"); + toolBar3.add(columnJump); + columnPopulate = new JButton(); + columnPopulate.setEnabled(false); + columnPopulate.setIcon(new ImageIcon(getClass().getResource("/images/populate.png"))); + columnPopulate.setMinimumSize(new Dimension(24, 24)); + columnPopulate.setPreferredSize(new Dimension(24, 24)); + columnPopulate.setText(""); + columnPopulate.setToolTipText("Populate rows for the selected columns"); + toolBar3.add(columnPopulate); final JScrollPane scrollPane2 = new JScrollPane(); scrollPane2.setDoubleBuffered(true); panel5.add( @@ -2251,7 +2703,7 @@ scrollPane2, new GridConstraints( columnsTable.setAutoResizeMode(1); scrollPane2.setViewportView(columnsTable); final JPanel panel7 = new JPanel(); - panel7.setLayout(new GridLayoutManager(1, 2, new Insets(0, 0, 0, 0), -1, -1)); + panel7.setLayout(new GridLayoutManager(1, 1, new Insets(0, 0, 0, 0), -1, -1)); panel5.add( panel7, new GridConstraints( 2, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, @@ -2261,57 +2713,64 @@ panel7, new GridConstraints( toolBar4.setFloatable(false); panel7.add( toolBar4, new GridConstraints( - 0, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_FIXED, null, - new Dimension(-1, 20), null, 0, false)); - checkAllButton = new JButton(); - checkAllButton.setEnabled(false); - checkAllButton.setIcon(new ImageIcon(getClass().getResource("/images/select.png"))); - checkAllButton.setMaximumSize(new Dimension(27, 27)); - checkAllButton.setMinimumSize(new Dimension(24, 24)); - checkAllButton.setPreferredSize(new Dimension(24, 24)); - checkAllButton.setText(""); - checkAllButton.setToolTipText("Select all columns"); - toolBar4.add(checkAllButton); - uncheckAllButton = new JButton(); - uncheckAllButton.setEnabled(false); - uncheckAllButton.setIcon(new ImageIcon(getClass().getResource("/images/unselect.png"))); - uncheckAllButton.setMaximumSize(new Dimension(27, 27)); - uncheckAllButton.setMinimumSize(new Dimension(24, 24)); - uncheckAllButton.setPreferredSize(new Dimension(24, 24)); - uncheckAllButton.setText(""); - uncheckAllButton.setToolTipText("Unselect all columns"); - toolBar4.add(uncheckAllButton); - final JToolBar toolBar5 = new JToolBar(); - toolBar5.setBorderPainted(false); - toolBar5.setFloatable(false); - panel7.add( - toolBar5, new GridConstraints( - 0, 1, 1, 1, GridConstraints.ANCHOR_EAST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_FIXED, null, - new Dimension(-1, 20), null, 0, false)); - scanButton = new JButton(); - scanButton.setEnabled(false); - scanButton.setIcon(new ImageIcon(getClass().getResource("/images/search.png"))); - scanButton.setMinimumSize(new Dimension(24, 24)); - scanButton.setPreferredSize(new Dimension(24, 24)); - scanButton.setText(""); - scanButton.setToolTipText("Perform an advanced scan..."); - toolBar5.add(scanButton); - jumpButton = new JButton(); - jumpButton.setEnabled(false); - jumpButton.setIcon(new ImageIcon(getClass().getResource("/images/jump.png"))); - jumpButton.setMinimumSize(new Dimension(24, 24)); - jumpButton.setPreferredSize(new Dimension(24, 24)); - jumpButton.setText(""); - jumpButton.setToolTipText("Jump to a specific row number"); - toolBar5.add(jumpButton); - populateButton = new JButton(); - populateButton.setEnabled(false); - populateButton.setIcon(new ImageIcon(getClass().getResource("/images/populate.png"))); - populateButton.setMinimumSize(new Dimension(24, 24)); - populateButton.setPreferredSize(new Dimension(24, 24)); - populateButton.setText(""); - populateButton.setToolTipText("Populate rows for the selected columns"); - toolBar5.add(populateButton); + 0, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_FIXED, + null, new Dimension(-1, 20), null, 0, false)); + columnCheck = new JButton(); + columnCheck.setEnabled(false); + columnCheck.setIcon(new ImageIcon(getClass().getResource("/images/select.png"))); + columnCheck.setMaximumSize(new Dimension(24, 24)); + columnCheck.setMinimumSize(new Dimension(24, 24)); + columnCheck.setPreferredSize(new Dimension(24, 24)); + columnCheck.setText(""); + columnCheck.setToolTipText("Select all columns"); + toolBar4.add(columnCheck); + columnUncheck = new JButton(); + columnUncheck.setEnabled(false); + columnUncheck.setIcon(new ImageIcon(getClass().getResource("/images/unselect.png"))); + columnUncheck.setMaximumSize(new Dimension(24, 24)); + columnUncheck.setMinimumSize(new Dimension(24, 24)); + columnUncheck.setPreferredSize(new Dimension(24, 24)); + columnUncheck.setText(""); + columnUncheck.setToolTipText("Unselect all columns"); + toolBar4.add(columnUncheck); + final JToolBar.Separator toolBar$Separator4 = new JToolBar.Separator(); + toolBar4.add(toolBar$Separator4); + columnConverters = new WideComboBox(); + columnConverters.setEnabled(true); + columnConverters.setMaximumSize(new Dimension(32767, 26)); + columnConverters.setMinimumSize(new Dimension(50, 20)); + columnConverters.setPreferredSize(new Dimension(-1, -1)); + columnConverters.setToolTipText("Choose type to be used to convert column name"); + toolBar4.add(columnConverters); + final JToolBar.Separator toolBar$Separator5 = new JToolBar.Separator(); + toolBar4.add(toolBar$Separator5); + columnAddConverter = new JButton(); + columnAddConverter.setEnabled(true); + columnAddConverter.setIcon(new ImageIcon(getClass().getResource("/images/addConverter.png"))); + columnAddConverter.setMaximumSize(new Dimension(24, 24)); + columnAddConverter.setMinimumSize(new Dimension(24, 24)); + columnAddConverter.setPreferredSize(new Dimension(24, 24)); + columnAddConverter.setText(""); + columnAddConverter.setToolTipText("Add custom type converter"); + toolBar4.add(columnAddConverter); + columnEditConverter = new JButton(); + columnEditConverter.setEnabled(false); + columnEditConverter.setIcon(new ImageIcon(getClass().getResource("/images/editConverter.png"))); + columnEditConverter.setMaximumSize(new Dimension(24, 24)); + columnEditConverter.setMinimumSize(new Dimension(24, 24)); + columnEditConverter.setPreferredSize(new Dimension(24, 24)); + columnEditConverter.setText(""); + columnEditConverter.setToolTipText("Edit custom type converter"); + toolBar4.add(columnEditConverter); + columnDeleteConverter = new JButton(); + columnDeleteConverter.setEnabled(false); + columnDeleteConverter.setIcon(new ImageIcon(getClass().getResource("/images/deleteConverter.png"))); + columnDeleteConverter.setMaximumSize(new Dimension(24, 24)); + columnDeleteConverter.setMinimumSize(new Dimension(24, 24)); + columnDeleteConverter.setPreferredSize(new Dimension(24, 24)); + columnDeleteConverter.setText(""); + columnDeleteConverter.setToolTipText("Delete custom type converter"); + toolBar4.add(columnDeleteConverter); final JPanel panel8 = new JPanel(); panel8.setLayout(new GridLayoutManager(1, 3, new Insets(0, 0, 0, 0), -1, -1)); panel5.add( @@ -2354,111 +2813,116 @@ panel10, new GridConstraints( label3, new GridConstraints( 0, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); + final JToolBar toolBar5 = new JToolBar(); + toolBar5.setBorderPainted(false); + toolBar5.setFloatable(false); + panel10.add( + toolBar5, new GridConstraints( + 0, 2, 1, 1, GridConstraints.ANCHOR_EAST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_FIXED, null, + new Dimension(-1, 20), null, 0, false)); + rowOpen = new JButton(); + rowOpen.setEnabled(false); + rowOpen.setIcon(new ImageIcon(getClass().getResource("/images/viewer.png"))); + rowOpen.setMinimumSize(new Dimension(24, 24)); + rowOpen.setPreferredSize(new Dimension(24, 24)); + rowOpen.setText(""); + rowOpen.setToolTipText("Open rows in external viewer"); + toolBar5.add(rowOpen); + rowAdd = new JButton(); + rowAdd.setEnabled(false); + rowAdd.setIcon(new ImageIcon(getClass().getResource("/images/add.png"))); + rowAdd.setMinimumSize(new Dimension(24, 24)); + rowAdd.setPreferredSize(new Dimension(24, 24)); + rowAdd.setText(""); + rowAdd.setToolTipText("Create a new row"); + toolBar5.add(rowAdd); + rowDelete = new JButton(); + rowDelete.setEnabled(false); + rowDelete.setIcon(new ImageIcon(getClass().getResource("/images/delete.png"))); + rowDelete.setMinimumSize(new Dimension(24, 24)); + rowDelete.setPreferredSize(new Dimension(24, 24)); + rowDelete.setText(""); + rowDelete.setToolTipText("Delete selected rows"); + toolBar5.add(rowDelete); + rowCopy = new JButton(); + rowCopy.setEnabled(false); + rowCopy.setIcon(new ImageIcon(getClass().getResource("/images/copy.png"))); + rowCopy.setMaximumSize(new Dimension(49, 27)); + rowCopy.setMinimumSize(new Dimension(24, 24)); + rowCopy.setPreferredSize(new Dimension(24, 24)); + rowCopy.setText(""); + rowCopy.setToolTipText("Copy selected rows to the clipboard"); + toolBar5.add(rowCopy); + rowPaste = new JButton(); + rowPaste.setEnabled(false); + rowPaste.setIcon(new ImageIcon(getClass().getResource("/images/paste.png"))); + rowPaste.setMaximumSize(new Dimension(49, 27)); + rowPaste.setMinimumSize(new Dimension(24, 24)); + rowPaste.setPreferredSize(new Dimension(24, 24)); + rowPaste.setText(""); + rowPaste.setToolTipText("Paste rows from the clipboard"); + toolBar5.add(rowPaste); + rowSave = new JButton(); + rowSave.setEnabled(false); + rowSave.setIcon(new ImageIcon(getClass().getResource("/images/save.png"))); + rowSave.setMinimumSize(new Dimension(24, 24)); + rowSave.setPreferredSize(new Dimension(24, 24)); + rowSave.setText(""); + rowSave.setToolTipText("Save all modified rows to the hbase"); + toolBar5.add(rowSave); final JToolBar toolBar6 = new JToolBar(); toolBar6.setBorderPainted(false); toolBar6.setFloatable(false); panel10.add( toolBar6, new GridConstraints( - 0, 2, 1, 1, GridConstraints.ANCHOR_EAST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_FIXED, null, - new Dimension(-1, 20), null, 0, false)); - openInViewerButton = new JButton(); - openInViewerButton.setEnabled(false); - openInViewerButton.setIcon(new ImageIcon(getClass().getResource("/images/viewer.png"))); - openInViewerButton.setMinimumSize(new Dimension(24, 24)); - openInViewerButton.setPreferredSize(new Dimension(24, 24)); - openInViewerButton.setText(""); - openInViewerButton.setToolTipText("Open rows in external viewer"); - toolBar6.add(openInViewerButton); - addRowButton = new JButton(); - addRowButton.setEnabled(false); - addRowButton.setIcon(new ImageIcon(getClass().getResource("/images/add.png"))); - addRowButton.setMinimumSize(new Dimension(24, 24)); - addRowButton.setPreferredSize(new Dimension(24, 24)); - addRowButton.setText(""); - addRowButton.setToolTipText("Create a new row"); - toolBar6.add(addRowButton); - deleteRowButton = new JButton(); - deleteRowButton.setEnabled(false); - deleteRowButton.setIcon(new ImageIcon(getClass().getResource("/images/delete.png"))); - deleteRowButton.setMinimumSize(new Dimension(24, 24)); - deleteRowButton.setPreferredSize(new Dimension(24, 24)); - deleteRowButton.setText(""); - deleteRowButton.setToolTipText("Delete selected rows"); - toolBar6.add(deleteRowButton); - copyRowButton = new JButton(); - copyRowButton.setEnabled(false); - copyRowButton.setIcon(new ImageIcon(getClass().getResource("/images/copy.png"))); - copyRowButton.setMaximumSize(new Dimension(49, 27)); - copyRowButton.setMinimumSize(new Dimension(24, 24)); - copyRowButton.setPreferredSize(new Dimension(24, 24)); - copyRowButton.setText(""); - copyRowButton.setToolTipText("Copy selected rows to the clipboard"); - toolBar6.add(copyRowButton); - pasteRowButton = new JButton(); - pasteRowButton.setEnabled(false); - pasteRowButton.setIcon(new ImageIcon(getClass().getResource("/images/paste.png"))); - pasteRowButton.setMaximumSize(new Dimension(49, 27)); - pasteRowButton.setMinimumSize(new Dimension(24, 24)); - pasteRowButton.setPreferredSize(new Dimension(24, 24)); - pasteRowButton.setText(""); - pasteRowButton.setToolTipText("Paste rows from the clipboard"); - toolBar6.add(pasteRowButton); - updateRowButton = new JButton(); - updateRowButton.setEnabled(false); - updateRowButton.setIcon(new ImageIcon(getClass().getResource("/images/save.png"))); - updateRowButton.setMinimumSize(new Dimension(24, 24)); - updateRowButton.setPreferredSize(new Dimension(24, 24)); - updateRowButton.setText(""); - updateRowButton.setToolTipText("Save all modified rows to the hbase"); - toolBar6.add(updateRowButton); - final JToolBar toolBar7 = new JToolBar(); - toolBar7.setBorderPainted(false); - toolBar7.setFloatable(false); - panel10.add( - toolBar7, new GridConstraints( 0, 1, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_FIXED, null, new Dimension(-1, 20), null, 0, false)); - rowsNumberSpinner = new JSpinner(); - rowsNumberSpinner.setDoubleBuffered(true); - rowsNumberSpinner.setEnabled(true); - rowsNumberSpinner.setMinimumSize(new Dimension(45, 24)); - rowsNumberSpinner.setPreferredSize(new Dimension(60, 24)); - toolBar7.add(rowsNumberSpinner); - final JToolBar.Separator toolBar$Separator3 = new JToolBar.Separator(); - toolBar7.add(toolBar$Separator3); - visibleRowsLabel = new JLabel(); - visibleRowsLabel.setText("?"); - toolBar7.add(visibleRowsLabel); - final JToolBar.Separator toolBar$Separator4 = new JToolBar.Separator(); - toolBar7.add(toolBar$Separator4); + rowsNumber = new JSpinner(); + rowsNumber.setDoubleBuffered(true); + rowsNumber.setEnabled(true); + rowsNumber.setMinimumSize(new Dimension(45, 24)); + rowsNumber.setPreferredSize(new Dimension(60, 24)); + toolBar6.add(rowsNumber); + final JToolBar.Separator toolBar$Separator6 = new JToolBar.Separator(); + toolBar6.add(toolBar$Separator6); + rowsVisible = new JLabel(); + rowsVisible.setText("?"); + toolBar6.add(rowsVisible); + final JToolBar.Separator toolBar$Separator7 = new JToolBar.Separator(); + toolBar6.add(toolBar$Separator7); final JLabel label4 = new JLabel(); label4.setText("of"); - toolBar7.add(label4); - final JToolBar.Separator toolBar$Separator5 = new JToolBar.Separator(); - toolBar7.add(toolBar$Separator5); - rowsNumberLabel = new JLabel(); - rowsNumberLabel.setText("?"); - toolBar7.add(rowsNumberLabel); - final JToolBar.Separator toolBar$Separator6 = new JToolBar.Separator(); - toolBar7.add(toolBar$Separator6); - showPrevPageButton = new JButton(); - showPrevPageButton.setEnabled(false); - showPrevPageButton.setIcon(new ImageIcon(getClass().getResource("/images/prev.png"))); - showPrevPageButton.setMaximumSize(new Dimension(49, 27)); - showPrevPageButton.setMinimumSize(new Dimension(24, 24)); - showPrevPageButton.setPreferredSize(new Dimension(24, 24)); - showPrevPageButton.setText(""); - showPrevPageButton.setToolTipText("Go to the prvious page"); - toolBar7.add(showPrevPageButton); - showNextPageButton = new JButton(); - showNextPageButton.setEnabled(false); - showNextPageButton.setIcon(new ImageIcon(getClass().getResource("/images/next.png"))); - showNextPageButton.setMaximumSize(new Dimension(49, 27)); - showNextPageButton.setMinimumSize(new Dimension(24, 24)); - showNextPageButton.setPreferredSize(new Dimension(24, 24)); - showNextPageButton.setText(""); - showNextPageButton.setToolTipText("Go to the next page"); - toolBar7.add(showNextPageButton); + toolBar6.add(label4); + final JToolBar.Separator toolBar$Separator8 = new JToolBar.Separator(); + toolBar6.add(toolBar$Separator8); + rowsTotal = new JLabel(); + rowsTotal.setText("?"); + toolBar6.add(rowsTotal); + rowsNumberIcon = new JLabel(); + rowsNumberIcon.setIcon(new ImageIcon(getClass().getResource("/images/busy.gif"))); + rowsNumberIcon.setText(""); + rowsNumberIcon.setVisible(false); + toolBar6.add(rowsNumberIcon); + final JToolBar.Separator toolBar$Separator9 = new JToolBar.Separator(); + toolBar6.add(toolBar$Separator9); + rowsPrev = new JButton(); + rowsPrev.setEnabled(false); + rowsPrev.setIcon(new ImageIcon(getClass().getResource("/images/prev.png"))); + rowsPrev.setMaximumSize(new Dimension(49, 27)); + rowsPrev.setMinimumSize(new Dimension(24, 24)); + rowsPrev.setPreferredSize(new Dimension(24, 24)); + rowsPrev.setText(""); + rowsPrev.setToolTipText("Go to the prvious page"); + toolBar6.add(rowsPrev); + rowsNext = new JButton(); + rowsNext.setEnabled(false); + rowsNext.setIcon(new ImageIcon(getClass().getResource("/images/next.png"))); + rowsNext.setMaximumSize(new Dimension(49, 27)); + rowsNext.setMinimumSize(new Dimension(24, 24)); + rowsNext.setPreferredSize(new Dimension(24, 24)); + rowsNext.setText(""); + rowsNext.setToolTipText("Go to the next page"); + toolBar6.add(rowsNext); final JScrollPane scrollPane3 = new JScrollPane(); panel9.add( scrollPane3, new GridConstraints( diff --git a/src/main/resources/images/addConverter.png b/src/main/resources/images/addConverter.png new file mode 100644 index 0000000..29397ce Binary files /dev/null and b/src/main/resources/images/addConverter.png differ diff --git a/src/main/resources/images/deleteConverter.png b/src/main/resources/images/deleteConverter.png new file mode 100644 index 0000000..1e7df0d Binary files /dev/null and b/src/main/resources/images/deleteConverter.png differ diff --git a/src/main/resources/images/editConverter.png b/src/main/resources/images/editConverter.png new file mode 100644 index 0000000..5165268 Binary files /dev/null and b/src/main/resources/images/editConverter.png differ diff --git a/src/main/resources/log4j.xml b/src/main/resources/log4j.xml new file mode 100644 index 0000000..de31ce6 --- /dev/null +++ b/src/main/resources/log4j.xml @@ -0,0 +1,52 @@ + + + + + ] +> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/update.properties b/update.properties new file mode 100644 index 0000000..dea497b --- /dev/null +++ b/update.properties @@ -0,0 +1,4 @@ +hrider.version=1.0.7.2 +hbase.versions=0.94.1 +hbase.0.94.1=http://bit.ly/14R3Pdv +hbase.0.94.1.reduced=http://bit.ly/1a3YF1A diff --git a/updater/pom.xml b/updater/pom.xml new file mode 100644 index 0000000..4a0866c --- /dev/null +++ b/updater/pom.xml @@ -0,0 +1,100 @@ + + + 4.0.0 + + h-rider + h-rider-updater + h-rider-updater + h-rider updater + + jar + 1.0.7.2 + + + + com.intellij + forms_rt + 7.0.3 + + + + + + + + org.apache.maven.plugins + maven-shade-plugin + 1.5 + + + package + + shade + + + + + + + org.apache.maven.plugins + maven-jar-plugin + 2.4 + + + + hrider.updater.ui.forms.Window + hrider.updater.ui.forms + + + ${project.version} + + + + + + + maven-compiler-plugin + 2.5.1 + + 1.6 + 1.6 + + + + + + + + + + org.eclipse.m2e + lifecycle-mapping + 1.0.0 + + + + + + org.apache.maven.plugins + maven-dependency-plugin + [2.5.1,) + + copy-dependencies + + + + + + + + + + + + + + + + diff --git a/updater/src/main/java/hrider/updater/io/Downloader.java b/updater/src/main/java/hrider/updater/io/Downloader.java new file mode 100644 index 0000000..970e4c9 --- /dev/null +++ b/updater/src/main/java/hrider/updater/io/Downloader.java @@ -0,0 +1,131 @@ +package hrider.updater.io; + +import java.io.*; +import java.net.HttpURLConnection; +import java.net.MalformedURLException; +import java.net.URL; + +/** + * Copyright (C) 2012 NICE Systems ltd. + *

+ * 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. + * + * @author Igor Cher + * @version %I%, %G% + */ +public class Downloader { + + //region Constants + private static final int DEFAULT_BUFFER_SIZE = 1024 * 4; + private static final double TOTAL_PERCENTS = 100.0; + //endregion + + //region Variables + private static ProgressListener listener; + //endregion + + //region Constructor + private Downloader() { + } + //endregion + + //region Public Properties + public static ProgressListener getListener() { + return listener; + } + + public static void setListener(ProgressListener listener) { + Downloader.listener = listener; + } + //endregion + + //region Public Methods + public static File download(URL url) throws IOException, FileNotFoundException, MalformedURLException { + StringBuilder fileName = new StringBuilder(); + StringBuilder extension = new StringBuilder(); + + extractFileNameAndExtension(url.getPath(), fileName, extension); + + File temp = File.createTempFile(fileName.toString() + '-', extension.length() == 0 ? null : extension.toString()); + HttpURLConnection connection = (HttpURLConnection)url.openConnection(); + + int code = connection.getResponseCode(); + while (code / 100 == 3) { + URL redirectedUrl = new URL(connection.getHeaderField("Location")); + connection.disconnect(); + + connection = (HttpURLConnection)redirectedUrl.openConnection(); + code = connection.getResponseCode(); + } + + InputStream in = connection.getInputStream(); + OutputStream out = new FileOutputStream(temp); + + try { + copy(in, connection.getContentLength(), out); + } + finally { + try { + in.close(); + } + catch (IOException ignore) { + } + + try { + out.close(); + } + catch (IOException ignore) { + } + } + + return temp; + } + //endregion + + //region Private Methods + private static void copy(InputStream in, double inSize, OutputStream out) throws IOException { + byte[] buffer = new byte[DEFAULT_BUFFER_SIZE]; + + double totalBytesRead = 0; + int bytesRead; + + while (-1 != (bytesRead = in.read(buffer))) { + out.write(buffer, 0, bytesRead); + + totalBytesRead += bytesRead; + + if (listener != null) { + listener.onProgress((int)(totalBytesRead / inSize * TOTAL_PERCENTS)); + } + } + } + + private static void extractFileNameAndExtension(String path, StringBuilder fileName, StringBuilder extension) { + String leaf = path.replace("\\", "/"); + + int index = leaf.lastIndexOf('/'); + if (index != -1) { + leaf = leaf.substring(index + 1); + } + + index = leaf.lastIndexOf('.'); + if (index == -1) { + fileName.append(leaf); + } + else { + fileName.append(leaf.substring(0, index)); + extension.append(leaf.substring(index)); + } + } + //endregion +} diff --git a/updater/src/main/java/hrider/updater/io/FileHelper.java b/updater/src/main/java/hrider/updater/io/FileHelper.java new file mode 100644 index 0000000..ef83713 --- /dev/null +++ b/updater/src/main/java/hrider/updater/io/FileHelper.java @@ -0,0 +1,65 @@ +package hrider.updater.io; + +import java.io.File; +import java.util.Arrays; +import java.util.List; +import java.util.regex.Pattern; + +/** + * Copyright (C) 2012 NICE Systems ltd. + *

+ * 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. + * + * @author Igor Cher + * @version %I%, %G% + */ +public class FileHelper { + + //region Constructor + private FileHelper() { + } + //endregion + + //region Public Methods + public static void delete(File path, String... exclude) { + if (path != null && path.exists()) { + List excludedPaths = Arrays.asList(exclude); + + File[] files = path.listFiles(); + if (files != null) { + for (File file : files) { + if (!excludedPaths.contains(file.getName())) { + delete(file); + } + } + } + + path.delete(); + } + } + + public static File findFile(File folder, Pattern regex) { + if (folder != null && folder.exists()) { + File[] files = folder.listFiles(); + if (files != null) { + for (File file : files) { + if (regex.matcher(file.getName()).find()) { + return file; + } + } + } + } + return null; + } + //endregion +} diff --git a/updater/src/main/java/hrider/updater/io/ProgressListener.java b/updater/src/main/java/hrider/updater/io/ProgressListener.java new file mode 100644 index 0000000..5d2dcf8 --- /dev/null +++ b/updater/src/main/java/hrider/updater/io/ProgressListener.java @@ -0,0 +1,29 @@ +package hrider.updater.io; + +/** + * Copyright (C) 2012 NICE Systems ltd. + *

+ * 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. + * + * @author Igor Cher + * @version %I%, %G% + */ +public interface ProgressListener { + + /** + * The method is called each time the operation is progressed. + * + * @param percentage The current percentage of the operation. + */ + void onProgress(int percentage); +} diff --git a/updater/src/main/java/hrider/updater/io/ZipHelper.java b/updater/src/main/java/hrider/updater/io/ZipHelper.java new file mode 100644 index 0000000..45d2c97 --- /dev/null +++ b/updater/src/main/java/hrider/updater/io/ZipHelper.java @@ -0,0 +1,111 @@ +package hrider.updater.io; + +import java.io.*; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.List; +import java.util.zip.ZipEntry; +import java.util.zip.ZipException; +import java.util.zip.ZipFile; + +/** + * Copyright (C) 2012 NICE Systems ltd. + *

+ * 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. + * + * @author Igor Cher + * @version %I%, %G% + */ +public class ZipHelper { + + //region Constants + private static final int BUFFER_SIZE = 4096; + //endregion + + //region Constructor + private ZipHelper() { + } + //endregion + + //region Public Methods + @SuppressWarnings("ResultOfMethodCallIgnored") + public static void unzip(File zippedFile, File targetDir) throws ZipException, IOException, FileNotFoundException { + targetDir.mkdirs(); + + ZipFile zipFile = new ZipFile(zippedFile); + + try { + Enumeration entries = zipFile.entries(); + while (entries.hasMoreElements()) { + ZipEntry entry = entries.nextElement(); + File targetFile = new File(targetDir, entry.getName()); + if (entry.isDirectory()) { + targetFile.mkdirs(); + } + else { + InputStream input = zipFile.getInputStream(entry); + try { + OutputStream output = new FileOutputStream(targetFile); + try { + copy(input, output); + } + finally { + output.close(); + } + } + finally { + input.close(); + } + } + } + } + finally { + zipFile.close(); + } + } + + public static List getRootEntries(File zippedFile) throws IOException, ZipException { + List fileNames = new ArrayList(); + + ZipFile zipFile = new ZipFile(zippedFile); + + try { + Enumeration entries = zipFile.entries(); + while (entries.hasMoreElements()) { + ZipEntry entry = entries.nextElement(); + + int index = entry.getName().indexOf('/'); + if (index == -1 || index == entry.getName().length() - 1) { + fileNames.add(entry.getName()); + } + } + } + finally { + zipFile.close(); + } + + return fileNames; + } + //endregion + + //region Private Methods + private static void copy(InputStream in, OutputStream out) throws IOException { + byte[] buffer = new byte[BUFFER_SIZE]; + + int bytesRead; + while ((bytesRead = in.read(buffer)) != -1) { + out.write(buffer, 0, bytesRead); + } + } + //endregion +} diff --git a/updater/src/main/java/hrider/updater/ui/forms/Window.form b/updater/src/main/java/hrider/updater/ui/forms/Window.form new file mode 100644 index 0000000..bb661b9 --- /dev/null +++ b/updater/src/main/java/hrider/updater/ui/forms/Window.form @@ -0,0 +1,39 @@ + +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/updater/src/main/java/hrider/updater/ui/forms/Window.java b/updater/src/main/java/hrider/updater/ui/forms/Window.java new file mode 100644 index 0000000..07799a9 --- /dev/null +++ b/updater/src/main/java/hrider/updater/ui/forms/Window.java @@ -0,0 +1,223 @@ +package hrider.updater.ui.forms; + +import com.intellij.uiDesigner.core.GridConstraints; +import com.intellij.uiDesigner.core.GridLayoutManager; +import hrider.updater.io.Downloader; +import hrider.updater.io.FileHelper; +import hrider.updater.io.ProgressListener; +import hrider.updater.io.ZipHelper; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.WindowEvent; +import java.io.File; +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.List; +import java.util.jar.Attributes; +import java.util.jar.JarFile; +import java.util.regex.Pattern; + +/** + * Copyright (C) 2012 NICE Systems ltd. + *

+ * 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. + * + * @author Igor Cher + * @version %I%, %G% + */ +public class Window { + + //region Constants + private static final Pattern JAR_PATTERN = Pattern.compile("h-rider-?[0-9]{0,4}\\.?[0-9]{0,4}\\.?[0-9]{0,4}\\.?[0-9]{0,4}\\.jar"); + private static final Pattern UPDATER_PATTERN = Pattern.compile("h-rider-updater-?[0-9]{0,4}\\.?[0-9]{0,4}\\.?[0-9]{0,4}\\.?[0-9]{0,4}\\.jar"); + //endregion + + //region Variables + private JPanel topPanel; + private JFrame frame; + private JProgressBar progressBar; + private JButton cancelButton; + private JLabel actionLabel; + //endregion + + //region Constructor + public Window(final URL url, final File targetFolder) { + + cancelButton.addActionListener( + new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + frame.dispatchEvent(new WindowEvent(frame, WindowEvent.WINDOW_CLOSING)); + } + }); + + Downloader.setListener( + new ProgressListener() { + @Override + public void onProgress(int percentage) { + progressBar.setValue(percentage); + } + }); + + new Thread( + new Runnable() { + @Override + public void run() { + try { + File file = Downloader.download(url); + if (file != null) { + actionLabel.setText("Deleting old files..."); + + List filesToRemove = ZipHelper.getRootEntries(file); + for (String fileName : filesToRemove) { + if (JAR_PATTERN.matcher(fileName).find()) { + File jar = FileHelper.findFile(targetFolder, JAR_PATTERN); + FileHelper.delete(jar); + } + else if (UPDATER_PATTERN.matcher(fileName).find()) { + File jar = FileHelper.findFile(targetFolder, UPDATER_PATTERN); + FileHelper.delete(jar); + } + else { + FileHelper.delete(new File(targetFolder + "/" + fileName)); + } + } + + actionLabel.setText("Extracting archive..."); + ZipHelper.unzip(file, targetFolder); + + File jar = FileHelper.findFile( + targetFolder, Pattern.compile("h-rider-?[0-9]{0,4}\\.?[0-9]{0,4}\\.?[0-9]{0,4}\\.?[0-9]{0,4}\\.jar")); + + if (jar != null) { + Runtime.getRuntime().exec(String.format("java -jar %s", jar.getName()), null, jar.getParentFile()); + } + } + } + catch (Exception e) { + JOptionPane.showMessageDialog(topPanel, e.getMessage(), "Error", JOptionPane.ERROR_MESSAGE); + } + finally { + frame.dispatchEvent(new WindowEvent(frame, WindowEvent.WINDOW_CLOSING)); + } + } + }).start(); + } + //endregion + + //region Public Methods + public static void main(String[] args) throws MalformedURLException { + if (args.length != 2) { + throw new IllegalArgumentException("args missing"); + } + + final URL url = new URL(args[0]); + final File targetFolder = new File(args[1]); + + // Schedule a job for the event-dispatching thread: + // creating and showing this application's GUI. + SwingUtilities.invokeLater( + new Runnable() { + @Override + public void run() { + createAndShowGUI(url, targetFolder); + } + }); + } + //endregion + + //region Private Methods + + /** + * Create the GUI and show it. For thread safety, + * this method should be invoked from the + * event-dispatching thread. + */ + private static void createAndShowGUI(URL url, File targetFolder) { + Window window = new Window(url, targetFolder); + + JFrame frame = new JFrame("h-rider-updater - " + getVersion()); + window.frame = frame; + + frame.setContentPane(window.topPanel); + frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); + frame.setLocationByPlatform(true); + frame.setIconImage(new ImageIcon(Thread.currentThread().getContextClassLoader().getResource("images/h-rider.png")).getImage()); + frame.setResizable(false); + + // Display the window. + frame.pack(); + frame.setVisible(true); + } + + private static String getVersion() { + try { + String jarPath = Window.class.getProtectionDomain().getCodeSource().getLocation().getPath(); + + JarFile file = new JarFile(jarPath); + return file.getManifest().getMainAttributes().get(new Attributes.Name("version")).toString(); + } + catch (IOException ignore) { + return "Unknown Version"; + } + } + //endregion + + { + // GUI initializer generated by IntelliJ IDEA GUI Designer + // >>> IMPORTANT!! <<< + // DO NOT EDIT OR ADD ANY CODE HERE! + $$$setupUI$$$(); + } + + /** + * Method generated by IntelliJ IDEA GUI Designer + * >>> IMPORTANT!! <<< + * DO NOT edit this method OR call it in your code! + * + * @noinspection ALL + */ + private void $$$setupUI$$$() { + topPanel = new JPanel(); + topPanel.setLayout(new GridLayoutManager(2, 2, new Insets(10, 10, 10, 10), -1, -1)); + progressBar = new JProgressBar(); + progressBar.setStringPainted(true); + topPanel.add( + progressBar, new GridConstraints( + 1, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_FIXED, + null, new Dimension(300, 20), null, 0, false)); + cancelButton = new JButton(); + cancelButton.setText("Cancel"); + topPanel.add( + cancelButton, new GridConstraints( + 1, 1, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, + GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); + actionLabel = new JLabel(); + actionLabel.setText("Downloading..."); + topPanel.add( + actionLabel, new GridConstraints( + 0, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, + null, 0, false)); + } + + /** + * @noinspection ALL + */ + public JComponent $$$getRootComponent$$$() { + return topPanel; + } +} diff --git a/updater/src/main/resources/images/h-rider.png b/updater/src/main/resources/images/h-rider.png new file mode 100644 index 0000000..a9f8646 Binary files /dev/null and b/updater/src/main/resources/images/h-rider.png differ