diff --git a/README.md b/README.md index 96a81ef..e3502e0 100644 --- a/README.md +++ b/README.md @@ -49,26 +49,30 @@ You can easily expand your configurations using the generic template, see the [J Please refer [here](https://lightstreamer.com/docs/ls-server/latest/General%20Concepts.pdf) for more details about Lightstreamer Adapters. ## Install + If you want to install a version of this demo in your local Lightstreamer Server, follow these steps: -* Download *Lightstreamer Server* (Lightstreamer Server comes with a free non-expiring demo license for 20 connected users; this should be preferred to using COMMUNITY edition, otherwise you would see a limit on the event rate) from [Lightstreamer Download page](http://www.lightstreamer.com/download.htm), and install it, as explained in the `GETTING_STARTED.TXT` file in the installation home directory. +* Download *Lightstreamer Server* (Lightstreamer Server comes with a free non-expiring demo license for 20 connected users; this should be preferred to using COMMUNITY edition, otherwise you would see a limit on the event rate) from [Lightstreamer Download page](https://lightstreamer.com/download/), and install it, as explained in the `GETTING_STARTED.TXT` file in the installation home directory. * Get the `deploy.zip` file of the [latest release](https://github.com/Lightstreamer/Lightstreamer-example-LeapMotion-adapter-java/releases), unzip it, and copy the just unzipped `LeapDemo` folder into the `adapters` folder of your Lightstreamer Server installation. -* Download [croftsoft](http://sourceforge.net/projects/croftsoft/files/) library and compile a `croftsoft-math.jar` version. Please make sure to include: applet, io, inlp, lang, and math packages. Copy the just compiled `croftsoft-math.jar` file in the `adapters/LeapDemo/lib` folder. +* [Optional] Customize logging settings in log4j configuration file `LeapDemo/classes/log4j2.xml`. * Launch Lightstreamer Server. * Test the Adapter, launching one of the clients listed in [Clients Using This Adapter](https://github.com/Lightstreamer/Lightstreamer-example-LeapMotion-adapter-java#clients-using-this-adapter). ## Build -To build your own version of `LS_leapdemo_adapters.jar`, instead of using the one provided in the `deploy.zip` file from the [Install](https://github.com/Lightstreamer/Lightstreamer-example-LeapMotion-adapter-java#install) section above, follow these steps: -* Clone this project -* Get the `ls-adapter-interface.jar` file from the [Lightstreamer distribution](http://www.lightstreamer.com/download) and copy it into the `lib` folder. -* Get the `log4j-1.2.17.jar` file from [Apache log4j](https://logging.apache.org/log4j/1.2/) and copy it into the `lib` folder. -* Download [croftsoft](http://sourceforge.net/projects/croftsoft/files/) library and compile a `croftsoft-math.jar` version. Please make sure to include: applet, io, inlp, lang and math packages. Put the just compiled `croftsoft-math.jar` file in the `lib` folder. -* Build the java source files in the `src` folder into a `LS_leapdemo_adapters.jar` file. Here is an example for that: -```sh - > mkdir classes - > javac -classpath ./lib/croftsoft-math.jar;./lib/ls-adapter-interface.jar;./lib/log4j-1.2.17.jar -d ./classes ./src/com/lightstreamer/adapters/\LeapMotionDemo/*.java ./src/com/lightstreamer/adapters/\LeapMotionDemo/engine3D/*.java ./src/com/lightstreamer/adapters/\LeapMotionDemo/room/*.java - > jar cvf LS_leapdemo_adapters.jar -C classes com + +TTo build your own version of `example-LeapMotion-adapter-java-0.0.1-SNAPSHOT.jar`, instead of using the one provided in the `deploy.zip` file from the [Install](https://github.com/Lightstreamer/Lightstreamer-example-LeapMotion-adapter-java#install) section above, you have two options: +either use [Maven](https://maven.apache.org/) (or other build tools) to take care of dependencies and building (recommended) or gather the necessary jars yourself and build it manually. +As a precondition for compiling you need to download [croftsoft](http://sourceforge.net/projects/croftsoft/files/) library and copy the sources of applet, io, jnlp, lang and math packages into `src\main\java\` folder of this project. +For the sake of simplicity only the Maven case is detailed here. + +### Maven + +You can easily build and run this application using Maven through the pom.xml file located in the root folder of this project. As an alternative, you can use an alternative build tool (e.g. Gradle, Ivy, etc.) by converting the provided pom.xml file. + +Assuming Maven is installed and available in your path you can build the demo by running +```sh + mvn install dependency:copy-dependencies ``` -* Copy the just compiled `LS_leapdemo_adapters.jar` in the `adapters/LeapDemo/lib` folder of your Lightstreamer Server installation. + ## See Also @@ -82,5 +86,7 @@ To build your own version of `LS_leapdemo_adapters.jar`, instead of using the on ## Lightstreamer Compatibility Notes -* Compatible with Lightstreamer SDK for Java In-Process Adapters since 6.0 + +- Compatible with Lightstreamer SDK for Java In-Process Adapters since 7.3. +- For a version of this example compatible with Lightstreamer SDK for Java Adapters version 6.0, please refer to [this tag](https://github.com/Lightstreamer/Lightstreamer-example-LeapMotion-adapter-java/tree/pre_mvn). - For a version of this example compatible with Lightstreamer SDK for Java Adapters version 5.1, please refer to [this tag](https://github.com/Lightstreamer/Lightstreamer-example-LeapMotion-adapter-java/tree/for_Lightstreamer_5.1). \ No newline at end of file diff --git a/deployment_LS/README.md b/deployment_LS/README.md deleted file mode 100644 index bc7c060..0000000 --- a/deployment_LS/README.md +++ /dev/null @@ -1,2 +0,0 @@ -This folder contains the binaries and the configuration resources needed to plug the Leap Motion Demo Adapter into Lightstreamer Server. -For a complete installation guide please refer to the main README of the project. diff --git a/deployment_LS/leapdemo/adapters.xml b/deployment_LS/leapdemo/adapters.xml deleted file mode 100644 index e0665e4..0000000 --- a/deployment_LS/leapdemo/adapters.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - com.lightstreamer.adapters.LeapMotionDemo.LeapMotionMetaDataAdapter - - - - com.lightstreamer.adapters.LeapMotionDemo.LeapMotionDataAdapter - - \ No newline at end of file diff --git a/lib/README.md b/lib/README.md deleted file mode 100644 index 8d2f8ed..0000000 --- a/lib/README.md +++ /dev/null @@ -1,5 +0,0 @@ -This folder should contain all the library files needed to compile the LeapMotion Adapter. Please drop here the: - - log4j-1.2.17.jar from [Apache log4j](https://logging.apache.org/log4j/1.2/); - ls-adapter-interface.jar from the /lib folder of latest Lightstreamer distribution; - croftsoft-math.jar CroftSoft Code Library. \ No newline at end of file diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..836126b --- /dev/null +++ b/pom.xml @@ -0,0 +1,85 @@ + + 4.0.0 + + com.lightstreamer + example-LeapMotion-adapter-java + 0.0.1-SNAPSHOT + jar + + example-LeapMotion-adapter-java + http://maven.apache.org + + + 1.8 + 1.8 + UTF-8 + + + + src + + + org.apache.maven.plugins + maven-compiler-plugin + 3.1 + + + org.codehaus.mojo + exec-maven-plugin + 1.2.1 + + + + java + + + + + com.lightstreamer.adapters.LeapMotionDemo.LeapMotionMetaDataAdapter + + + + + + + + + Twitter + Twitter repository + https://maven.twttr.com/ + + + Anahata + Anahata Repository + http://repo.anahata.uno/artifactory/anahata-public/ + + + + + + + junit + junit + 4.13.1 + test + + + com.lightstreamer + ls-adapter-inprocess + 7.3.0 + + + org.apache.logging.log4j + log4j-core + 2.13.3 + + + + javax.jnlp + jnlp + 7.0 + + + + \ No newline at end of file diff --git a/src/com/lightstreamer/adapters/LeapMotionDemo/Constants.java b/src/main/java/com/lightstreamer/adapters/LeapMotionDemo/Constants.java similarity index 97% rename from src/com/lightstreamer/adapters/LeapMotionDemo/Constants.java rename to src/main/java/com/lightstreamer/adapters/LeapMotionDemo/Constants.java index 454639d..54f5794 100644 --- a/src/com/lightstreamer/adapters/LeapMotionDemo/Constants.java +++ b/src/main/java/com/lightstreamer/adapters/LeapMotionDemo/Constants.java @@ -1,53 +1,53 @@ -/* - Copyright (c) Lightstreamer Srl - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ -package com.lightstreamer.adapters.LeapMotionDemo; - -public class Constants { - - public static String USER_SUBSCRIPTION = "user_"; - public static String ROOMPOSITION_SUBSCRIPTION = "roompos_"; - public static String ROOMCHATLIST_SUBSCRIPTION = "roomchatlist_"; - - public static String SPLIT_CHAR_REG = "\\|"; - public static String SPLIT_CHAR = "|"; - - public static final String LOGGER_CAT = "LS_demos_Logger.LeapDemo.adapters"; - public static final String CHAT_CAT = "LS_demos_Logger.LeapDemo.chat"; - public static final String WORLD_CAT = "LS_demos_Logger.LeapDemo.world"; - - public static final String USER_ID = "USER_ID"; - - public static final String NICK_MESSAGE = "nick|"; - public static final String STATUS_MESSAGE = "status|"; - public static final String VOID_STATUS_ID = "0"; - public static final String ENTER_ROOM = "enter|"; - public static final String EXIT_ROOM = "leave|"; - - public static final String GRAB_MESSAGE = "grab|"; - public static final String RELEASE_MESSAGE = "release|"; - public static final String MOVE_MESSAGE = "move|"; - - public static final int BASE_RATE = 10; - public static final int FRAME_INTERVAL = 50; - - - public static String getVal(String original, String type) { - if(original.indexOf(type) == 0) { - return original.substring(type.length()); - } - return null; - } -} +/* + Copyright (c) Lightstreamer Srl + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ +package com.lightstreamer.adapters.LeapMotionDemo; + +public class Constants { + + public static String USER_SUBSCRIPTION = "user_"; + public static String ROOMPOSITION_SUBSCRIPTION = "roompos_"; + public static String ROOMCHATLIST_SUBSCRIPTION = "roomchatlist_"; + + public static String SPLIT_CHAR_REG = "\\|"; + public static String SPLIT_CHAR = "|"; + + public static final String LOGGER_CAT = "LS_demos_Logger.LeapDemo.adapters"; + public static final String CHAT_CAT = "LS_demos_Logger.LeapDemo.chat"; + public static final String WORLD_CAT = "LS_demos_Logger.LeapDemo.world"; + + public static final String USER_ID = "USER_ID"; + + public static final String NICK_MESSAGE = "nick|"; + public static final String STATUS_MESSAGE = "status|"; + public static final String VOID_STATUS_ID = "0"; + public static final String ENTER_ROOM = "enter|"; + public static final String EXIT_ROOM = "leave|"; + + public static final String GRAB_MESSAGE = "grab|"; + public static final String RELEASE_MESSAGE = "release|"; + public static final String MOVE_MESSAGE = "move|"; + + public static final int BASE_RATE = 10; + public static final int FRAME_INTERVAL = 50; + + + public static String getVal(String original, String type) { + if(original.indexOf(type) == 0) { + return original.substring(type.length()); + } + return null; + } +} diff --git a/src/com/lightstreamer/adapters/LeapMotionDemo/LeapMotionDataAdapter.java b/src/main/java/com/lightstreamer/adapters/LeapMotionDemo/LeapMotionDataAdapter.java similarity index 96% rename from src/com/lightstreamer/adapters/LeapMotionDemo/LeapMotionDataAdapter.java rename to src/main/java/com/lightstreamer/adapters/LeapMotionDemo/LeapMotionDataAdapter.java index 4f5ec7c..1f6936f 100644 --- a/src/com/lightstreamer/adapters/LeapMotionDemo/LeapMotionDataAdapter.java +++ b/src/main/java/com/lightstreamer/adapters/LeapMotionDemo/LeapMotionDataAdapter.java @@ -1,338 +1,340 @@ -/* - Copyright (c) Lightstreamer Srl - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ -package com.lightstreamer.adapters.LeapMotionDemo; - -import java.io.File; -import java.util.HashMap; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - -import org.apache.log4j.Logger; -import org.apache.log4j.xml.DOMConfigurator; - -import com.lightstreamer.adapters.LeapMotionDemo.engine3D.Universe; -import com.lightstreamer.adapters.LeapMotionDemo.engine3D.UniverseListener; -import com.lightstreamer.adapters.LeapMotionDemo.room.ChatRoom; -import com.lightstreamer.adapters.LeapMotionDemo.room.ChatRoomListener; -import com.lightstreamer.adapters.LeapMotionDemo.room.User; -import com.lightstreamer.interfaces.data.DataProviderException; -import com.lightstreamer.interfaces.data.FailureException; -import com.lightstreamer.interfaces.data.ItemEventListener; -import com.lightstreamer.interfaces.data.SmartDataProvider; -import com.lightstreamer.interfaces.data.SubscriptionException; - -public class LeapMotionDataAdapter implements SmartDataProvider, UniverseListener, ChatRoomListener { - - public static final ConcurrentHashMap feedMap = - new ConcurrentHashMap(); - - private Logger logger; - - private Universe universe = new Universe(this); - private ChatRoom chat = new ChatRoom(this); - private ItemEventListener listener; - - @Override - public void init(Map params, File configDir) throws DataProviderException { - String logConfig = (String) params.get("log_config"); - if (logConfig != null) { - File logConfigFile = new File(configDir, logConfig); - String logRefresh = (String) params.get("log_config_refresh_seconds"); - if (logRefresh != null) { - DOMConfigurator.configureAndWatch(logConfigFile.getAbsolutePath(), Integer.parseInt(logRefresh) * 1000); - } else { - DOMConfigurator.configure(logConfigFile.getAbsolutePath()); - } - } //else the bridge to logback is expected - - logger = Logger.getLogger(Constants.LOGGER_CAT); - - logger.info("Adapter Logger start."); - - // Read the Adapter Set name, which is supplied by the Server as a parameter - String adapterSetId = (String) params.get("adapters_conf.id"); - - // Put a reference to this instance on a static map - // to be read by the Metadata Adapter - feedMap.put(adapterSetId, this); - - logger.info("LeapMotionDataAdapter ready"); - - } - - @Override - public void setListener(ItemEventListener listener) { - this.listener = listener; - } - - ChatRoom getChatFeed() { - return this.chat; - } - - Universe getUniverse() { - return this.universe; - } - - @Override - public boolean isSnapshotAvailable(String item) - throws SubscriptionException { - - if (item.indexOf(Constants.USER_SUBSCRIPTION) == 0) { - return false; //currently does not generate any event at all (and never will) - } else if (item.indexOf(Constants.ROOMPOSITION_SUBSCRIPTION) == 0) { - return true; - } else if (item.indexOf(Constants.ROOMCHATLIST_SUBSCRIPTION) == 0) { - return true; - } else { - return true; - } - } - - - @Override - public synchronized void subscribe(String item, Object handle, boolean needsIterator) - throws SubscriptionException, FailureException { - - String val; - if (( val = Constants.getVal(item,Constants.USER_SUBSCRIPTION)) != null) { - //DISTINCT used only to signal presence - logger.debug("User subscription: " + item); - - //user is created on subscription and destroyed on unsubscription - chat.startUserMessageListen(val,handle); - - } else if (item.indexOf(Constants.ROOMPOSITION_SUBSCRIPTION) == 0) { - //COMMAND contains list of users and object positions - logger.debug("Position subscription: " + item); - - String roomId = item.substring(Constants.ROOMPOSITION_SUBSCRIPTION.length()); - - universe.startWatchingWorld(roomId, handle); - - } else if (item.indexOf(Constants.ROOMCHATLIST_SUBSCRIPTION) == 0) { - //COMMAND contains users of a certain room - logger.debug("Room list subscription: " + item); - - String roomId = item.substring(Constants.ROOMCHATLIST_SUBSCRIPTION.length()); - chat.startRoomListen(roomId,handle);// will add the room if non-existent (room may exist if a user entered it even if no one is listening to it) - - } else { - //MERGE subscription for user status and nick + their commands and forced positions - logger.debug("User status subscription: " + item); - chat.startUserStatusListen(item,handle); - } - } - - @Override - public synchronized void unsubscribe(String item) throws SubscriptionException, - FailureException { - - String val; - if (( val = Constants.getVal(item,Constants.USER_SUBSCRIPTION)) != null) { - logger.debug("User unsubscription: " + item); - - chat.stopUserMessageListen(val); - - } else if (item.indexOf(Constants.ROOMPOSITION_SUBSCRIPTION) == 0) { - logger.debug("Position unsubscription: " + item); - - String roomId = item.substring(Constants.ROOMPOSITION_SUBSCRIPTION.length()); - - universe.stopWatchingWorld(roomId); - - } else if (item.indexOf(Constants.ROOMCHATLIST_SUBSCRIPTION) == 0) { - logger.debug("Room list unsubscription: " + item); - - String roomId = item.substring(Constants.ROOMCHATLIST_SUBSCRIPTION.length()); - chat.stopRoomListen(roomId); - } else { - logger.debug("User status unsubscription: " + item); - - chat.stopUserStatusListen(item); - } - } - - //user related events sequentiality is ensured by the chat class; are only generated if there is the associated handle - - @Override - public void onUserEnter(User user, String room, Object roomStatusHandle, boolean realTimeEvent) { - String id = user.getId(); - logger.debug(id + " enters " + room); - - HashMap update = new HashMap(); - update.put(SmartDataProvider.KEY_FIELD, id); - update.put(SmartDataProvider.COMMAND_FIELD, SmartDataProvider.ADD_COMMAND); - - this.listener.smartUpdate(roomStatusHandle, update, !realTimeEvent); - - universe.addPlayerToWorld(id,room); - - } - - @Override - public void onRoomListComplete(String id, Object roomStatusHandle) { - logger.debug(id + " filled"); - - this.listener.smartEndOfSnapshot(roomStatusHandle); - } - - @Override - public void onUserExit(User user, String room, Object roomStatusHandle) { - String id = user.getId(); - logger.debug(id + " exits " + room); - - HashMap update = new HashMap(); - update.put(SmartDataProvider.KEY_FIELD, id); - update.put(SmartDataProvider.COMMAND_FIELD, SmartDataProvider.DELETE_COMMAND); - - this.listener.smartUpdate(roomStatusHandle, update, false); - - universe.removePlayerFromWorld(id,room); - } - - - @Override - public void onUserStatusChange(User user, String nick, String statusId, String status, Map extra, Object userStatusHandle, boolean realTimeEvent) { - String id = user.getId(); - logger.debug(id + " has new status/nick"); - - if (extra == null) { - extra = new HashMap(); - } - - if (nick != null) { - extra.put("nick", nick); - } - if (status != null) { - extra.put("status", status); - extra.put("statusId", statusId); - } - - this.listener.smartUpdate(userStatusHandle, extra, !realTimeEvent); - - //note: in case of a !realtime event, if we assign an initial movement to the players we'll have to check if the player for this user already exists and grab its forces - - } - - @Override - public void onUserMessage(String id, String message, String room, Object roomHandle, boolean realTimeEvent) { - //not used - logger.debug(id + " sent a message to " + room); - } - - @Override - public void onPrivateMessage(String fromId, String toId, String message, Object userHandle) { //always realtime (can't send messages to disconnected users) - //not used - not implemented - logger.debug(toId + " got a private message from " + fromId); - } - - @Override - public void onNewUser(String id) { - //do nothing - logger.debug(id + " is ready"); - } - - @Override - public void onUserDeleted(String id) { - //do nothing - logger.debug(id + " is gone"); - } - - // universe events are generated even if there is no handle - - @Override - public void onWorldComplete(String worldId, Object worldHandle) { - logger.debug("World " + worldId + " flushed"); - - if (worldHandle != null) { - this.listener.smartEndOfSnapshot(worldHandle); - } - } - - @Override - public void onPlayerCreated(String id, String worldId, Object worldHandle, HashMap currentPosition, HashMap currentImpulses, boolean realTimeEvent) { - logger.debug(id + " enters world " + worldId); - - if (worldHandle != null) { - currentPosition.put(SmartDataProvider.KEY_FIELD, id); - currentPosition.put(SmartDataProvider.COMMAND_FIELD, SmartDataProvider.ADD_COMMAND); - this.listener.smartUpdate(worldHandle, currentPosition, !realTimeEvent); - } - - Object userHandle = chat.getUserStatusHandle(id); - if (userHandle != null) { - this.listener.smartUpdate(userHandle, currentImpulses, false); - } - } - - @Override - public void onPlayerMoved(String id, String worldId, Object worldHandle, - HashMap currentPosition, boolean forced) { - - if (logger.isTraceEnabled()) { - logger.trace(id + " in world " + worldId + " moves"); - } - - if (!forced) { - if (worldHandle != null) { - currentPosition.put(SmartDataProvider.KEY_FIELD, id); - currentPosition.put(SmartDataProvider.COMMAND_FIELD, SmartDataProvider.UPDATE_COMMAND); - this.listener.smartUpdate(worldHandle, currentPosition, false); - } - } else { - Object userHandle = chat.getUserStatusHandle(id); - if (userHandle != null) { - this.listener.smartUpdate(userHandle, currentPosition, false); - } - } - } - - @Override - public void onPlayerActed(String id, String worldId, Object worldHandle, - HashMap currentImpulses) { - - if (logger.isTraceEnabled()) { - logger.trace(id + " in world " + worldId + " acts"); //block/release actions - } - - Object userHandle = chat.getUserStatusHandle(id); - if (userHandle != null) { - this.listener.smartUpdate(userHandle, currentImpulses, false); - } - } - - @Override - public void onPlayerDisposed(String id, String worldId, Object worldHandle) { - logger.debug(id + " exits world " + worldId); - if (worldHandle != null) { - HashMap update = new HashMap(); - update.put(SmartDataProvider.KEY_FIELD, id); - update.put(SmartDataProvider.COMMAND_FIELD, SmartDataProvider.DELETE_COMMAND); - - this.listener.smartUpdate(worldHandle, update, false); - } - } - - - @Override - public void subscribe(String arg0, boolean arg1) - throws SubscriptionException, FailureException { - // unused - logger.error("Unexpected call"); - throw new SubscriptionException("Unexpected call"); - } - -} +/* + Copyright (c) Lightstreamer Srl + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ +package com.lightstreamer.adapters.LeapMotionDemo; + +import java.io.File; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import com.lightstreamer.adapters.LeapMotionDemo.engine3D.Universe; +import com.lightstreamer.adapters.LeapMotionDemo.engine3D.UniverseListener; +import com.lightstreamer.adapters.LeapMotionDemo.room.ChatRoom; +import com.lightstreamer.adapters.LeapMotionDemo.room.ChatRoomListener; +import com.lightstreamer.adapters.LeapMotionDemo.room.User; +import com.lightstreamer.interfaces.data.DataProviderException; +import com.lightstreamer.interfaces.data.FailureException; +import com.lightstreamer.interfaces.data.ItemEventListener; +import com.lightstreamer.interfaces.data.SmartDataProvider; +import com.lightstreamer.interfaces.data.SubscriptionException; + +public class LeapMotionDataAdapter implements SmartDataProvider, UniverseListener, ChatRoomListener { + + public static final ConcurrentHashMap feedMap = + new ConcurrentHashMap(); + + private Logger logger; + + private Universe universe = new Universe(this); + private ChatRoom chat = new ChatRoom(this); + private ItemEventListener listener; + + @Override + public void init(Map params, File configDir) throws DataProviderException { + /* + String logConfig = (String) params.get("log_config"); + if (logConfig != null) { + File logConfigFile = new File(configDir, logConfig); + String logRefresh = (String) params.get("log_config_refresh_seconds"); + if (logRefresh != null) { + DOMConfigurator.configureAndWatch(logConfigFile.getAbsolutePath(), Integer.parseInt(logRefresh) * 1000); + } else { + DOMConfigurator.configure(logConfigFile.getAbsolutePath()); + } + } //else the bridge to logback is expected + */ + + logger = LogManager.getLogger(Constants.LOGGER_CAT); + + logger.info("Adapter Logger start."); + + // Read the Adapter Set name, which is supplied by the Server as a parameter + String adapterSetId = (String) params.get("adapters_conf.id"); + + // Put a reference to this instance on a static map + // to be read by the Metadata Adapter + feedMap.put(adapterSetId, this); + + logger.info("LeapMotionDataAdapter ready"); + + } + + @Override + public void setListener(ItemEventListener listener) { + this.listener = listener; + } + + ChatRoom getChatFeed() { + return this.chat; + } + + Universe getUniverse() { + return this.universe; + } + + @Override + public boolean isSnapshotAvailable(String item) + throws SubscriptionException { + + if (item.indexOf(Constants.USER_SUBSCRIPTION) == 0) { + return false; //currently does not generate any event at all (and never will) + } else if (item.indexOf(Constants.ROOMPOSITION_SUBSCRIPTION) == 0) { + return true; + } else if (item.indexOf(Constants.ROOMCHATLIST_SUBSCRIPTION) == 0) { + return true; + } else { + return true; + } + } + + + @Override + public synchronized void subscribe(String item, Object handle, boolean needsIterator) + throws SubscriptionException, FailureException { + + String val; + if (( val = Constants.getVal(item,Constants.USER_SUBSCRIPTION)) != null) { + //DISTINCT used only to signal presence + logger.debug("User subscription: " + item); + + //user is created on subscription and destroyed on unsubscription + chat.startUserMessageListen(val,handle); + + } else if (item.indexOf(Constants.ROOMPOSITION_SUBSCRIPTION) == 0) { + //COMMAND contains list of users and object positions + logger.debug("Position subscription: " + item); + + String roomId = item.substring(Constants.ROOMPOSITION_SUBSCRIPTION.length()); + + universe.startWatchingWorld(roomId, handle); + + } else if (item.indexOf(Constants.ROOMCHATLIST_SUBSCRIPTION) == 0) { + //COMMAND contains users of a certain room + logger.debug("Room list subscription: " + item); + + String roomId = item.substring(Constants.ROOMCHATLIST_SUBSCRIPTION.length()); + chat.startRoomListen(roomId,handle);// will add the room if non-existent (room may exist if a user entered it even if no one is listening to it) + + } else { + //MERGE subscription for user status and nick + their commands and forced positions + logger.debug("User status subscription: " + item); + chat.startUserStatusListen(item,handle); + } + } + + @Override + public synchronized void unsubscribe(String item) throws SubscriptionException, + FailureException { + + String val; + if (( val = Constants.getVal(item,Constants.USER_SUBSCRIPTION)) != null) { + logger.debug("User unsubscription: " + item); + + chat.stopUserMessageListen(val); + + } else if (item.indexOf(Constants.ROOMPOSITION_SUBSCRIPTION) == 0) { + logger.debug("Position unsubscription: " + item); + + String roomId = item.substring(Constants.ROOMPOSITION_SUBSCRIPTION.length()); + + universe.stopWatchingWorld(roomId); + + } else if (item.indexOf(Constants.ROOMCHATLIST_SUBSCRIPTION) == 0) { + logger.debug("Room list unsubscription: " + item); + + String roomId = item.substring(Constants.ROOMCHATLIST_SUBSCRIPTION.length()); + chat.stopRoomListen(roomId); + } else { + logger.debug("User status unsubscription: " + item); + + chat.stopUserStatusListen(item); + } + } + + //user related events sequentiality is ensured by the chat class; are only generated if there is the associated handle + + @Override + public void onUserEnter(User user, String room, Object roomStatusHandle, boolean realTimeEvent) { + String id = user.getId(); + logger.debug(id + " enters " + room); + + HashMap update = new HashMap(); + update.put(SmartDataProvider.KEY_FIELD, id); + update.put(SmartDataProvider.COMMAND_FIELD, SmartDataProvider.ADD_COMMAND); + + this.listener.smartUpdate(roomStatusHandle, update, !realTimeEvent); + + universe.addPlayerToWorld(id,room); + + } + + @Override + public void onRoomListComplete(String id, Object roomStatusHandle) { + logger.debug(id + " filled"); + + this.listener.smartEndOfSnapshot(roomStatusHandle); + } + + @Override + public void onUserExit(User user, String room, Object roomStatusHandle) { + String id = user.getId(); + logger.debug(id + " exits " + room); + + HashMap update = new HashMap(); + update.put(SmartDataProvider.KEY_FIELD, id); + update.put(SmartDataProvider.COMMAND_FIELD, SmartDataProvider.DELETE_COMMAND); + + this.listener.smartUpdate(roomStatusHandle, update, false); + + universe.removePlayerFromWorld(id,room); + } + + + @Override + public void onUserStatusChange(User user, String nick, String statusId, String status, Map extra, Object userStatusHandle, boolean realTimeEvent) { + String id = user.getId(); + logger.debug(id + " has new status/nick"); + + if (extra == null) { + extra = new HashMap(); + } + + if (nick != null) { + extra.put("nick", nick); + } + if (status != null) { + extra.put("status", status); + extra.put("statusId", statusId); + } + + this.listener.smartUpdate(userStatusHandle, extra, !realTimeEvent); + + //note: in case of a !realtime event, if we assign an initial movement to the players we'll have to check if the player for this user already exists and grab its forces + + } + + @Override + public void onUserMessage(String id, String message, String room, Object roomHandle, boolean realTimeEvent) { + //not used + logger.debug(id + " sent a message to " + room); + } + + @Override + public void onPrivateMessage(String fromId, String toId, String message, Object userHandle) { //always realtime (can't send messages to disconnected users) + //not used - not implemented + logger.debug(toId + " got a private message from " + fromId); + } + + @Override + public void onNewUser(String id) { + //do nothing + logger.debug(id + " is ready"); + } + + @Override + public void onUserDeleted(String id) { + //do nothing + logger.debug(id + " is gone"); + } + + // universe events are generated even if there is no handle + + @Override + public void onWorldComplete(String worldId, Object worldHandle) { + logger.debug("World " + worldId + " flushed"); + + if (worldHandle != null) { + this.listener.smartEndOfSnapshot(worldHandle); + } + } + + @Override + public void onPlayerCreated(String id, String worldId, Object worldHandle, HashMap currentPosition, HashMap currentImpulses, boolean realTimeEvent) { + logger.debug(id + " enters world " + worldId); + + if (worldHandle != null) { + currentPosition.put(SmartDataProvider.KEY_FIELD, id); + currentPosition.put(SmartDataProvider.COMMAND_FIELD, SmartDataProvider.ADD_COMMAND); + this.listener.smartUpdate(worldHandle, currentPosition, !realTimeEvent); + } + + Object userHandle = chat.getUserStatusHandle(id); + if (userHandle != null) { + this.listener.smartUpdate(userHandle, currentImpulses, false); + } + } + + @Override + public void onPlayerMoved(String id, String worldId, Object worldHandle, + HashMap currentPosition, boolean forced) { + + if (logger.isTraceEnabled()) { + logger.trace(id + " in world " + worldId + " moves"); + } + + if (!forced) { + if (worldHandle != null) { + currentPosition.put(SmartDataProvider.KEY_FIELD, id); + currentPosition.put(SmartDataProvider.COMMAND_FIELD, SmartDataProvider.UPDATE_COMMAND); + this.listener.smartUpdate(worldHandle, currentPosition, false); + } + } else { + Object userHandle = chat.getUserStatusHandle(id); + if (userHandle != null) { + this.listener.smartUpdate(userHandle, currentPosition, false); + } + } + } + + @Override + public void onPlayerActed(String id, String worldId, Object worldHandle, + HashMap currentImpulses) { + + if (logger.isTraceEnabled()) { + logger.trace(id + " in world " + worldId + " acts"); //block/release actions + } + + Object userHandle = chat.getUserStatusHandle(id); + if (userHandle != null) { + this.listener.smartUpdate(userHandle, currentImpulses, false); + } + } + + @Override + public void onPlayerDisposed(String id, String worldId, Object worldHandle) { + logger.debug(id + " exits world " + worldId); + if (worldHandle != null) { + HashMap update = new HashMap(); + update.put(SmartDataProvider.KEY_FIELD, id); + update.put(SmartDataProvider.COMMAND_FIELD, SmartDataProvider.DELETE_COMMAND); + + this.listener.smartUpdate(worldHandle, update, false); + } + } + + + @Override + public void subscribe(String arg0, boolean arg1) + throws SubscriptionException, FailureException { + // unused + logger.error("Unexpected call"); + throw new SubscriptionException("Unexpected call"); + } + +} diff --git a/src/com/lightstreamer/adapters/LeapMotionDemo/LeapMotionMetaDataAdapter.java b/src/main/java/com/lightstreamer/adapters/LeapMotionDemo/LeapMotionMetaDataAdapter.java similarity index 96% rename from src/com/lightstreamer/adapters/LeapMotionDemo/LeapMotionMetaDataAdapter.java rename to src/main/java/com/lightstreamer/adapters/LeapMotionDemo/LeapMotionMetaDataAdapter.java index fd706a8..1886bc3 100644 --- a/src/com/lightstreamer/adapters/LeapMotionDemo/LeapMotionMetaDataAdapter.java +++ b/src/main/java/com/lightstreamer/adapters/LeapMotionDemo/LeapMotionMetaDataAdapter.java @@ -1,300 +1,302 @@ -/* - Copyright (c) Lightstreamer Srl - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ -package com.lightstreamer.adapters.LeapMotionDemo; - -import java.io.File; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - -import org.apache.log4j.Logger; -import org.apache.log4j.xml.DOMConfigurator; - -import com.lightstreamer.adapters.LeapMotionDemo.engine3D.Universe; -import com.lightstreamer.adapters.LeapMotionDemo.room.ChatRoom; -import com.lightstreamer.adapters.metadata.LiteralBasedProvider; -import com.lightstreamer.interfaces.metadata.CreditsException; -import com.lightstreamer.interfaces.metadata.ItemsException; -import com.lightstreamer.interfaces.metadata.MetadataProviderException; -import com.lightstreamer.interfaces.metadata.Mode; -import com.lightstreamer.interfaces.metadata.NotificationException; - -public class LeapMotionMetaDataAdapter extends LiteralBasedProvider { - - /** - * Unique identification of the Adapter Set. It is used to uniquely - * identify the related Data Adapter instance; - * see feedMap on LeapMotionDataAdapter. - */ - private String adapterSetId; - /** - * Keeps the client context information supplied by Lightstreamer on the - * new session notifications. - * Session information is needed to pass the IP to logging purpose. - */ - private ConcurrentHashMap> sessions = new ConcurrentHashMap>(); - private ConcurrentHashMap ids = new ConcurrentHashMap(); - - private LeapMotionDataAdapter feed; - - private Logger logger; - - @Override - public void init(Map params, File configDir) throws MetadataProviderException { - String logConfig = (String) params.get("log_config"); - if (logConfig != null) { - File logConfigFile = new File(configDir, logConfig); - String logRefresh = (String) params.get("log_config_refresh_seconds"); - if (logRefresh != null) { - DOMConfigurator.configureAndWatch(logConfigFile.getAbsolutePath(), Integer.parseInt(logRefresh) * 1000); - } else { - DOMConfigurator.configure(logConfigFile.getAbsolutePath()); - } - } //else the bridge to logback is expected - - logger = Logger.getLogger(Constants.LOGGER_CAT); - - // Read the Adapter Set name, which is supplied by the Server as a parameter - this.adapterSetId = (String) params.get("adapters_conf.id"); - } - - @Override - public String[] getItems(String user, String session, String group) - throws ItemsException { - String[] split = super.getItems(user,session,group); - for (int i = 0; i sessionInfo = sessions.get(session); - if (sessionInfo == null) { - logger.warn("Can't find session " + session); - throw new ItemsException("Can't find session"); - } - - if (sessionInfo.containsKey(Constants.USER_ID)) { - //currently permit only one id per session - String prevVal = sessionInfo.get(Constants.USER_ID); - if (!val.equals(prevVal)) { - logger.warn("Session alredy owns an ID: " + val); - throw new ItemsException("Session alredy owns an ID: " + val); - } - - } else { - if (ids.containsKey(val)) { - logger.debug("Id already taken: " + val); - throw new ItemsException("Id already taken, try again"); - } - sessionInfo.put(Constants.USER_ID, val); - } - ids.put(val, session); - try { - this.loadFeed(); - } catch (CreditsException e) { - throw new ItemsException("Feed unavailable"); - } - ChatRoom chat = this.feed.getChatFeed(); - chat.startUser(val); - split[i] = Constants.USER_SUBSCRIPTION + val; - } - } - } - - return split; - - } - - @Override - public boolean modeMayBeAllowed(String item, Mode mode) { - if (item.indexOf(Constants.USER_SUBSCRIPTION) == 0 && mode == Mode.DISTINCT) { - return true; - - } else if (item.indexOf(Constants.ROOMPOSITION_SUBSCRIPTION) == 0 && mode == Mode.COMMAND) { - return true; - - } else if (item.indexOf(Constants.ROOMCHATLIST_SUBSCRIPTION) == 0 && mode == Mode.COMMAND) { - return true; - - } - - return mode == Mode.MERGE; - } - - @Override - public void notifyUserMessage(String user, String session, String message) - throws CreditsException, NotificationException { - - //be sure we can communicate with the data adapter - this.loadFeed(); - - //get the user id of the current user - String id = null; - String IP = null; - synchronized(sessions) { - Map sessionInfo = sessions.get(session); - if (sessionInfo == null) { - logger.warn("Can't find session " + session); - throw new CreditsException(-1, "Can't find user id (session missing)"); - } - id = sessionInfo.get(Constants.USER_ID); - if (id == null) { - logger.warn("Can't find user id"); - throw new CreditsException(-2, "Can't find user id (value missing)"); - } - - IP = sessionInfo.get("REMOTE_IP"); - } - - //nick| <-- changing nick - //status| <-- changing status message - //enter| <-- enter a room - //leave| <-- leave a room - //grab| <-- grab the cube - //release| <-- release the cube - //move| <-- move the cube - - String val; - if (( val = Constants.getVal(message,Constants.NICK_MESSAGE)) != null) { - logger.debug("new nick message from "+id+" received: " + message); - - ChatRoom chat = this.feed.getChatFeed(); - chat.changeUserNick(id, val, IP); - - } else if (( val = Constants.getVal(message,Constants.STATUS_MESSAGE)) != null) { - logger.debug("new status message from "+id+" received: " + message); - - ChatRoom chat = this.feed.getChatFeed(); - chat.changeUserStatus(id, val, Constants.VOID_STATUS_ID, IP); - - } else if (( val = Constants.getVal(message,Constants.ENTER_ROOM)) != null) { - logger.debug("new enter-room message from "+id+" received: " + message); - - ChatRoom chat = this.feed.getChatFeed(); - chat.enterRoom(id,val); - - } else if (( val = Constants.getVal(message,Constants.EXIT_ROOM)) != null) { - logger.debug("new exit-room message from "+id+" received: " + message); - - ChatRoom chat = this.feed.getChatFeed(); - chat.leaveRoom(id,val); - - } else if (( val = Constants.getVal(message,Constants.GRAB_MESSAGE)) != null) { - if (logger.isTraceEnabled()) { - logger.trace("new grab message from "+id+" received: " + message); - } - - Universe universe = this.feed.getUniverse(); - universe.block(id,val); - - } else if (( val = Constants.getVal(message,Constants.RELEASE_MESSAGE)) != null) { - if (logger.isTraceEnabled()) { - logger.trace("new release message from "+id+" received: " + message); - } - - Universe universe = this.feed.getUniverse(); - String[] values = val.split(Constants.SPLIT_CHAR_REG); - double[] dobuleValues = getDoubles(values); - universe.release(id,values[0],dobuleValues[0],dobuleValues[1],dobuleValues[2]); - - } else if (( val = Constants.getVal(message,Constants.MOVE_MESSAGE)) != null) { - if (logger.isTraceEnabled()) { - logger.trace("new move message from "+id+" received: " + message); - } - - Universe universe = this.feed.getUniverse(); - String[] values = val.split(Constants.SPLIT_CHAR_REG); - double[] dobuleValues = getDoubles(values); - universe.move(id,values[0],dobuleValues[0],dobuleValues[1],dobuleValues[2]); - - } else { - logger.warn("unexpected message from "+id+" received: " + message); - throw new CreditsException(-3, "Unexpected message"); - } - } - - private static double[] getDoubles(String[] values) throws CreditsException { - if (values.length != 4) { - throw new CreditsException(-4, "wrongly formatted message"); - } - double[] res = new double[3]; - try { - res[0] = Double.parseDouble(values[1]); - res[1] = Double.parseDouble(values[2]); - res[2] = Double.parseDouble(values[3]); - } catch(NumberFormatException nf) { - throw new CreditsException(-5, "wrongly formatted number"); - } - return res; - } - - - private void loadFeed() throws CreditsException { - if (this.feed == null) { - try { - // Get the LeapMotionDataAdapter instance to bind it with this - // Metadata Adapter and send chat messages through it - this.feed = LeapMotionDataAdapter.feedMap.get(this.adapterSetId); - logger.debug("LeapMotionDataAdapter bound"); - } catch(Throwable t) { - // It can happen if the Chat Data Adapter jar was not even - // included in the Adapter Set lib directory (the LeapMotion - // Data Adapter could not be included in the Adapter Set as well) - logger.error("LeapMotionDataAdapter class was not loaded: " + t); - throw new CreditsException(0, "No feed available", "No feed available"); - } - - if (this.feed == null) { - // The feed is not yet available on the static map, maybe the - // LeapMotion Data Adapter was not included in the Adapter Set - logger.error("LeapMotionDataAdapter not found"); - throw new CreditsException(0, "No feed available", "No feed available"); - } - } - } - - - - @Override - public void notifyNewSession(String user, String session, Map sessionInfo) throws CreditsException, NotificationException { - // Register the session details on the sessions HashMap. - logger.info("New session available " + session); - sessions.put(session, sessionInfo); - } - - @Override - public void notifySessionClose(String session) throws NotificationException { - synchronized(sessions) { - //we have to remove session information from the session HashMap - logger.info("Clearing session " + session); - Map sessionInfo = sessions.remove(session); - String id = sessionInfo.get(Constants.USER_ID); - if (id != null) { - ids.remove(id); - - try { - this.loadFeed(); - } catch (CreditsException e) { - logger.error("Unexpected: feed not available"); - return; - } - - ChatRoom chat = this.feed.getChatFeed(); - chat.leaveAllRooms(id); - chat.stopUser(id); - } - } - } - -} +/* + Copyright (c) Lightstreamer Srl + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ +package com.lightstreamer.adapters.LeapMotionDemo; + +import java.io.File; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import com.lightstreamer.adapters.LeapMotionDemo.engine3D.Universe; +import com.lightstreamer.adapters.LeapMotionDemo.room.ChatRoom; +import com.lightstreamer.adapters.metadata.LiteralBasedProvider; +import com.lightstreamer.interfaces.metadata.CreditsException; +import com.lightstreamer.interfaces.metadata.ItemsException; +import com.lightstreamer.interfaces.metadata.MetadataProviderException; +import com.lightstreamer.interfaces.metadata.Mode; +import com.lightstreamer.interfaces.metadata.NotificationException; + +public class LeapMotionMetaDataAdapter extends LiteralBasedProvider { + + /** + * Unique identification of the Adapter Set. It is used to uniquely + * identify the related Data Adapter instance; + * see feedMap on LeapMotionDataAdapter. + */ + private String adapterSetId; + /** + * Keeps the client context information supplied by Lightstreamer on the + * new session notifications. + * Session information is needed to pass the IP to logging purpose. + */ + private ConcurrentHashMap> sessions = new ConcurrentHashMap>(); + private ConcurrentHashMap ids = new ConcurrentHashMap(); + + private LeapMotionDataAdapter feed; + + private Logger logger; + + @Override + public void init(Map params, File configDir) throws MetadataProviderException { + /* + String logConfig = (String) params.get("log_config"); + if (logConfig != null) { + File logConfigFile = new File(configDir, logConfig); + String logRefresh = (String) params.get("log_config_refresh_seconds"); + if (logRefresh != null) { + DOMConfigurator.configureAndWatch(logConfigFile.getAbsolutePath(), Integer.parseInt(logRefresh) * 1000); + } else { + DOMConfigurator.configure(logConfigFile.getAbsolutePath()); + } + } //else the bridge to logback is expected + */ + + logger = LogManager.getLogger(Constants.LOGGER_CAT); + + // Read the Adapter Set name, which is supplied by the Server as a parameter + this.adapterSetId = (String) params.get("adapters_conf.id"); + } + + @Override + public String[] getItems(String user, String session, String group) + throws ItemsException { + String[] split = super.getItems(user,session,group); + for (int i = 0; i sessionInfo = sessions.get(session); + if (sessionInfo == null) { + logger.warn("Can't find session " + session); + throw new ItemsException("Can't find session"); + } + + if (sessionInfo.containsKey(Constants.USER_ID)) { + //currently permit only one id per session + String prevVal = sessionInfo.get(Constants.USER_ID); + if (!val.equals(prevVal)) { + logger.warn("Session alredy owns an ID: " + val); + throw new ItemsException("Session alredy owns an ID: " + val); + } + + } else { + if (ids.containsKey(val)) { + logger.debug("Id already taken: " + val); + throw new ItemsException("Id already taken, try again"); + } + sessionInfo.put(Constants.USER_ID, val); + } + ids.put(val, session); + try { + this.loadFeed(); + } catch (CreditsException e) { + throw new ItemsException("Feed unavailable"); + } + ChatRoom chat = this.feed.getChatFeed(); + chat.startUser(val); + split[i] = Constants.USER_SUBSCRIPTION + val; + } + } + } + + return split; + + } + + @Override + public boolean modeMayBeAllowed(String item, Mode mode) { + if (item.indexOf(Constants.USER_SUBSCRIPTION) == 0 && mode == Mode.DISTINCT) { + return true; + + } else if (item.indexOf(Constants.ROOMPOSITION_SUBSCRIPTION) == 0 && mode == Mode.COMMAND) { + return true; + + } else if (item.indexOf(Constants.ROOMCHATLIST_SUBSCRIPTION) == 0 && mode == Mode.COMMAND) { + return true; + + } + + return mode == Mode.MERGE; + } + + @Override + public void notifyUserMessage(String user, String session, String message) + throws CreditsException, NotificationException { + + //be sure we can communicate with the data adapter + this.loadFeed(); + + //get the user id of the current user + String id = null; + String IP = null; + synchronized(sessions) { + Map sessionInfo = sessions.get(session); + if (sessionInfo == null) { + logger.warn("Can't find session " + session); + throw new CreditsException(-1, "Can't find user id (session missing)"); + } + id = sessionInfo.get(Constants.USER_ID); + if (id == null) { + logger.warn("Can't find user id"); + throw new CreditsException(-2, "Can't find user id (value missing)"); + } + + IP = sessionInfo.get("REMOTE_IP"); + } + + //nick| <-- changing nick + //status| <-- changing status message + //enter| <-- enter a room + //leave| <-- leave a room + //grab| <-- grab the cube + //release| <-- release the cube + //move| <-- move the cube + + String val; + if (( val = Constants.getVal(message,Constants.NICK_MESSAGE)) != null) { + logger.debug("new nick message from "+id+" received: " + message); + + ChatRoom chat = this.feed.getChatFeed(); + chat.changeUserNick(id, val, IP); + + } else if (( val = Constants.getVal(message,Constants.STATUS_MESSAGE)) != null) { + logger.debug("new status message from "+id+" received: " + message); + + ChatRoom chat = this.feed.getChatFeed(); + chat.changeUserStatus(id, val, Constants.VOID_STATUS_ID, IP); + + } else if (( val = Constants.getVal(message,Constants.ENTER_ROOM)) != null) { + logger.debug("new enter-room message from "+id+" received: " + message); + + ChatRoom chat = this.feed.getChatFeed(); + chat.enterRoom(id,val); + + } else if (( val = Constants.getVal(message,Constants.EXIT_ROOM)) != null) { + logger.debug("new exit-room message from "+id+" received: " + message); + + ChatRoom chat = this.feed.getChatFeed(); + chat.leaveRoom(id,val); + + } else if (( val = Constants.getVal(message,Constants.GRAB_MESSAGE)) != null) { + if (logger.isTraceEnabled()) { + logger.trace("new grab message from "+id+" received: " + message); + } + + Universe universe = this.feed.getUniverse(); + universe.block(id,val); + + } else if (( val = Constants.getVal(message,Constants.RELEASE_MESSAGE)) != null) { + if (logger.isTraceEnabled()) { + logger.trace("new release message from "+id+" received: " + message); + } + + Universe universe = this.feed.getUniverse(); + String[] values = val.split(Constants.SPLIT_CHAR_REG); + double[] dobuleValues = getDoubles(values); + universe.release(id,values[0],dobuleValues[0],dobuleValues[1],dobuleValues[2]); + + } else if (( val = Constants.getVal(message,Constants.MOVE_MESSAGE)) != null) { + if (logger.isTraceEnabled()) { + logger.trace("new move message from "+id+" received: " + message); + } + + Universe universe = this.feed.getUniverse(); + String[] values = val.split(Constants.SPLIT_CHAR_REG); + double[] dobuleValues = getDoubles(values); + universe.move(id,values[0],dobuleValues[0],dobuleValues[1],dobuleValues[2]); + + } else { + logger.warn("unexpected message from "+id+" received: " + message); + throw new CreditsException(-3, "Unexpected message"); + } + } + + private static double[] getDoubles(String[] values) throws CreditsException { + if (values.length != 4) { + throw new CreditsException(-4, "wrongly formatted message"); + } + double[] res = new double[3]; + try { + res[0] = Double.parseDouble(values[1]); + res[1] = Double.parseDouble(values[2]); + res[2] = Double.parseDouble(values[3]); + } catch(NumberFormatException nf) { + throw new CreditsException(-5, "wrongly formatted number"); + } + return res; + } + + + private void loadFeed() throws CreditsException { + if (this.feed == null) { + try { + // Get the LeapMotionDataAdapter instance to bind it with this + // Metadata Adapter and send chat messages through it + this.feed = LeapMotionDataAdapter.feedMap.get(this.adapterSetId); + logger.debug("LeapMotionDataAdapter bound"); + } catch(Throwable t) { + // It can happen if the Chat Data Adapter jar was not even + // included in the Adapter Set lib directory (the LeapMotion + // Data Adapter could not be included in the Adapter Set as well) + logger.error("LeapMotionDataAdapter class was not loaded: " + t); + throw new CreditsException(0, "No feed available", "No feed available"); + } + + if (this.feed == null) { + // The feed is not yet available on the static map, maybe the + // LeapMotion Data Adapter was not included in the Adapter Set + logger.error("LeapMotionDataAdapter not found"); + throw new CreditsException(0, "No feed available", "No feed available"); + } + } + } + + + + @Override + public void notifyNewSession(String user, String session, Map sessionInfo) throws CreditsException, NotificationException { + // Register the session details on the sessions HashMap. + logger.info("New session available " + session); + sessions.put(session, sessionInfo); + } + + @Override + public void notifySessionClose(String session) throws NotificationException { + synchronized(sessions) { + //we have to remove session information from the session HashMap + logger.info("Clearing session " + session); + Map sessionInfo = sessions.remove(session); + String id = sessionInfo.get(Constants.USER_ID); + if (id != null) { + ids.remove(id); + + try { + this.loadFeed(); + } catch (CreditsException e) { + logger.error("Unexpected: feed not available"); + return; + } + + ChatRoom chat = this.feed.getChatFeed(); + chat.leaveAllRooms(id); + chat.stopUser(id); + } + } + } + +} diff --git a/src/com/lightstreamer/adapters/LeapMotionDemo/engine3D/Base64Manager.java b/src/main/java/com/lightstreamer/adapters/LeapMotionDemo/engine3D/Base64Manager.java similarity index 97% rename from src/com/lightstreamer/adapters/LeapMotionDemo/engine3D/Base64Manager.java rename to src/main/java/com/lightstreamer/adapters/LeapMotionDemo/engine3D/Base64Manager.java index a1b5a89..3ea87e4 100644 --- a/src/com/lightstreamer/adapters/LeapMotionDemo/engine3D/Base64Manager.java +++ b/src/main/java/com/lightstreamer/adapters/LeapMotionDemo/engine3D/Base64Manager.java @@ -1,1477 +1,1477 @@ -/* - Copyright (c) Lightstreamer Srl - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ -package com.lightstreamer.adapters.LeapMotionDemo.engine3D; - -import java.io.IOException; - -public class Base64Manager { - public String encodeBytes(byte [] bytes, boolean dontBreakLines) throws IOException { - return Base64.encodeBytes(bytes, dontBreakLines ? Base64.DONT_BREAK_LINES : Base64.NO_OPTIONS); - } - - public byte [] decodeBuffer(String string) throws IOException { - return Base64.decode(string); - } - - private static void onCloseError(Exception e) { - System.out.println("Unexpected close error: " + e); - } - - /////////////////////////////////////////////////////////////////////////// - // Classe di supporto per l'encoding-decoding in base64 - // Fonte: http://iharder.sourceforge.net/base64/ - // - // MODIFICATO PER QUANTO RIGUARDA LA GESTIONE DEGLI ERRORI - // - // ATTENZIONE! NON SONO DISPONIBILI IMPLEMENTAZIONI NEL JDK, - // MA SONO DISPONIBILI NELLE LIBRERIE Apache Commons - - /** - * Encodes and decodes to and from Base64 notation. - * - *

- * Change Log: - *

- *
    - *
  • v2.1 - Cleaned up javadoc comments and unused variables and methods. Added - * some convenience methods for reading and writing to and from files.
  • - *
  • v2.0.2 - Now specifies UTF-8 encoding in places where the code fails on systems - * with other encodings (like EBCDIC).
  • - *
  • v2.0.1 - Fixed an error when decoding a single byte, that is, when the - * encoded data was a single byte.
  • - *
  • v2.0 - I got rid of methods that used booleans to set options. - * Now everything is more consolidated and cleaner. The code now detects - * when data that's being decoded is gzip-compressed and will decompress it - * automatically. Generally things are cleaner. You'll probably have to - * change some method calls that you were making to support the new - * options format (ints that you "OR" together).
  • - *
  • v1.5.1 - Fixed bug when decompressing and decoding to a - * byte[] using decode( String s, boolean gzipCompressed ). - * Added the ability to "suspend" encoding in the Output Stream so - * you can turn on and off the encoding if you need to embed base64 - * data in an otherwise "normal" stream (like an XML file).
  • - *
  • v1.5 - Output stream pases on flush() command but doesn't do anything itself. - * This helps when using GZIP streams. - * Added the ability to GZip-compress objects before encoding them.
  • - *
  • v1.4 - Added helper methods to read/write files.
  • - *
  • v1.3.6 - Fixed OutputStream.flush() so that 'position' is reset.
  • - *
  • v1.3.5 - Added flag to turn on and off line breaks. Fixed bug in input stream - * where last buffer being read, if not completely full, was not returned.
  • - *
  • v1.3.4 - Fixed when "improperly padded stream" error was thrown at the wrong time.
  • - *
  • v1.3.3 - Fixed I/O streams which were totally messed up.
  • - *
- * - *

- * I am placing this code in the Public Domain. Do with it as you will. - * This software comes with no guarantees or warranties but with - * plenty of well-wishing instead! - * Please visit http://iharder.net/base64 - * periodically to check for updates or to contribute improvements. - *

- * - * @author Robert Harder - * @author rob@iharder.net - * @version 2.1 - */ - protected static class Base64 - { - - /* ******** P U B L I C F I E L D S ******** */ - - - /** No options specified. Value is zero. */ - public final static int NO_OPTIONS = 0; - - /** Specify encoding. */ - public final static int ENCODE = 1; - - - /** Specify decoding. */ - public final static int DECODE = 0; - - - /** Specify that data should be gzip-compressed. */ - public final static int GZIP = 2; - - - /** Don't break lines when encoding (violates strict Base64 specification) */ - public final static int DONT_BREAK_LINES = 8; - - - /* ******** P R I V A T E F I E L D S ******** */ - - - /** Maximum line length (76) of Base64 output. */ - private final static int MAX_LINE_LENGTH = 76; - - - /** The equals sign (=) as a byte. */ - private final static byte EQUALS_SIGN = (byte)'='; - - - /** The new line character (\n) as a byte. */ - private final static byte NEW_LINE = (byte)'\n'; - - - /** Preferred encoding. */ - private final static String PREFERRED_ENCODING = "UTF-8"; - - - /** The 64 valid Base64 values. */ - private final static byte[] ALPHABET; - private final static byte[] _NATIVE_ALPHABET = /* May be something funny like EBCDIC */ - { - (byte)'A', (byte)'B', (byte)'C', (byte)'D', (byte)'E', (byte)'F', (byte)'G', - (byte)'H', (byte)'I', (byte)'J', (byte)'K', (byte)'L', (byte)'M', (byte)'N', - (byte)'O', (byte)'P', (byte)'Q', (byte)'R', (byte)'S', (byte)'T', (byte)'U', - (byte)'V', (byte)'W', (byte)'X', (byte)'Y', (byte)'Z', - (byte)'a', (byte)'b', (byte)'c', (byte)'d', (byte)'e', (byte)'f', (byte)'g', - (byte)'h', (byte)'i', (byte)'j', (byte)'k', (byte)'l', (byte)'m', (byte)'n', - (byte)'o', (byte)'p', (byte)'q', (byte)'r', (byte)'s', (byte)'t', (byte)'u', - (byte)'v', (byte)'w', (byte)'x', (byte)'y', (byte)'z', - (byte)'0', (byte)'1', (byte)'2', (byte)'3', (byte)'4', (byte)'5', - (byte)'6', (byte)'7', (byte)'8', (byte)'9', (byte)'+', (byte)'/' - }; - - /** Determine which ALPHABET to use. */ - static - { - byte[] __bytes; - try - { - __bytes = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".getBytes( PREFERRED_ENCODING ); - } // end try - catch (java.io.UnsupportedEncodingException use) - { - __bytes = _NATIVE_ALPHABET; // Fall back to native encoding - } // end catch - ALPHABET = __bytes; - } // end static - - - /** - * Translates a Base64 value to either its 6-bit reconstruction value - * or a negative number indicating some other meaning. - **/ - private final static byte[] DECODABET = - { - -9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 0 - 8 - -5,-5, // Whitespace: Tab and Linefeed - -9,-9, // Decimal 11 - 12 - -5, // Whitespace: Carriage Return - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 14 - 26 - -9,-9,-9,-9,-9, // Decimal 27 - 31 - -5, // Whitespace: Space - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 33 - 42 - 62, // Plus sign at decimal 43 - -9,-9,-9, // Decimal 44 - 46 - 63, // Slash at decimal 47 - 52,53,54,55,56,57,58,59,60,61, // Numbers zero through nine - -9,-9,-9, // Decimal 58 - 60 - -1, // Equals sign at decimal 61 - -9,-9,-9, // Decimal 62 - 64 - 0,1,2,3,4,5,6,7,8,9,10,11,12,13, // Letters 'A' through 'N' - 14,15,16,17,18,19,20,21,22,23,24,25, // Letters 'O' through 'Z' - -9,-9,-9,-9,-9,-9, // Decimal 91 - 96 - 26,27,28,29,30,31,32,33,34,35,36,37,38, // Letters 'a' through 'm' - 39,40,41,42,43,44,45,46,47,48,49,50,51, // Letters 'n' through 'z' - -9,-9,-9,-9 // Decimal 123 - 126 - /*,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 127 - 139 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 140 - 152 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 153 - 165 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 166 - 178 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 179 - 191 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 192 - 204 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 205 - 217 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 218 - 230 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 231 - 243 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9 // Decimal 244 - 255 */ - }; - - // I think I end up not using the BAD_ENCODING indicator. - //private final static byte BAD_ENCODING = -9; // Indicates error in encoding - private final static byte WHITE_SPACE_ENC = -5; // Indicates white space in encoding - private final static byte EQUALS_SIGN_ENC = -1; // Indicates equals sign in encoding - - - /** Defeats instantiation. */ - private Base64() { - // legal empty block - } - - - - /* ******** E N C O D I N G M E T H O D S ******** */ - - - /** - * Encodes up to the first three bytes of array threeBytes - * and returns a four-byte array in Base64 notation. - * The actual number of significant bytes in your array is - * given by numSigBytes. - * The array threeBytes needs only be as big as - * numSigBytes. - * Code can reuse a byte array by passing a four-byte array as b4. - * - * @param b4 A reusable byte array to reduce array instantiation - * @param threeBytes the array to convert - * @param numSigBytes the number of significant bytes in your array - * @return four byte array in Base64 notation. - * @since 1.5.1 - */ - private static byte[] encode3to4( byte[] b4, byte[] threeBytes, int numSigBytes ) - { - encode3to4( threeBytes, 0, numSigBytes, b4, 0 ); - return b4; - } // end encode3to4 - - - /** - * Encodes up to three bytes of the array source - * and writes the resulting four Base64 bytes to destination. - * The source and destination arrays can be manipulated - * anywhere along their length by specifying - * srcOffset and destOffset. - * This method does not check to make sure your arrays - * are large enough to accomodate srcOffset + 3 for - * the source array or destOffset + 4 for - * the destination array. - * The actual number of significant bytes in your array is - * given by numSigBytes. - * - * @param source the array to convert - * @param srcOffset the index where conversion begins - * @param numSigBytes the number of significant bytes in your array - * @param destination the array to hold the conversion - * @param destOffset the index where output will be put - * @return the destination array - * @since 1.3 - */ - private static byte[] encode3to4( - byte[] source, int srcOffset, int numSigBytes, - byte[] destination, int destOffset ) - { - // 1 2 3 - // 01234567890123456789012345678901 Bit position - // --------000000001111111122222222 Array position from threeBytes - // --------| || || || | Six bit groups to index ALPHABET - // >>18 >>12 >> 6 >> 0 Right shift necessary - // 0x3f 0x3f 0x3f Additional AND - - // Create buffer with zero-padding if there are only one or two - // significant bytes passed in the array. - // We have to shift left 24 in order to flush out the 1's that appear - // when Java treats a value as negative that is cast from a byte to an int. - int inBuff = ( numSigBytes > 0 ? ((source[ srcOffset ] << 24) >>> 8) : 0 ) - | ( numSigBytes > 1 ? ((source[ srcOffset + 1 ] << 24) >>> 16) : 0 ) - | ( numSigBytes > 2 ? ((source[ srcOffset + 2 ] << 24) >>> 24) : 0 ); - - switch( numSigBytes ) - { - case 3: - destination[ destOffset ] = ALPHABET[ (inBuff >>> 18) ]; - destination[ destOffset + 1 ] = ALPHABET[ (inBuff >>> 12) & 0x3f ]; - destination[ destOffset + 2 ] = ALPHABET[ (inBuff >>> 6) & 0x3f ]; - destination[ destOffset + 3 ] = ALPHABET[ (inBuff ) & 0x3f ]; - return destination; - - case 2: - destination[ destOffset ] = ALPHABET[ (inBuff >>> 18) ]; - destination[ destOffset + 1 ] = ALPHABET[ (inBuff >>> 12) & 0x3f ]; - destination[ destOffset + 2 ] = ALPHABET[ (inBuff >>> 6) & 0x3f ]; - destination[ destOffset + 3 ] = EQUALS_SIGN; - return destination; - - case 1: - destination[ destOffset ] = ALPHABET[ (inBuff >>> 18) ]; - destination[ destOffset + 1 ] = ALPHABET[ (inBuff >>> 12) & 0x3f ]; - destination[ destOffset + 2 ] = EQUALS_SIGN; - destination[ destOffset + 3 ] = EQUALS_SIGN; - return destination; - - default: - return destination; - } // end switch - } // end encode3to4 - - - - /** - * Serializes an object and returns the Base64-encoded - * version of that serialized object. If the object - * cannot be serialized or there is another error, - * the method will return null. - * The object is not GZip-compressed before being encoded. - * - * @param serializableObject The object to encode - * @return The Base64-encoded object - * @throws IOException - * @since 1.4 - */ - public static String encodeObject( java.io.Serializable serializableObject ) - throws IOException - { - return encodeObject( serializableObject, NO_OPTIONS ); - } // end encodeObject - - - - /** - * Serializes an object and returns the Base64-encoded - * version of that serialized object. If the object - * cannot be serialized or there is another error, - * the method will return null. - *

- * Valid options:

-         *   GZIP: gzip-compresses object before encoding it.
-         *   DONT_BREAK_LINES: don't break lines at 76 characters
-         *     Note: Technically, this makes your encoding non-compliant.
-         * 
- *

- * Example: encodeObject( myObj, Base64.GZIP ) or - *

- * Example: encodeObject( myObj, Base64.GZIP | Base64.DONT_BREAK_LINES ) - * - * @param serializableObject The object to encode - * @param options Specified options - * @return The Base64-encoded object - * @throws IOException - * @see Base64#GZIP - * @see Base64#DONT_BREAK_LINES - * @since 2.0 - */ - public static String encodeObject( java.io.Serializable serializableObject, int options ) - throws IOException - { - // Streams - java.io.ByteArrayOutputStream baos = null; - java.io.OutputStream b64os = null; - java.io.ObjectOutputStream oos = null; - java.util.zip.GZIPOutputStream gzos = null; - - // Isolate options - int gzip = (options & GZIP); - int dontBreakLines = (options & DONT_BREAK_LINES); - - try - { - // ObjectOutputStream -> (GZIP) -> Base64 -> ByteArrayOutputStream - baos = new java.io.ByteArrayOutputStream(); - b64os = new Base64.OutputStream( baos, ENCODE | dontBreakLines ); - - // GZip? - if( gzip == GZIP ) - { - gzos = new java.util.zip.GZIPOutputStream( b64os ); - oos = new java.io.ObjectOutputStream( gzos ); - } // end if: gzip - else - oos = new java.io.ObjectOutputStream( b64os ); - - oos.writeObject( serializableObject ); - } // end try - finally - { - try{ oos.close(); } catch( Exception e ){ onCloseError(e); } - try{ gzos.close(); } catch( Exception e ){ onCloseError(e); } - try{ b64os.close(); } catch( Exception e ){ onCloseError(e); } - try{ baos.close(); } catch( Exception e ){ onCloseError(e); } - } // end finally - - // Return value according to relevant encoding. - try - { - return new String( baos.toByteArray(), PREFERRED_ENCODING ); - } // end try - catch (java.io.UnsupportedEncodingException uue) - { - return new String( baos.toByteArray() ); - } // end catch - - } // end encode - - - - /** - * Encodes a byte array into Base64 notation. - * Does not GZip-compress data. - * - * @param source The data to convert - * @throws IOException - * @since 1.4 - */ - public static String encodeBytes( byte[] source ) throws IOException - { - return encodeBytes( source, 0, source.length, NO_OPTIONS ); - } // end encodeBytes - - - - /** - * Encodes a byte array into Base64 notation. - *

- * Valid options:

-         *   GZIP: gzip-compresses object before encoding it.
-         *   DONT_BREAK_LINES: don't break lines at 76 characters
-         *     Note: Technically, this makes your encoding non-compliant.
-         * 
- *

- * Example: encodeBytes( myData, Base64.GZIP ) or - *

- * Example: encodeBytes( myData, Base64.GZIP | Base64.DONT_BREAK_LINES ) - * - * - * @param source The data to convert - * @param options Specified options - * @throws IOException - * @see Base64#GZIP - * @see Base64#DONT_BREAK_LINES - * @since 2.0 - */ - public static String encodeBytes( byte[] source, int options ) - throws IOException - { - return encodeBytes( source, 0, source.length, options ); - } // end encodeBytes - - - /** - * Encodes a byte array into Base64 notation. - * Does not GZip-compress data. - * - * @param source The data to convert - * @param off Offset in array where conversion should begin - * @param len Length of data to convert - * @throws IOException - * @since 1.4 - */ - public static String encodeBytes( byte[] source, int off, int len ) - throws IOException - { - return encodeBytes( source, off, len, NO_OPTIONS ); - } // end encodeBytes - - - - /** - * Encodes a byte array into Base64 notation. - *

- * Valid options:

-         *   GZIP: gzip-compresses object before encoding it.
-         *   DONT_BREAK_LINES: don't break lines at 76 characters
-         *     Note: Technically, this makes your encoding non-compliant.
-         * 
- *

- * Example: encodeBytes( myData, Base64.GZIP ) or - *

- * Example: encodeBytes( myData, Base64.GZIP | Base64.DONT_BREAK_LINES ) - * - * - * @param source The data to convert - * @param off Offset in array where conversion should begin - * @param len Length of data to convert - * @param options Specified options - * @throws IOException - * @see Base64#GZIP - * @see Base64#DONT_BREAK_LINES - * @since 2.0 - */ - public static String encodeBytes( byte[] source, int off, int len, int options ) - throws IOException - { - // Isolate options - int dontBreakLines = ( options & DONT_BREAK_LINES ); - int gzip = ( options & GZIP ); - - // Compress? - if( gzip == GZIP ) - { - java.io.ByteArrayOutputStream baos = null; - java.util.zip.GZIPOutputStream gzos = null; - Base64.OutputStream b64os = null; - - - try - { - // GZip -> Base64 -> ByteArray - baos = new java.io.ByteArrayOutputStream(); - b64os = new Base64.OutputStream( baos, ENCODE | dontBreakLines ); - gzos = new java.util.zip.GZIPOutputStream( b64os ); - - gzos.write( source, off, len ); - gzos.close(); - } // end try - finally - { - try{ gzos.close(); } catch( Exception e ){ onCloseError(e); } - try{ b64os.close(); } catch( Exception e ){ onCloseError(e); } - try{ baos.close(); } catch( Exception e ){ onCloseError(e); } - } // end finally - - // Return value according to relevant encoding. - try - { - return new String( baos.toByteArray(), PREFERRED_ENCODING ); - } // end try - catch (java.io.UnsupportedEncodingException uue) - { - return new String( baos.toByteArray() ); - } // end catch - } // end if: compress - - // Else, don't compress. Better not to use streams at all then. - else - { - // Convert option to boolean in way that code likes it. - boolean breakLines = dontBreakLines == 0; - - int len43 = len * 4 / 3; - byte[] outBuff = new byte[ ( len43 ) // Main 4:3 - + ( (len % 3) > 0 ? 4 : 0 ) // Account for padding - + (breakLines ? ( len43 / MAX_LINE_LENGTH ) : 0) ]; // New lines - int d = 0; - int e = 0; - int len2 = len - 2; - int lineLength = 0; - for( ; d < len2; d+=3, e+=4 ) - { - encode3to4( source, d+off, 3, outBuff, e ); - - lineLength += 4; - if( breakLines && lineLength == MAX_LINE_LENGTH ) - { - outBuff[e+4] = NEW_LINE; - e++; - lineLength = 0; - } // end if: end of line - } // en dfor: each piece of array - - if( d < len ) - { - encode3to4( source, d+off, len - d, outBuff, e ); - e += 4; - } // end if: some padding needed - - - // Return value according to relevant encoding. - try - { - return new String( outBuff, 0, e, PREFERRED_ENCODING ); - } // end try - catch (java.io.UnsupportedEncodingException uue) - { - return new String( outBuff, 0, e ); - } // end catch - - } // end else: don't compress - - } // end encodeBytes - - - - - - /* ******** D E C O D I N G M E T H O D S ******** */ - - - /** - * Decodes four bytes from array source - * and writes the resulting bytes (up to three of them) - * to destination. - * The source and destination arrays can be manipulated - * anywhere along their length by specifying - * srcOffset and destOffset. - * This method does not check to make sure your arrays - * are large enough to accomodate srcOffset + 4 for - * the source array or destOffset + 3 for - * the destination array. - * This method returns the actual number of bytes that - * were converted from the Base64 encoding. - * - * - * @param source the array to convert - * @param srcOffset the index where conversion begins - * @param destination the array to hold the conversion - * @param destOffset the index where output will be put - * @return the number of decoded bytes converted - * @since 1.3 - */ - private static int decode4to3( byte[] source, int srcOffset, byte[] destination, int destOffset ) - { - // Example: Dk== - if( source[ srcOffset + 2] == EQUALS_SIGN ) - { - // Two ways to do the same thing. Don't know which way I like best. - //int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6 ) - // | ( ( DECODABET[ source[ srcOffset + 1] ] << 24 ) >>> 12 ); - int outBuff = ( ( DECODABET[ source[ srcOffset ] ] & 0xFF ) << 18 ) - | ( ( DECODABET[ source[ srcOffset + 1] ] & 0xFF ) << 12 ); - - destination[ destOffset ] = (byte)( outBuff >>> 16 ); - return 1; - } - - // Example: DkL= - else if( source[ srcOffset + 3 ] == EQUALS_SIGN ) - { - // Two ways to do the same thing. Don't know which way I like best. - //int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6 ) - // | ( ( DECODABET[ source[ srcOffset + 1 ] ] << 24 ) >>> 12 ) - // | ( ( DECODABET[ source[ srcOffset + 2 ] ] << 24 ) >>> 18 ); - int outBuff = ( ( DECODABET[ source[ srcOffset ] ] & 0xFF ) << 18 ) - | ( ( DECODABET[ source[ srcOffset + 1 ] ] & 0xFF ) << 12 ) - | ( ( DECODABET[ source[ srcOffset + 2 ] ] & 0xFF ) << 6 ); - - destination[ destOffset ] = (byte)( outBuff >>> 16 ); - destination[ destOffset + 1 ] = (byte)( outBuff >>> 8 ); - return 2; - } - - // Example: DkLE - else - { - // Two ways to do the same thing. Don't know which way I like best. - //int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6 ) - // | ( ( DECODABET[ source[ srcOffset + 1 ] ] << 24 ) >>> 12 ) - // | ( ( DECODABET[ source[ srcOffset + 2 ] ] << 24 ) >>> 18 ) - // | ( ( DECODABET[ source[ srcOffset + 3 ] ] << 24 ) >>> 24 ); - int outBuff = ( ( DECODABET[ source[ srcOffset ] ] & 0xFF ) << 18 ) - | ( ( DECODABET[ source[ srcOffset + 1 ] ] & 0xFF ) << 12 ) - | ( ( DECODABET[ source[ srcOffset + 2 ] ] & 0xFF ) << 6) - | ( ( DECODABET[ source[ srcOffset + 3 ] ] & 0xFF ) ); - - - destination[ destOffset ] = (byte)( outBuff >> 16 ); - destination[ destOffset + 1 ] = (byte)( outBuff >> 8 ); - destination[ destOffset + 2 ] = (byte)( outBuff ); - - return 3; - } - } // end decodeToBytes - - - - - /** - * Very low-level access to decoding ASCII characters in - * the form of a byte array. Does not support automatically - * gunzipping or any other "fancy" features. - * - * @param source The Base64 encoded data - * @param off The offset of where to begin decoding - * @param len The length of characters to decode - * @return decoded data - * @throws IOException - * @since 1.3 - */ - public static byte[] decode( byte[] source, int off, int len ) - throws IOException - { - int len34 = len * 3 / 4; - byte[] outBuff = new byte[ len34 ]; // Upper limit on size of output - int outBuffPosn = 0; - - byte[] b4 = new byte[4]; - int b4Posn = 0; - int i = 0; - byte sbiCrop = 0; - byte sbiDecode = 0; - for( i = off; i < off+len; i++ ) - { - sbiCrop = (byte)(source[i] & 0x7f); // Only the low seven bits - sbiDecode = DECODABET[ sbiCrop ]; - - if( sbiDecode >= WHITE_SPACE_ENC ) // White space, Equals sign or better - { - if( sbiDecode >= EQUALS_SIGN_ENC ) - { - b4[ b4Posn++ ] = sbiCrop; - if( b4Posn > 3 ) - { - outBuffPosn += decode4to3( b4, 0, outBuff, outBuffPosn ); - b4Posn = 0; - - // If that was the equals sign, break out of 'for' loop - if( sbiCrop == EQUALS_SIGN ) - break; - } // end if: quartet built - - } // end if: equals sign or better - - } // end if: white space, equals sign or better - else - { - throw new IOException("Bad Base64 input character at " + i + ": " + source[i] + "(decimal)" ); - } // end else: - } // each input character - - byte[] out = new byte[ outBuffPosn ]; - System.arraycopy( outBuff, 0, out, 0, outBuffPosn ); - return out; - } // end decode - - - - - /** - * Decodes data from Base64 notation, automatically - * detecting gzip-compressed data and decompressing it. - * - * @param s the string to decode - * @return the decoded data - * @throws IOException - * @since 1.4 - */ - public static byte[] decode( String s ) throws IOException - { - byte[] bytes; - try - { - bytes = s.getBytes( PREFERRED_ENCODING ); - } // end try - catch( java.io.UnsupportedEncodingException uee ) - { - bytes = s.getBytes(); - } // end catch - // - - // Decode - bytes = decode( bytes, 0, bytes.length ); - - - // Check to see if it's gzip-compressed - // GZIP Magic Two-Byte Number: 0x8b1f (35615) - if( bytes != null && bytes.length >= 4 ) - { - - int head = ((int)bytes[0] & 0xff) | ((bytes[1] << 8) & 0xff00); - if( java.util.zip.GZIPInputStream.GZIP_MAGIC == head ) - { - java.io.ByteArrayInputStream bais = null; - java.util.zip.GZIPInputStream gzis = null; - java.io.ByteArrayOutputStream baos = null; - byte[] buffer = new byte[2048]; - int length = 0; - - try - { - baos = new java.io.ByteArrayOutputStream(); - bais = new java.io.ByteArrayInputStream( bytes ); - gzis = new java.util.zip.GZIPInputStream( bais ); - - while( ( length = gzis.read( buffer ) ) >= 0 ) - { - baos.write(buffer,0,length); - } // end while: reading input - - // No error? Get new bytes. - bytes = baos.toByteArray(); - - } // end try - catch( java.io.IOException e ) - { - // VEDERE MEGLIO - // Just return originally-decoded bytes - } // end catch - finally - { - try{ baos.close(); } catch( Exception e ){ onCloseError(e); } - try{ gzis.close(); } catch( Exception e ){ onCloseError(e); } - try{ bais.close(); } catch( Exception e ){ onCloseError(e); } - } // end finally - - } // end if: gzipped - } // end if: bytes.length >= 2 - - return bytes; - } // end decode - - - - - /** - * Attempts to decode Base64 data and deserialize a Java - * Object within. Returns null if there was an error. - * - * @param encodedObject The Base64 data to decode - * @return The decoded and deserialized object - * @throws IOException - * @throws ClassNotFoundException - * @since 1.5 - */ - public static Object decodeToObject( String encodedObject ) - throws IOException, ClassNotFoundException - { - // Decode and gunzip if necessary - byte[] objBytes = decode( encodedObject ); - - java.io.ByteArrayInputStream bais = null; - java.io.ObjectInputStream ois = null; - Object obj = null; - - try - { - bais = new java.io.ByteArrayInputStream( objBytes ); - ois = new java.io.ObjectInputStream( bais ); - - obj = ois.readObject(); - } // end try - finally - { - try{ bais.close(); } catch( Exception e ){ onCloseError(e); } - try{ ois.close(); } catch( Exception e ){ onCloseError(e); } - } // end finally - - return obj; - } // end decodeObject - - - - /** - * Convenience method for encoding data to a file. - * - * @param dataToEncode byte array of data to encode in base64 form - * @param filename Filename for saving encoded data - * @return true if successful, false otherwise - * @throws IOException - * - * @since 2.1 - */ - public static boolean encodeToFile( byte[] dataToEncode, String filename ) - throws IOException - { - boolean success = false; - Base64.OutputStream bos = null; - try - { - bos = new Base64.OutputStream( - new java.io.FileOutputStream( filename ), Base64.ENCODE ); - bos.write( dataToEncode ); - success = true; - } // end try - finally - { - try{ bos.close(); } catch( Exception e ){ onCloseError(e); } - } // end finally - - return success; - } // end encodeToFile - - - /** - * Convenience method for decoding data to a file. - * - * @param dataToDecode Base64-encoded data as a string - * @param filename Filename for saving decoded data - * @return true if successful, false otherwise - * @throws IOException - * - * @since 2.1 - */ - public static boolean decodeToFile( String dataToDecode, String filename ) - throws IOException - { - boolean success = false; - Base64.OutputStream bos = null; - try - { - bos = new Base64.OutputStream( - new java.io.FileOutputStream( filename ), Base64.DECODE ); - bos.write( dataToDecode.getBytes( PREFERRED_ENCODING ) ); - success = true; - } // end try - finally - { - try{ bos.close(); } catch( Exception e ){ onCloseError(e); } - } // end finally - - return success; - } // end decodeToFile - - - - - /** - * Convenience method for reading a base64-encoded - * file and decoding it. - * - * @param filename Filename for reading encoded data - * @return decoded byte array or null if unsuccessful - * @throws IOException - * - * @since 2.1 - */ - public static byte[] decodeFromFile( String filename ) - throws IOException - { - byte[] decodedData = null; - Base64.InputStream bis = null; - try - { - // Set up some useful variables - java.io.File file = new java.io.File( filename ); - byte[] buffer = null; - int length = 0; - int numBytes = 0; - - // Check for size of file - if( file.length() > Integer.MAX_VALUE ) - { - throw new IOException("File is too big for this convenience method (" + file.length() + " bytes)." ); - } // end if: file too big for int index - buffer = new byte[ (int)file.length() ]; - - // Open a stream - bis = new Base64.InputStream( - new java.io.BufferedInputStream( - new java.io.FileInputStream( file ) ), Base64.DECODE ); - - // Read until done - while( ( numBytes = bis.read( buffer, length, 4096 ) ) >= 0 ) - length += numBytes; - - // Save in a variable to return - decodedData = new byte[ length ]; - System.arraycopy( buffer, 0, decodedData, 0, length ); - - } // end try - finally - { - try{ bis.close(); } catch( Exception e) { onCloseError(e); } - } // end finally - - return decodedData; - } // end decodeFromFile - - - - /** - * Convenience method for reading a binary file - * and base64-encoding it. - * - * @param filename Filename for reading binary data - * @return base64-encoded string or null if unsuccessful - * @throws IOException - * - * @since 2.1 - */ - public static String encodeFromFile( String filename ) - throws IOException - { - String encodedData = null; - Base64.InputStream bis = null; - try - { - // Set up some useful variables - java.io.File file = new java.io.File( filename ); - byte[] buffer = new byte[ (int)(file.length() * 1.4) ]; - int length = 0; - int numBytes = 0; - - // Open a stream - bis = new Base64.InputStream( - new java.io.BufferedInputStream( - new java.io.FileInputStream( file ) ), Base64.ENCODE ); - - // Read until done - while( ( numBytes = bis.read( buffer, length, 4096 ) ) >= 0 ) - length += numBytes; - - // Save in a variable to return - encodedData = new String( buffer, 0, length, Base64.PREFERRED_ENCODING ); - - } // end try - finally - { - try{ bis.close(); } catch( Exception e) { onCloseError(e); } - } // end finally - - return encodedData; - } // end encodeFromFile - - - - - /* ******** I N N E R C L A S S I N P U T S T R E A M ******** */ - - - - /** - * A {@link Base64.InputStream} will read data from another - * java.io.InputStream, given in the constructor, - * and encode/decode to/from Base64 notation on the fly. - * - * @see Base64 - * @since 1.3 - */ - public static class InputStream extends java.io.FilterInputStream - { - private boolean encode; // Encoding or decoding - private int position; // Current position in the buffer - private byte[] buffer; // Small buffer holding converted data - private int bufferLength; // Length of buffer (3 or 4) - private int numSigBytes; // Number of meaningful bytes in the buffer - private int lineLength; - private boolean breakLines; // Break lines at less than 80 characters - - - /** - * Constructs a {@link Base64.InputStream} in DECODE mode. - * - * @param in the java.io.InputStream from which to read data. - * @since 1.3 - */ - public InputStream( java.io.InputStream in ) - { - this( in, DECODE ); - } // end constructor - - - /** - * Constructs a {@link Base64.InputStream} in - * either ENCODE or DECODE mode. - *

- * Valid options:

-             *   ENCODE or DECODE: Encode or Decode as data is read.
-             *   DONT_BREAK_LINES: don't break lines at 76 characters
-             *     (only meaningful when encoding)
-             *     Note: Technically, this makes your encoding non-compliant.
-             * 
- *

- * Example: new Base64.InputStream( in, Base64.DECODE ) - * - * - * @param in the java.io.InputStream from which to read data. - * @param options Specified options - * @see Base64#ENCODE - * @see Base64#DECODE - * @see Base64#DONT_BREAK_LINES - * @since 2.0 - */ - public InputStream( java.io.InputStream in, int options ) - { - super( in ); - this.breakLines = (options & DONT_BREAK_LINES) != DONT_BREAK_LINES; - this.encode = (options & ENCODE) == ENCODE; - this.bufferLength = encode ? 4 : 3; - this.buffer = new byte[ bufferLength ]; - this.position = -1; - this.lineLength = 0; - } // end constructor - - /** - * Reads enough of the input stream to convert - * to/from Base64 and returns the next byte. - * - * @return next byte - * @since 1.3 - */ - @Override - public int read() throws java.io.IOException - { - // Do we need to get data? - if( position < 0 ) - { - if( encode ) - { - byte[] b3 = new byte[3]; - int numBinaryBytes = 0; - for( int i = 0; i < 3; i++ ) - { - try - { - int b = in.read(); - - // If end of stream, b is -1. - if( b >= 0 ) - { - b3[i] = (byte)b; - numBinaryBytes++; - } // end if: not end of stream - - } // end try: read - catch( java.io.IOException e ) - { - // Only a problem if we got no data at all. - if( i == 0 ) - throw e; - - } // end catch - } // end for: each needed input byte - - if( numBinaryBytes > 0 ) - { - encode3to4( b3, 0, numBinaryBytes, buffer, 0 ); - position = 0; - numSigBytes = 4; - } // end if: got data - else - { - return -1; - } // end else - } // end if: encoding - - // Else decoding - else - { - byte[] b4 = new byte[4]; - int i = 0; - for( i = 0; i < 4; i++ ) - { - // Read four "meaningful" bytes: - int b = 0; - do{ b = in.read(); } - while( b >= 0 && DECODABET[ b & 0x7f ] <= WHITE_SPACE_ENC ); - - if( b < 0 ) - break; // Reads a -1 if end of stream - - b4[i] = (byte)b; - } // end for: each needed input byte - - if( i == 4 ) - { - numSigBytes = decode4to3( b4, 0, buffer, 0 ); - position = 0; - } // end if: got four characters - else if( i == 0 ){ - return -1; - } // end else if: also padded correctly - else - { - // Must have broken out from above. - throw new java.io.IOException( "Improperly padded Base64 input." ); - } // end - - } // end else: decode - } // end else: get data - - // Got data? - if( position >= 0 ) - { - // End of relevant data? - if( /*!encode &&*/ position >= numSigBytes ) - return -1; - - if( encode && breakLines && lineLength >= MAX_LINE_LENGTH ) - { - lineLength = 0; - return '\n'; - } // end if - else - { - lineLength++; // This isn't important when decoding - // but throwing an extra "if" seems - // just as wasteful. - - int b = buffer[ position++ ]; - - if( position >= bufferLength ) - position = -1; - - return b & 0xFF; // This is how you "cast" a byte that's - // intended to be unsigned. - } // end else - } // end if: position >= 0 - - // Else error - else - { - throw new java.io.IOException( "Error in Base64 code reading stream." ); - } // end else - } // end read - - - /** - * Calls {@link #read()} repeatedly until the end of stream - * is reached or len bytes are read. - * Returns number of bytes read into array or -1 if - * end of stream is encountered. - * - * @param dest array to hold values - * @param off offset for array - * @param len max number of bytes to read into array - * @return bytes read into array or -1 if end of stream is encountered. - * @since 1.3 - */ - @Override - public int read( byte[] dest, int off, int len ) throws java.io.IOException - { - int i; - int b; - for( i = 0; i < len; i++ ) - { - b = read(); - - //if( b < 0 && i == 0 ) - // return -1; - - if( b >= 0 ) - dest[off + i] = (byte)b; - else if( i == 0 ) - return -1; - else - break; // Out of 'for' loop - } // end for: each byte read - return i; - } // end read - - } // end inner class InputStream - - - - - - - /* ******** I N N E R C L A S S O U T P U T S T R E A M ******** */ - - - - /** - * A {@link Base64.OutputStream} will write data to another - * java.io.OutputStream, given in the constructor, - * and encode/decode to/from Base64 notation on the fly. - * - * @see Base64 - * @since 1.3 - */ - public static class OutputStream extends java.io.FilterOutputStream - { - private boolean encode; - private int position; - private byte[] buffer; - private int bufferLength; - private int lineLength; - private boolean breakLines; - private byte[] b4; // Scratch used in a few places - private boolean suspendEncoding; - - /** - * Constructs a {@link Base64.OutputStream} in ENCODE mode. - * - * @param out the java.io.OutputStream to which data will be written. - * @since 1.3 - */ - public OutputStream( java.io.OutputStream out ) - { - this( out, ENCODE ); - } // end constructor - - - /** - * Constructs a {@link Base64.OutputStream} in - * either ENCODE or DECODE mode. - *

- * Valid options:

-             *   ENCODE or DECODE: Encode or Decode as data is read.
-             *   DONT_BREAK_LINES: don't break lines at 76 characters
-             *     (only meaningful when encoding)
-             *     Note: Technically, this makes your encoding non-compliant.
-             * 
- *

- * Example: new Base64.OutputStream( out, Base64.ENCODE ) - * - * @param out the java.io.OutputStream to which data will be written. - * @param options Specified options. - * @see Base64#ENCODE - * @see Base64#DECODE - * @see Base64#DONT_BREAK_LINES - * @since 1.3 - */ - public OutputStream( java.io.OutputStream out, int options ) - { - super( out ); - this.breakLines = (options & DONT_BREAK_LINES) != DONT_BREAK_LINES; - this.encode = (options & ENCODE) == ENCODE; - this.bufferLength = encode ? 3 : 4; - this.buffer = new byte[ bufferLength ]; - this.position = 0; - this.lineLength = 0; - this.suspendEncoding = false; - this.b4 = new byte[4]; - } // end constructor - - - /** - * Writes the byte to the output stream after - * converting to/from Base64 notation. - * When encoding, bytes are buffered three - * at a time before the output stream actually - * gets a write() call. - * When decoding, bytes are buffered four - * at a time. - * - * @param theByte the byte to write - * @since 1.3 - */ - @Override - public void write(int theByte) throws java.io.IOException - { - // Encoding suspended? - if( suspendEncoding ) - { - super.out.write( theByte ); - return; - } // end if: supsended - - // Encode? - if( encode ) - { - buffer[ position++ ] = (byte)theByte; - if( position >= bufferLength ) // Enough to encode. - { - out.write( encode3to4( b4, buffer, bufferLength ) ); - - lineLength += 4; - if( breakLines && lineLength >= MAX_LINE_LENGTH ) - { - out.write( NEW_LINE ); - lineLength = 0; - } // end if: end of line - - position = 0; - } // end if: enough to output - } // end if: encoding - - // Else, Decoding - else - { - // Meaningful Base64 character? - if( DECODABET[ theByte & 0x7f ] > WHITE_SPACE_ENC ) - { - buffer[ position++ ] = (byte)theByte; - if( position >= bufferLength ) // Enough to output. - { - int len = Base64.decode4to3( buffer, 0, b4, 0 ); - out.write( b4, 0, len ); - //out.write( Base64.decode4to3( buffer ) ); - position = 0; - } // end if: enough to output - } // end if: meaningful base64 character - else if( DECODABET[ theByte & 0x7f ] != WHITE_SPACE_ENC ) - { - throw new java.io.IOException( "Invalid character in Base64 data." ); - } // end else: not white space either - } // end else: decoding - } // end write - - - - /** - * Calls {@link #write(int)} repeatedly until len - * bytes are written. - * - * @param theBytes array from which to read bytes - * @param off offset for array - * @param len max number of bytes to read into array - * @since 1.3 - */ - @Override - public void write( byte[] theBytes, int off, int len ) throws java.io.IOException - { - // Encoding suspended? - if( suspendEncoding ) - { - super.out.write( theBytes, off, len ); - return; - } // end if: supsended - - for( int i = 0; i < len; i++ ) - { - write( theBytes[ off + i ] ); - } // end for: each byte written - - } // end write - - - - /** - * Method added by PHIL. [Thanks, PHIL. -Rob] - * This pads the buffer without closing the stream. - */ - public void flushBase64() throws java.io.IOException - { - if( position > 0 ) - { - if( encode ) - { - out.write( encode3to4( b4, buffer, position ) ); - position = 0; - } // end if: encoding - else - { - throw new java.io.IOException( "Base64 input not properly padded." ); - } // end else: decoding - } // end if: buffer partially full - - } // end flush - - - /** - * Flushes and closes (I think, in the superclass) the stream. - * - * @since 1.3 - */ - @Override - public void close() throws java.io.IOException - { - // 1. Ensure that pending characters are written - flushBase64(); - - // 2. Actually close the stream - // Base class both flushes and closes. - super.close(); - - buffer = null; - out = null; - } // end close - - - - /** - * Suspends encoding of the stream. - * May be helpful if you need to embed a piece of - * base640-encoded data in a stream. - * - * @since 1.5.1 - */ - public void suspendEncoding() throws java.io.IOException - { - flushBase64(); - this.suspendEncoding = true; - } // end suspendEncoding - - - /** - * Resumes encoding of the stream. - * May be helpful if you need to embed a piece of - * base640-encoded data in a stream. - * - * @since 1.5.1 - */ - public void resumeEncoding() - { - this.suspendEncoding = false; - } // end resumeEncoding - - - - } // end inner class OutputStream - - - } // end class Base64 - -} +/* + Copyright (c) Lightstreamer Srl + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ +package com.lightstreamer.adapters.LeapMotionDemo.engine3D; + +import java.io.IOException; + +public class Base64Manager { + public String encodeBytes(byte [] bytes, boolean dontBreakLines) throws IOException { + return Base64.encodeBytes(bytes, dontBreakLines ? Base64.DONT_BREAK_LINES : Base64.NO_OPTIONS); + } + + public byte [] decodeBuffer(String string) throws IOException { + return Base64.decode(string); + } + + private static void onCloseError(Exception e) { + System.out.println("Unexpected close error: " + e); + } + + /////////////////////////////////////////////////////////////////////////// + // Classe di supporto per l'encoding-decoding in base64 + // Fonte: http://iharder.sourceforge.net/base64/ + // + // MODIFICATO PER QUANTO RIGUARDA LA GESTIONE DEGLI ERRORI + // + // ATTENZIONE! NON SONO DISPONIBILI IMPLEMENTAZIONI NEL JDK, + // MA SONO DISPONIBILI NELLE LIBRERIE Apache Commons + + /** + * Encodes and decodes to and from Base64 notation. + * + *

+ * Change Log: + *

+ *
    + *
  • v2.1 - Cleaned up javadoc comments and unused variables and methods. Added + * some convenience methods for reading and writing to and from files.
  • + *
  • v2.0.2 - Now specifies UTF-8 encoding in places where the code fails on systems + * with other encodings (like EBCDIC).
  • + *
  • v2.0.1 - Fixed an error when decoding a single byte, that is, when the + * encoded data was a single byte.
  • + *
  • v2.0 - I got rid of methods that used booleans to set options. + * Now everything is more consolidated and cleaner. The code now detects + * when data that's being decoded is gzip-compressed and will decompress it + * automatically. Generally things are cleaner. You'll probably have to + * change some method calls that you were making to support the new + * options format (ints that you "OR" together).
  • + *
  • v1.5.1 - Fixed bug when decompressing and decoding to a + * byte[] using decode( String s, boolean gzipCompressed ). + * Added the ability to "suspend" encoding in the Output Stream so + * you can turn on and off the encoding if you need to embed base64 + * data in an otherwise "normal" stream (like an XML file).
  • + *
  • v1.5 - Output stream pases on flush() command but doesn't do anything itself. + * This helps when using GZIP streams. + * Added the ability to GZip-compress objects before encoding them.
  • + *
  • v1.4 - Added helper methods to read/write files.
  • + *
  • v1.3.6 - Fixed OutputStream.flush() so that 'position' is reset.
  • + *
  • v1.3.5 - Added flag to turn on and off line breaks. Fixed bug in input stream + * where last buffer being read, if not completely full, was not returned.
  • + *
  • v1.3.4 - Fixed when "improperly padded stream" error was thrown at the wrong time.
  • + *
  • v1.3.3 - Fixed I/O streams which were totally messed up.
  • + *
+ * + *

+ * I am placing this code in the Public Domain. Do with it as you will. + * This software comes with no guarantees or warranties but with + * plenty of well-wishing instead! + * Please visit http://iharder.net/base64 + * periodically to check for updates or to contribute improvements. + *

+ * + * @author Robert Harder + * @author rob@iharder.net + * @version 2.1 + */ + protected static class Base64 + { + + /* ******** P U B L I C F I E L D S ******** */ + + + /** No options specified. Value is zero. */ + public final static int NO_OPTIONS = 0; + + /** Specify encoding. */ + public final static int ENCODE = 1; + + + /** Specify decoding. */ + public final static int DECODE = 0; + + + /** Specify that data should be gzip-compressed. */ + public final static int GZIP = 2; + + + /** Don't break lines when encoding (violates strict Base64 specification) */ + public final static int DONT_BREAK_LINES = 8; + + + /* ******** P R I V A T E F I E L D S ******** */ + + + /** Maximum line length (76) of Base64 output. */ + private final static int MAX_LINE_LENGTH = 76; + + + /** The equals sign (=) as a byte. */ + private final static byte EQUALS_SIGN = (byte)'='; + + + /** The new line character (\n) as a byte. */ + private final static byte NEW_LINE = (byte)'\n'; + + + /** Preferred encoding. */ + private final static String PREFERRED_ENCODING = "UTF-8"; + + + /** The 64 valid Base64 values. */ + private final static byte[] ALPHABET; + private final static byte[] _NATIVE_ALPHABET = /* May be something funny like EBCDIC */ + { + (byte)'A', (byte)'B', (byte)'C', (byte)'D', (byte)'E', (byte)'F', (byte)'G', + (byte)'H', (byte)'I', (byte)'J', (byte)'K', (byte)'L', (byte)'M', (byte)'N', + (byte)'O', (byte)'P', (byte)'Q', (byte)'R', (byte)'S', (byte)'T', (byte)'U', + (byte)'V', (byte)'W', (byte)'X', (byte)'Y', (byte)'Z', + (byte)'a', (byte)'b', (byte)'c', (byte)'d', (byte)'e', (byte)'f', (byte)'g', + (byte)'h', (byte)'i', (byte)'j', (byte)'k', (byte)'l', (byte)'m', (byte)'n', + (byte)'o', (byte)'p', (byte)'q', (byte)'r', (byte)'s', (byte)'t', (byte)'u', + (byte)'v', (byte)'w', (byte)'x', (byte)'y', (byte)'z', + (byte)'0', (byte)'1', (byte)'2', (byte)'3', (byte)'4', (byte)'5', + (byte)'6', (byte)'7', (byte)'8', (byte)'9', (byte)'+', (byte)'/' + }; + + /** Determine which ALPHABET to use. */ + static + { + byte[] __bytes; + try + { + __bytes = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".getBytes( PREFERRED_ENCODING ); + } // end try + catch (java.io.UnsupportedEncodingException use) + { + __bytes = _NATIVE_ALPHABET; // Fall back to native encoding + } // end catch + ALPHABET = __bytes; + } // end static + + + /** + * Translates a Base64 value to either its 6-bit reconstruction value + * or a negative number indicating some other meaning. + **/ + private final static byte[] DECODABET = + { + -9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 0 - 8 + -5,-5, // Whitespace: Tab and Linefeed + -9,-9, // Decimal 11 - 12 + -5, // Whitespace: Carriage Return + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 14 - 26 + -9,-9,-9,-9,-9, // Decimal 27 - 31 + -5, // Whitespace: Space + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 33 - 42 + 62, // Plus sign at decimal 43 + -9,-9,-9, // Decimal 44 - 46 + 63, // Slash at decimal 47 + 52,53,54,55,56,57,58,59,60,61, // Numbers zero through nine + -9,-9,-9, // Decimal 58 - 60 + -1, // Equals sign at decimal 61 + -9,-9,-9, // Decimal 62 - 64 + 0,1,2,3,4,5,6,7,8,9,10,11,12,13, // Letters 'A' through 'N' + 14,15,16,17,18,19,20,21,22,23,24,25, // Letters 'O' through 'Z' + -9,-9,-9,-9,-9,-9, // Decimal 91 - 96 + 26,27,28,29,30,31,32,33,34,35,36,37,38, // Letters 'a' through 'm' + 39,40,41,42,43,44,45,46,47,48,49,50,51, // Letters 'n' through 'z' + -9,-9,-9,-9 // Decimal 123 - 126 + /*,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 127 - 139 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 140 - 152 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 153 - 165 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 166 - 178 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 179 - 191 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 192 - 204 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 205 - 217 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 218 - 230 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 231 - 243 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9 // Decimal 244 - 255 */ + }; + + // I think I end up not using the BAD_ENCODING indicator. + //private final static byte BAD_ENCODING = -9; // Indicates error in encoding + private final static byte WHITE_SPACE_ENC = -5; // Indicates white space in encoding + private final static byte EQUALS_SIGN_ENC = -1; // Indicates equals sign in encoding + + + /** Defeats instantiation. */ + private Base64() { + // legal empty block + } + + + + /* ******** E N C O D I N G M E T H O D S ******** */ + + + /** + * Encodes up to the first three bytes of array threeBytes + * and returns a four-byte array in Base64 notation. + * The actual number of significant bytes in your array is + * given by numSigBytes. + * The array threeBytes needs only be as big as + * numSigBytes. + * Code can reuse a byte array by passing a four-byte array as b4. + * + * @param b4 A reusable byte array to reduce array instantiation + * @param threeBytes the array to convert + * @param numSigBytes the number of significant bytes in your array + * @return four byte array in Base64 notation. + * @since 1.5.1 + */ + private static byte[] encode3to4( byte[] b4, byte[] threeBytes, int numSigBytes ) + { + encode3to4( threeBytes, 0, numSigBytes, b4, 0 ); + return b4; + } // end encode3to4 + + + /** + * Encodes up to three bytes of the array source + * and writes the resulting four Base64 bytes to destination. + * The source and destination arrays can be manipulated + * anywhere along their length by specifying + * srcOffset and destOffset. + * This method does not check to make sure your arrays + * are large enough to accomodate srcOffset + 3 for + * the source array or destOffset + 4 for + * the destination array. + * The actual number of significant bytes in your array is + * given by numSigBytes. + * + * @param source the array to convert + * @param srcOffset the index where conversion begins + * @param numSigBytes the number of significant bytes in your array + * @param destination the array to hold the conversion + * @param destOffset the index where output will be put + * @return the destination array + * @since 1.3 + */ + private static byte[] encode3to4( + byte[] source, int srcOffset, int numSigBytes, + byte[] destination, int destOffset ) + { + // 1 2 3 + // 01234567890123456789012345678901 Bit position + // --------000000001111111122222222 Array position from threeBytes + // --------| || || || | Six bit groups to index ALPHABET + // >>18 >>12 >> 6 >> 0 Right shift necessary + // 0x3f 0x3f 0x3f Additional AND + + // Create buffer with zero-padding if there are only one or two + // significant bytes passed in the array. + // We have to shift left 24 in order to flush out the 1's that appear + // when Java treats a value as negative that is cast from a byte to an int. + int inBuff = ( numSigBytes > 0 ? ((source[ srcOffset ] << 24) >>> 8) : 0 ) + | ( numSigBytes > 1 ? ((source[ srcOffset + 1 ] << 24) >>> 16) : 0 ) + | ( numSigBytes > 2 ? ((source[ srcOffset + 2 ] << 24) >>> 24) : 0 ); + + switch( numSigBytes ) + { + case 3: + destination[ destOffset ] = ALPHABET[ (inBuff >>> 18) ]; + destination[ destOffset + 1 ] = ALPHABET[ (inBuff >>> 12) & 0x3f ]; + destination[ destOffset + 2 ] = ALPHABET[ (inBuff >>> 6) & 0x3f ]; + destination[ destOffset + 3 ] = ALPHABET[ (inBuff ) & 0x3f ]; + return destination; + + case 2: + destination[ destOffset ] = ALPHABET[ (inBuff >>> 18) ]; + destination[ destOffset + 1 ] = ALPHABET[ (inBuff >>> 12) & 0x3f ]; + destination[ destOffset + 2 ] = ALPHABET[ (inBuff >>> 6) & 0x3f ]; + destination[ destOffset + 3 ] = EQUALS_SIGN; + return destination; + + case 1: + destination[ destOffset ] = ALPHABET[ (inBuff >>> 18) ]; + destination[ destOffset + 1 ] = ALPHABET[ (inBuff >>> 12) & 0x3f ]; + destination[ destOffset + 2 ] = EQUALS_SIGN; + destination[ destOffset + 3 ] = EQUALS_SIGN; + return destination; + + default: + return destination; + } // end switch + } // end encode3to4 + + + + /** + * Serializes an object and returns the Base64-encoded + * version of that serialized object. If the object + * cannot be serialized or there is another error, + * the method will return null. + * The object is not GZip-compressed before being encoded. + * + * @param serializableObject The object to encode + * @return The Base64-encoded object + * @throws IOException + * @since 1.4 + */ + public static String encodeObject( java.io.Serializable serializableObject ) + throws IOException + { + return encodeObject( serializableObject, NO_OPTIONS ); + } // end encodeObject + + + + /** + * Serializes an object and returns the Base64-encoded + * version of that serialized object. If the object + * cannot be serialized or there is another error, + * the method will return null. + *

+ * Valid options:

+         *   GZIP: gzip-compresses object before encoding it.
+         *   DONT_BREAK_LINES: don't break lines at 76 characters
+         *     Note: Technically, this makes your encoding non-compliant.
+         * 
+ *

+ * Example: encodeObject( myObj, Base64.GZIP ) or + *

+ * Example: encodeObject( myObj, Base64.GZIP | Base64.DONT_BREAK_LINES ) + * + * @param serializableObject The object to encode + * @param options Specified options + * @return The Base64-encoded object + * @throws IOException + * @see Base64#GZIP + * @see Base64#DONT_BREAK_LINES + * @since 2.0 + */ + public static String encodeObject( java.io.Serializable serializableObject, int options ) + throws IOException + { + // Streams + java.io.ByteArrayOutputStream baos = null; + java.io.OutputStream b64os = null; + java.io.ObjectOutputStream oos = null; + java.util.zip.GZIPOutputStream gzos = null; + + // Isolate options + int gzip = (options & GZIP); + int dontBreakLines = (options & DONT_BREAK_LINES); + + try + { + // ObjectOutputStream -> (GZIP) -> Base64 -> ByteArrayOutputStream + baos = new java.io.ByteArrayOutputStream(); + b64os = new Base64.OutputStream( baos, ENCODE | dontBreakLines ); + + // GZip? + if( gzip == GZIP ) + { + gzos = new java.util.zip.GZIPOutputStream( b64os ); + oos = new java.io.ObjectOutputStream( gzos ); + } // end if: gzip + else + oos = new java.io.ObjectOutputStream( b64os ); + + oos.writeObject( serializableObject ); + } // end try + finally + { + try{ oos.close(); } catch( Exception e ){ onCloseError(e); } + try{ gzos.close(); } catch( Exception e ){ onCloseError(e); } + try{ b64os.close(); } catch( Exception e ){ onCloseError(e); } + try{ baos.close(); } catch( Exception e ){ onCloseError(e); } + } // end finally + + // Return value according to relevant encoding. + try + { + return new String( baos.toByteArray(), PREFERRED_ENCODING ); + } // end try + catch (java.io.UnsupportedEncodingException uue) + { + return new String( baos.toByteArray() ); + } // end catch + + } // end encode + + + + /** + * Encodes a byte array into Base64 notation. + * Does not GZip-compress data. + * + * @param source The data to convert + * @throws IOException + * @since 1.4 + */ + public static String encodeBytes( byte[] source ) throws IOException + { + return encodeBytes( source, 0, source.length, NO_OPTIONS ); + } // end encodeBytes + + + + /** + * Encodes a byte array into Base64 notation. + *

+ * Valid options:

+         *   GZIP: gzip-compresses object before encoding it.
+         *   DONT_BREAK_LINES: don't break lines at 76 characters
+         *     Note: Technically, this makes your encoding non-compliant.
+         * 
+ *

+ * Example: encodeBytes( myData, Base64.GZIP ) or + *

+ * Example: encodeBytes( myData, Base64.GZIP | Base64.DONT_BREAK_LINES ) + * + * + * @param source The data to convert + * @param options Specified options + * @throws IOException + * @see Base64#GZIP + * @see Base64#DONT_BREAK_LINES + * @since 2.0 + */ + public static String encodeBytes( byte[] source, int options ) + throws IOException + { + return encodeBytes( source, 0, source.length, options ); + } // end encodeBytes + + + /** + * Encodes a byte array into Base64 notation. + * Does not GZip-compress data. + * + * @param source The data to convert + * @param off Offset in array where conversion should begin + * @param len Length of data to convert + * @throws IOException + * @since 1.4 + */ + public static String encodeBytes( byte[] source, int off, int len ) + throws IOException + { + return encodeBytes( source, off, len, NO_OPTIONS ); + } // end encodeBytes + + + + /** + * Encodes a byte array into Base64 notation. + *

+ * Valid options:

+         *   GZIP: gzip-compresses object before encoding it.
+         *   DONT_BREAK_LINES: don't break lines at 76 characters
+         *     Note: Technically, this makes your encoding non-compliant.
+         * 
+ *

+ * Example: encodeBytes( myData, Base64.GZIP ) or + *

+ * Example: encodeBytes( myData, Base64.GZIP | Base64.DONT_BREAK_LINES ) + * + * + * @param source The data to convert + * @param off Offset in array where conversion should begin + * @param len Length of data to convert + * @param options Specified options + * @throws IOException + * @see Base64#GZIP + * @see Base64#DONT_BREAK_LINES + * @since 2.0 + */ + public static String encodeBytes( byte[] source, int off, int len, int options ) + throws IOException + { + // Isolate options + int dontBreakLines = ( options & DONT_BREAK_LINES ); + int gzip = ( options & GZIP ); + + // Compress? + if( gzip == GZIP ) + { + java.io.ByteArrayOutputStream baos = null; + java.util.zip.GZIPOutputStream gzos = null; + Base64.OutputStream b64os = null; + + + try + { + // GZip -> Base64 -> ByteArray + baos = new java.io.ByteArrayOutputStream(); + b64os = new Base64.OutputStream( baos, ENCODE | dontBreakLines ); + gzos = new java.util.zip.GZIPOutputStream( b64os ); + + gzos.write( source, off, len ); + gzos.close(); + } // end try + finally + { + try{ gzos.close(); } catch( Exception e ){ onCloseError(e); } + try{ b64os.close(); } catch( Exception e ){ onCloseError(e); } + try{ baos.close(); } catch( Exception e ){ onCloseError(e); } + } // end finally + + // Return value according to relevant encoding. + try + { + return new String( baos.toByteArray(), PREFERRED_ENCODING ); + } // end try + catch (java.io.UnsupportedEncodingException uue) + { + return new String( baos.toByteArray() ); + } // end catch + } // end if: compress + + // Else, don't compress. Better not to use streams at all then. + else + { + // Convert option to boolean in way that code likes it. + boolean breakLines = dontBreakLines == 0; + + int len43 = len * 4 / 3; + byte[] outBuff = new byte[ ( len43 ) // Main 4:3 + + ( (len % 3) > 0 ? 4 : 0 ) // Account for padding + + (breakLines ? ( len43 / MAX_LINE_LENGTH ) : 0) ]; // New lines + int d = 0; + int e = 0; + int len2 = len - 2; + int lineLength = 0; + for( ; d < len2; d+=3, e+=4 ) + { + encode3to4( source, d+off, 3, outBuff, e ); + + lineLength += 4; + if( breakLines && lineLength == MAX_LINE_LENGTH ) + { + outBuff[e+4] = NEW_LINE; + e++; + lineLength = 0; + } // end if: end of line + } // en dfor: each piece of array + + if( d < len ) + { + encode3to4( source, d+off, len - d, outBuff, e ); + e += 4; + } // end if: some padding needed + + + // Return value according to relevant encoding. + try + { + return new String( outBuff, 0, e, PREFERRED_ENCODING ); + } // end try + catch (java.io.UnsupportedEncodingException uue) + { + return new String( outBuff, 0, e ); + } // end catch + + } // end else: don't compress + + } // end encodeBytes + + + + + + /* ******** D E C O D I N G M E T H O D S ******** */ + + + /** + * Decodes four bytes from array source + * and writes the resulting bytes (up to three of them) + * to destination. + * The source and destination arrays can be manipulated + * anywhere along their length by specifying + * srcOffset and destOffset. + * This method does not check to make sure your arrays + * are large enough to accomodate srcOffset + 4 for + * the source array or destOffset + 3 for + * the destination array. + * This method returns the actual number of bytes that + * were converted from the Base64 encoding. + * + * + * @param source the array to convert + * @param srcOffset the index where conversion begins + * @param destination the array to hold the conversion + * @param destOffset the index where output will be put + * @return the number of decoded bytes converted + * @since 1.3 + */ + private static int decode4to3( byte[] source, int srcOffset, byte[] destination, int destOffset ) + { + // Example: Dk== + if( source[ srcOffset + 2] == EQUALS_SIGN ) + { + // Two ways to do the same thing. Don't know which way I like best. + //int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6 ) + // | ( ( DECODABET[ source[ srcOffset + 1] ] << 24 ) >>> 12 ); + int outBuff = ( ( DECODABET[ source[ srcOffset ] ] & 0xFF ) << 18 ) + | ( ( DECODABET[ source[ srcOffset + 1] ] & 0xFF ) << 12 ); + + destination[ destOffset ] = (byte)( outBuff >>> 16 ); + return 1; + } + + // Example: DkL= + else if( source[ srcOffset + 3 ] == EQUALS_SIGN ) + { + // Two ways to do the same thing. Don't know which way I like best. + //int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6 ) + // | ( ( DECODABET[ source[ srcOffset + 1 ] ] << 24 ) >>> 12 ) + // | ( ( DECODABET[ source[ srcOffset + 2 ] ] << 24 ) >>> 18 ); + int outBuff = ( ( DECODABET[ source[ srcOffset ] ] & 0xFF ) << 18 ) + | ( ( DECODABET[ source[ srcOffset + 1 ] ] & 0xFF ) << 12 ) + | ( ( DECODABET[ source[ srcOffset + 2 ] ] & 0xFF ) << 6 ); + + destination[ destOffset ] = (byte)( outBuff >>> 16 ); + destination[ destOffset + 1 ] = (byte)( outBuff >>> 8 ); + return 2; + } + + // Example: DkLE + else + { + // Two ways to do the same thing. Don't know which way I like best. + //int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6 ) + // | ( ( DECODABET[ source[ srcOffset + 1 ] ] << 24 ) >>> 12 ) + // | ( ( DECODABET[ source[ srcOffset + 2 ] ] << 24 ) >>> 18 ) + // | ( ( DECODABET[ source[ srcOffset + 3 ] ] << 24 ) >>> 24 ); + int outBuff = ( ( DECODABET[ source[ srcOffset ] ] & 0xFF ) << 18 ) + | ( ( DECODABET[ source[ srcOffset + 1 ] ] & 0xFF ) << 12 ) + | ( ( DECODABET[ source[ srcOffset + 2 ] ] & 0xFF ) << 6) + | ( ( DECODABET[ source[ srcOffset + 3 ] ] & 0xFF ) ); + + + destination[ destOffset ] = (byte)( outBuff >> 16 ); + destination[ destOffset + 1 ] = (byte)( outBuff >> 8 ); + destination[ destOffset + 2 ] = (byte)( outBuff ); + + return 3; + } + } // end decodeToBytes + + + + + /** + * Very low-level access to decoding ASCII characters in + * the form of a byte array. Does not support automatically + * gunzipping or any other "fancy" features. + * + * @param source The Base64 encoded data + * @param off The offset of where to begin decoding + * @param len The length of characters to decode + * @return decoded data + * @throws IOException + * @since 1.3 + */ + public static byte[] decode( byte[] source, int off, int len ) + throws IOException + { + int len34 = len * 3 / 4; + byte[] outBuff = new byte[ len34 ]; // Upper limit on size of output + int outBuffPosn = 0; + + byte[] b4 = new byte[4]; + int b4Posn = 0; + int i = 0; + byte sbiCrop = 0; + byte sbiDecode = 0; + for( i = off; i < off+len; i++ ) + { + sbiCrop = (byte)(source[i] & 0x7f); // Only the low seven bits + sbiDecode = DECODABET[ sbiCrop ]; + + if( sbiDecode >= WHITE_SPACE_ENC ) // White space, Equals sign or better + { + if( sbiDecode >= EQUALS_SIGN_ENC ) + { + b4[ b4Posn++ ] = sbiCrop; + if( b4Posn > 3 ) + { + outBuffPosn += decode4to3( b4, 0, outBuff, outBuffPosn ); + b4Posn = 0; + + // If that was the equals sign, break out of 'for' loop + if( sbiCrop == EQUALS_SIGN ) + break; + } // end if: quartet built + + } // end if: equals sign or better + + } // end if: white space, equals sign or better + else + { + throw new IOException("Bad Base64 input character at " + i + ": " + source[i] + "(decimal)" ); + } // end else: + } // each input character + + byte[] out = new byte[ outBuffPosn ]; + System.arraycopy( outBuff, 0, out, 0, outBuffPosn ); + return out; + } // end decode + + + + + /** + * Decodes data from Base64 notation, automatically + * detecting gzip-compressed data and decompressing it. + * + * @param s the string to decode + * @return the decoded data + * @throws IOException + * @since 1.4 + */ + public static byte[] decode( String s ) throws IOException + { + byte[] bytes; + try + { + bytes = s.getBytes( PREFERRED_ENCODING ); + } // end try + catch( java.io.UnsupportedEncodingException uee ) + { + bytes = s.getBytes(); + } // end catch + // + + // Decode + bytes = decode( bytes, 0, bytes.length ); + + + // Check to see if it's gzip-compressed + // GZIP Magic Two-Byte Number: 0x8b1f (35615) + if( bytes != null && bytes.length >= 4 ) + { + + int head = ((int)bytes[0] & 0xff) | ((bytes[1] << 8) & 0xff00); + if( java.util.zip.GZIPInputStream.GZIP_MAGIC == head ) + { + java.io.ByteArrayInputStream bais = null; + java.util.zip.GZIPInputStream gzis = null; + java.io.ByteArrayOutputStream baos = null; + byte[] buffer = new byte[2048]; + int length = 0; + + try + { + baos = new java.io.ByteArrayOutputStream(); + bais = new java.io.ByteArrayInputStream( bytes ); + gzis = new java.util.zip.GZIPInputStream( bais ); + + while( ( length = gzis.read( buffer ) ) >= 0 ) + { + baos.write(buffer,0,length); + } // end while: reading input + + // No error? Get new bytes. + bytes = baos.toByteArray(); + + } // end try + catch( java.io.IOException e ) + { + // VEDERE MEGLIO + // Just return originally-decoded bytes + } // end catch + finally + { + try{ baos.close(); } catch( Exception e ){ onCloseError(e); } + try{ gzis.close(); } catch( Exception e ){ onCloseError(e); } + try{ bais.close(); } catch( Exception e ){ onCloseError(e); } + } // end finally + + } // end if: gzipped + } // end if: bytes.length >= 2 + + return bytes; + } // end decode + + + + + /** + * Attempts to decode Base64 data and deserialize a Java + * Object within. Returns null if there was an error. + * + * @param encodedObject The Base64 data to decode + * @return The decoded and deserialized object + * @throws IOException + * @throws ClassNotFoundException + * @since 1.5 + */ + public static Object decodeToObject( String encodedObject ) + throws IOException, ClassNotFoundException + { + // Decode and gunzip if necessary + byte[] objBytes = decode( encodedObject ); + + java.io.ByteArrayInputStream bais = null; + java.io.ObjectInputStream ois = null; + Object obj = null; + + try + { + bais = new java.io.ByteArrayInputStream( objBytes ); + ois = new java.io.ObjectInputStream( bais ); + + obj = ois.readObject(); + } // end try + finally + { + try{ bais.close(); } catch( Exception e ){ onCloseError(e); } + try{ ois.close(); } catch( Exception e ){ onCloseError(e); } + } // end finally + + return obj; + } // end decodeObject + + + + /** + * Convenience method for encoding data to a file. + * + * @param dataToEncode byte array of data to encode in base64 form + * @param filename Filename for saving encoded data + * @return true if successful, false otherwise + * @throws IOException + * + * @since 2.1 + */ + public static boolean encodeToFile( byte[] dataToEncode, String filename ) + throws IOException + { + boolean success = false; + Base64.OutputStream bos = null; + try + { + bos = new Base64.OutputStream( + new java.io.FileOutputStream( filename ), Base64.ENCODE ); + bos.write( dataToEncode ); + success = true; + } // end try + finally + { + try{ bos.close(); } catch( Exception e ){ onCloseError(e); } + } // end finally + + return success; + } // end encodeToFile + + + /** + * Convenience method for decoding data to a file. + * + * @param dataToDecode Base64-encoded data as a string + * @param filename Filename for saving decoded data + * @return true if successful, false otherwise + * @throws IOException + * + * @since 2.1 + */ + public static boolean decodeToFile( String dataToDecode, String filename ) + throws IOException + { + boolean success = false; + Base64.OutputStream bos = null; + try + { + bos = new Base64.OutputStream( + new java.io.FileOutputStream( filename ), Base64.DECODE ); + bos.write( dataToDecode.getBytes( PREFERRED_ENCODING ) ); + success = true; + } // end try + finally + { + try{ bos.close(); } catch( Exception e ){ onCloseError(e); } + } // end finally + + return success; + } // end decodeToFile + + + + + /** + * Convenience method for reading a base64-encoded + * file and decoding it. + * + * @param filename Filename for reading encoded data + * @return decoded byte array or null if unsuccessful + * @throws IOException + * + * @since 2.1 + */ + public static byte[] decodeFromFile( String filename ) + throws IOException + { + byte[] decodedData = null; + Base64.InputStream bis = null; + try + { + // Set up some useful variables + java.io.File file = new java.io.File( filename ); + byte[] buffer = null; + int length = 0; + int numBytes = 0; + + // Check for size of file + if( file.length() > Integer.MAX_VALUE ) + { + throw new IOException("File is too big for this convenience method (" + file.length() + " bytes)." ); + } // end if: file too big for int index + buffer = new byte[ (int)file.length() ]; + + // Open a stream + bis = new Base64.InputStream( + new java.io.BufferedInputStream( + new java.io.FileInputStream( file ) ), Base64.DECODE ); + + // Read until done + while( ( numBytes = bis.read( buffer, length, 4096 ) ) >= 0 ) + length += numBytes; + + // Save in a variable to return + decodedData = new byte[ length ]; + System.arraycopy( buffer, 0, decodedData, 0, length ); + + } // end try + finally + { + try{ bis.close(); } catch( Exception e) { onCloseError(e); } + } // end finally + + return decodedData; + } // end decodeFromFile + + + + /** + * Convenience method for reading a binary file + * and base64-encoding it. + * + * @param filename Filename for reading binary data + * @return base64-encoded string or null if unsuccessful + * @throws IOException + * + * @since 2.1 + */ + public static String encodeFromFile( String filename ) + throws IOException + { + String encodedData = null; + Base64.InputStream bis = null; + try + { + // Set up some useful variables + java.io.File file = new java.io.File( filename ); + byte[] buffer = new byte[ (int)(file.length() * 1.4) ]; + int length = 0; + int numBytes = 0; + + // Open a stream + bis = new Base64.InputStream( + new java.io.BufferedInputStream( + new java.io.FileInputStream( file ) ), Base64.ENCODE ); + + // Read until done + while( ( numBytes = bis.read( buffer, length, 4096 ) ) >= 0 ) + length += numBytes; + + // Save in a variable to return + encodedData = new String( buffer, 0, length, Base64.PREFERRED_ENCODING ); + + } // end try + finally + { + try{ bis.close(); } catch( Exception e) { onCloseError(e); } + } // end finally + + return encodedData; + } // end encodeFromFile + + + + + /* ******** I N N E R C L A S S I N P U T S T R E A M ******** */ + + + + /** + * A {@link Base64.InputStream} will read data from another + * java.io.InputStream, given in the constructor, + * and encode/decode to/from Base64 notation on the fly. + * + * @see Base64 + * @since 1.3 + */ + public static class InputStream extends java.io.FilterInputStream + { + private boolean encode; // Encoding or decoding + private int position; // Current position in the buffer + private byte[] buffer; // Small buffer holding converted data + private int bufferLength; // Length of buffer (3 or 4) + private int numSigBytes; // Number of meaningful bytes in the buffer + private int lineLength; + private boolean breakLines; // Break lines at less than 80 characters + + + /** + * Constructs a {@link Base64.InputStream} in DECODE mode. + * + * @param in the java.io.InputStream from which to read data. + * @since 1.3 + */ + public InputStream( java.io.InputStream in ) + { + this( in, DECODE ); + } // end constructor + + + /** + * Constructs a {@link Base64.InputStream} in + * either ENCODE or DECODE mode. + *

+ * Valid options:

+             *   ENCODE or DECODE: Encode or Decode as data is read.
+             *   DONT_BREAK_LINES: don't break lines at 76 characters
+             *     (only meaningful when encoding)
+             *     Note: Technically, this makes your encoding non-compliant.
+             * 
+ *

+ * Example: new Base64.InputStream( in, Base64.DECODE ) + * + * + * @param in the java.io.InputStream from which to read data. + * @param options Specified options + * @see Base64#ENCODE + * @see Base64#DECODE + * @see Base64#DONT_BREAK_LINES + * @since 2.0 + */ + public InputStream( java.io.InputStream in, int options ) + { + super( in ); + this.breakLines = (options & DONT_BREAK_LINES) != DONT_BREAK_LINES; + this.encode = (options & ENCODE) == ENCODE; + this.bufferLength = encode ? 4 : 3; + this.buffer = new byte[ bufferLength ]; + this.position = -1; + this.lineLength = 0; + } // end constructor + + /** + * Reads enough of the input stream to convert + * to/from Base64 and returns the next byte. + * + * @return next byte + * @since 1.3 + */ + @Override + public int read() throws java.io.IOException + { + // Do we need to get data? + if( position < 0 ) + { + if( encode ) + { + byte[] b3 = new byte[3]; + int numBinaryBytes = 0; + for( int i = 0; i < 3; i++ ) + { + try + { + int b = in.read(); + + // If end of stream, b is -1. + if( b >= 0 ) + { + b3[i] = (byte)b; + numBinaryBytes++; + } // end if: not end of stream + + } // end try: read + catch( java.io.IOException e ) + { + // Only a problem if we got no data at all. + if( i == 0 ) + throw e; + + } // end catch + } // end for: each needed input byte + + if( numBinaryBytes > 0 ) + { + encode3to4( b3, 0, numBinaryBytes, buffer, 0 ); + position = 0; + numSigBytes = 4; + } // end if: got data + else + { + return -1; + } // end else + } // end if: encoding + + // Else decoding + else + { + byte[] b4 = new byte[4]; + int i = 0; + for( i = 0; i < 4; i++ ) + { + // Read four "meaningful" bytes: + int b = 0; + do{ b = in.read(); } + while( b >= 0 && DECODABET[ b & 0x7f ] <= WHITE_SPACE_ENC ); + + if( b < 0 ) + break; // Reads a -1 if end of stream + + b4[i] = (byte)b; + } // end for: each needed input byte + + if( i == 4 ) + { + numSigBytes = decode4to3( b4, 0, buffer, 0 ); + position = 0; + } // end if: got four characters + else if( i == 0 ){ + return -1; + } // end else if: also padded correctly + else + { + // Must have broken out from above. + throw new java.io.IOException( "Improperly padded Base64 input." ); + } // end + + } // end else: decode + } // end else: get data + + // Got data? + if( position >= 0 ) + { + // End of relevant data? + if( /*!encode &&*/ position >= numSigBytes ) + return -1; + + if( encode && breakLines && lineLength >= MAX_LINE_LENGTH ) + { + lineLength = 0; + return '\n'; + } // end if + else + { + lineLength++; // This isn't important when decoding + // but throwing an extra "if" seems + // just as wasteful. + + int b = buffer[ position++ ]; + + if( position >= bufferLength ) + position = -1; + + return b & 0xFF; // This is how you "cast" a byte that's + // intended to be unsigned. + } // end else + } // end if: position >= 0 + + // Else error + else + { + throw new java.io.IOException( "Error in Base64 code reading stream." ); + } // end else + } // end read + + + /** + * Calls {@link #read()} repeatedly until the end of stream + * is reached or len bytes are read. + * Returns number of bytes read into array or -1 if + * end of stream is encountered. + * + * @param dest array to hold values + * @param off offset for array + * @param len max number of bytes to read into array + * @return bytes read into array or -1 if end of stream is encountered. + * @since 1.3 + */ + @Override + public int read( byte[] dest, int off, int len ) throws java.io.IOException + { + int i; + int b; + for( i = 0; i < len; i++ ) + { + b = read(); + + //if( b < 0 && i == 0 ) + // return -1; + + if( b >= 0 ) + dest[off + i] = (byte)b; + else if( i == 0 ) + return -1; + else + break; // Out of 'for' loop + } // end for: each byte read + return i; + } // end read + + } // end inner class InputStream + + + + + + + /* ******** I N N E R C L A S S O U T P U T S T R E A M ******** */ + + + + /** + * A {@link Base64.OutputStream} will write data to another + * java.io.OutputStream, given in the constructor, + * and encode/decode to/from Base64 notation on the fly. + * + * @see Base64 + * @since 1.3 + */ + public static class OutputStream extends java.io.FilterOutputStream + { + private boolean encode; + private int position; + private byte[] buffer; + private int bufferLength; + private int lineLength; + private boolean breakLines; + private byte[] b4; // Scratch used in a few places + private boolean suspendEncoding; + + /** + * Constructs a {@link Base64.OutputStream} in ENCODE mode. + * + * @param out the java.io.OutputStream to which data will be written. + * @since 1.3 + */ + public OutputStream( java.io.OutputStream out ) + { + this( out, ENCODE ); + } // end constructor + + + /** + * Constructs a {@link Base64.OutputStream} in + * either ENCODE or DECODE mode. + *

+ * Valid options:

+             *   ENCODE or DECODE: Encode or Decode as data is read.
+             *   DONT_BREAK_LINES: don't break lines at 76 characters
+             *     (only meaningful when encoding)
+             *     Note: Technically, this makes your encoding non-compliant.
+             * 
+ *

+ * Example: new Base64.OutputStream( out, Base64.ENCODE ) + * + * @param out the java.io.OutputStream to which data will be written. + * @param options Specified options. + * @see Base64#ENCODE + * @see Base64#DECODE + * @see Base64#DONT_BREAK_LINES + * @since 1.3 + */ + public OutputStream( java.io.OutputStream out, int options ) + { + super( out ); + this.breakLines = (options & DONT_BREAK_LINES) != DONT_BREAK_LINES; + this.encode = (options & ENCODE) == ENCODE; + this.bufferLength = encode ? 3 : 4; + this.buffer = new byte[ bufferLength ]; + this.position = 0; + this.lineLength = 0; + this.suspendEncoding = false; + this.b4 = new byte[4]; + } // end constructor + + + /** + * Writes the byte to the output stream after + * converting to/from Base64 notation. + * When encoding, bytes are buffered three + * at a time before the output stream actually + * gets a write() call. + * When decoding, bytes are buffered four + * at a time. + * + * @param theByte the byte to write + * @since 1.3 + */ + @Override + public void write(int theByte) throws java.io.IOException + { + // Encoding suspended? + if( suspendEncoding ) + { + super.out.write( theByte ); + return; + } // end if: supsended + + // Encode? + if( encode ) + { + buffer[ position++ ] = (byte)theByte; + if( position >= bufferLength ) // Enough to encode. + { + out.write( encode3to4( b4, buffer, bufferLength ) ); + + lineLength += 4; + if( breakLines && lineLength >= MAX_LINE_LENGTH ) + { + out.write( NEW_LINE ); + lineLength = 0; + } // end if: end of line + + position = 0; + } // end if: enough to output + } // end if: encoding + + // Else, Decoding + else + { + // Meaningful Base64 character? + if( DECODABET[ theByte & 0x7f ] > WHITE_SPACE_ENC ) + { + buffer[ position++ ] = (byte)theByte; + if( position >= bufferLength ) // Enough to output. + { + int len = Base64.decode4to3( buffer, 0, b4, 0 ); + out.write( b4, 0, len ); + //out.write( Base64.decode4to3( buffer ) ); + position = 0; + } // end if: enough to output + } // end if: meaningful base64 character + else if( DECODABET[ theByte & 0x7f ] != WHITE_SPACE_ENC ) + { + throw new java.io.IOException( "Invalid character in Base64 data." ); + } // end else: not white space either + } // end else: decoding + } // end write + + + + /** + * Calls {@link #write(int)} repeatedly until len + * bytes are written. + * + * @param theBytes array from which to read bytes + * @param off offset for array + * @param len max number of bytes to read into array + * @since 1.3 + */ + @Override + public void write( byte[] theBytes, int off, int len ) throws java.io.IOException + { + // Encoding suspended? + if( suspendEncoding ) + { + super.out.write( theBytes, off, len ); + return; + } // end if: supsended + + for( int i = 0; i < len; i++ ) + { + write( theBytes[ off + i ] ); + } // end for: each byte written + + } // end write + + + + /** + * Method added by PHIL. [Thanks, PHIL. -Rob] + * This pads the buffer without closing the stream. + */ + public void flushBase64() throws java.io.IOException + { + if( position > 0 ) + { + if( encode ) + { + out.write( encode3to4( b4, buffer, position ) ); + position = 0; + } // end if: encoding + else + { + throw new java.io.IOException( "Base64 input not properly padded." ); + } // end else: decoding + } // end if: buffer partially full + + } // end flush + + + /** + * Flushes and closes (I think, in the superclass) the stream. + * + * @since 1.3 + */ + @Override + public void close() throws java.io.IOException + { + // 1. Ensure that pending characters are written + flushBase64(); + + // 2. Actually close the stream + // Base class both flushes and closes. + super.close(); + + buffer = null; + out = null; + } // end close + + + + /** + * Suspends encoding of the stream. + * May be helpful if you need to embed a piece of + * base640-encoded data in a stream. + * + * @since 1.5.1 + */ + public void suspendEncoding() throws java.io.IOException + { + flushBase64(); + this.suspendEncoding = true; + } // end suspendEncoding + + + /** + * Resumes encoding of the stream. + * May be helpful if you need to embed a piece of + * base640-encoded data in a stream. + * + * @since 1.5.1 + */ + public void resumeEncoding() + { + this.suspendEncoding = false; + } // end resumeEncoding + + + + } // end inner class OutputStream + + + } // end class Base64 + +} diff --git a/src/com/lightstreamer/adapters/LeapMotionDemo/engine3D/BaseModelBody.java b/src/main/java/com/lightstreamer/adapters/LeapMotionDemo/engine3D/BaseModelBody.java similarity index 95% rename from src/com/lightstreamer/adapters/LeapMotionDemo/engine3D/BaseModelBody.java rename to src/main/java/com/lightstreamer/adapters/LeapMotionDemo/engine3D/BaseModelBody.java index d72d36b..52092cd 100644 --- a/src/com/lightstreamer/adapters/LeapMotionDemo/engine3D/BaseModelBody.java +++ b/src/main/java/com/lightstreamer/adapters/LeapMotionDemo/engine3D/BaseModelBody.java @@ -1,377 +1,378 @@ -/* - Copyright (c) Lightstreamer Srl - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ -package com.lightstreamer.adapters.LeapMotionDemo.engine3D; - -import java.io.IOException; -import java.nio.ByteBuffer; -import java.util.HashMap; - -import org.apache.log4j.Logger; - -import com.croftsoft.core.lang.EnumUnknownException; -import com.croftsoft.core.math.MathLib; -import com.croftsoft.core.math.axis.AxisAngle; -import com.croftsoft.core.math.axis.AxisAngleImp; -import com.croftsoft.core.math.axis.AxisAngleMut; -import com.lightstreamer.adapters.LeapMotionDemo.Constants; - -public class BaseModelBody implements IBody { - - private Logger logger = Logger.getLogger(Constants.WORLD_CAT); - - private static final double ROTATE_DELTA = 0.5; - private static final double TRANSLATE_DELTA = 0.002; - private static final double WORLD_SIZE_X = 160; - private static final double WORLD_SIZE_Y = 90; - private static final double WORLD_SIZE_Z = 120; - - private String id; - - private long lifeSpan = 0; - private long lastCmdRcvd = 0; - - private double x, y, z; // position Vector3 - - private double vX, vY, vZ; // velocity Vector3 - private final AxisAngleMut axisAngle; // Spin Quaternion/Matrix3x3 - private double deltaRotX, deltaRotY, deltaRotZ; // angularMomentum Vector3 - - public BaseModelBody(String id) { - this(id,new AxisAngleImp(), - (double)((Math.random() * 50) - 25),(double)((Math.random() * 50) - 25),(double)((Math.random() * 50) - 25)); - } - - public BaseModelBody(BaseModelBody orig) { - this(orig.getId(),orig.getAxisAngle(), - orig.getX(),orig.getY(),orig.getZ(), - orig.getvX(),orig.getvY(),orig.getvZ(), - orig.getDeltaRotX(),orig.getDeltaRotY(),orig.getDeltaRotZ()); - } - - public BaseModelBody(String id, AxisAngle axisAngle, double x, double y, double z) { - this(id,axisAngle,x,y,z,0,0,0,0,0,0); - } - - public BaseModelBody(String id, AxisAngle axisAngle, - double x, double y, double z, - double vX, double vY, double vZ, - double deltaRotX, double deltaRotY, double deltaRotZ) { - - this.id = id; - - this.axisAngle = new AxisAngleImp(axisAngle); - - this.x = x; - this.y = y; - this.z = z; - - this.vX = vX; - this.vY = vY; - this.vZ = vZ; - - this.deltaRotX = deltaRotX; - this.deltaRotY = deltaRotY; - this.deltaRotZ = deltaRotZ; - - } - - // --> simple getters/setters - - public String getId() { - return this.id; - } - - public long getLifeSpan() { - return lifeSpan; - } - - @Override - public AxisAngle getAxisAngle() { - return axisAngle; - } - - @Override - public double getX() { - return x; - } - - @Override - public double getY() { - return y; - } - - @Override - public double getZ() { - return z; - } - - @Override - public void setAxisAngle(AxisAngle axisAngle) { - this.axisAngle.copy( axisAngle ); - } - - @Override - public void setX(double x) { - this.x = x; - } - - @Override - public void setY(double y) { - this.y = y; - } - - @Override - public void setZ(double z) { - this.z = z; - } - - public double getvX() { - return vX; - } - - public double getvY() { - return vY; - } - - public double getvZ() { - return vZ; - } - - public double getDeltaRotX() { - return deltaRotX; - } - - public double getDeltaRotY() { - return deltaRotY; - } - - public double getDeltaRotZ() { - return deltaRotZ; - } - - public long getInactivityPeriod() { - return (this.lifeSpan - this.lastCmdRcvd); - } - - // --> model transformations - - @Override - public void rotate(AxisAngle axisAngleRot) { - AxisAngleMut newAxisAngleMut = this.axisAngle.toQuat( ).multiply(axisAngleRot.toQuat()).toAxisAngle ( ); - - newAxisAngleMut.normalize (); - - this.axisAngle.copy(newAxisAngleMut); - } - - @Override - public void rotate(Axis axis, double degrees) { - // TODO Auto-generated method stub - - } - - @Override - public void rotate(Rotation rotation, double degrees) { - // TODO Auto-generated method stub - - } - - @Override - public void rotate() { - rotate ( new AxisAngleImp ( this.deltaRotX, 1, 0, 0 ) ); - rotate ( new AxisAngleImp ( this.deltaRotY, 0, 1, 0 ) ); - rotate ( new AxisAngleImp ( this.deltaRotZ, 0, 0, 1 ) ); - } - - @Override - public void rotate(double factor) { - rotate ( new AxisAngleImp ( this.deltaRotX * factor, 1, 0, 0 ) ); - rotate ( new AxisAngleImp ( this.deltaRotY * factor, 0, 1, 0 ) ); - rotate ( new AxisAngleImp ( this.deltaRotZ * factor, 0, 0, 1 ) ); - } - - @Override - public void translate(Axis axis, double distance) { - switch ( axis ) - { - case X: - this.x += distance; - - break; - - case Y: - this.y += distance; - - break; - - case Z: - this.z += distance; - - break; - - default: - // Skip. - } - } - - @Override - public void translate() { - this.x += (double)(this.vX * TRANSLATE_DELTA); - this.y += (double)(this.vY * TRANSLATE_DELTA); - this.z += (double)(this.vZ * TRANSLATE_DELTA); - - this.x = MathLib.wrap(this.x, (-0.5 * WORLD_SIZE_X), WORLD_SIZE_X); - this.y = MathLib.wrap(this.y, (-0.5 * WORLD_SIZE_Y), WORLD_SIZE_Y); - this.z = MathLib.wrap(this.z, (-0.5 * WORLD_SIZE_Z), WORLD_SIZE_Z); - - this.lifeSpan += 1; - } - - @Override - public void translate(double factor) { - this.x += (double)(this.vX * TRANSLATE_DELTA * factor); - this.y += (double)(this.vY * TRANSLATE_DELTA * factor); - this.z += (double)(this.vZ * TRANSLATE_DELTA * factor); - - this.x = MathLib.wrap(this.x, (-0.5 * WORLD_SIZE_X), WORLD_SIZE_X); - this.y = MathLib.wrap(this.y, (-0.5 * WORLD_SIZE_Y), WORLD_SIZE_Y); - this.z = MathLib.wrap(this.z, (-0.5 * WORLD_SIZE_Z), WORLD_SIZE_Z); - - this.lifeSpan += 1; - } - - @Override - public void translate(Translation translation, double distance) { - // TODO Auto-generated method stub - - } - - //user inputs --> - - public void setImpulse(Axis axis, double intensity) { - switch ( axis ) - { - case X: - this.vX += intensity; - - break; - - case Y: - this.vY += intensity; - - break; - - case Z: - this.vZ += intensity; - - break; - - default: - throw new EnumUnknownException ( axis ); - } - this.lastCmdRcvd = this.lifeSpan; - } - - public void setTourque(Axis axis, double intensity) { - switch ( axis ) - { - case X: - this.deltaRotX += (intensity * ROTATE_DELTA); - - break; - - case Y: - this.deltaRotY += (intensity * ROTATE_DELTA); - - break; - - case Z: - this.deltaRotZ += (intensity * ROTATE_DELTA); - - break; - - default: - throw new EnumUnknownException ( axis ); - } - this.lastCmdRcvd = this.lifeSpan; - } - - public void block() { - this.vX = 0; - this.vY = 0; - this.vZ = 0; - - this.deltaRotX = 0; - this.deltaRotY = 0; - this.deltaRotZ = 0; - } - - public void forcePosition(double x, double y, double z) { - this.x = x; - this.y = y; - this.z = z; - } - - // data extraction --> - - private static byte[] toByteArray(double value) { - byte[] bytes = new byte[8]; - ByteBuffer.wrap(bytes).putDouble(value); - return bytes; - } - - private static String toBase64(double value) throws IOException { - String s = (new Base64Manager()).encodeBytes(toByteArray((float)value),true); - return s.substring(0, s.indexOf("=")); - } - - public void fillPositionMap(HashMap model) { - if (logger.isTraceEnabled()) { - logger.trace(this.id+"|preparing position update"); - } - - try { - model.put("posX", toBase64(this.x)); - model.put("posY", toBase64(this.y)); - model.put("posZ", toBase64(this.z)); - - model.put("rotX", toBase64(this.axisAngle.toQuat().getX())); - model.put("rotY", toBase64(this.axisAngle.toQuat().getY())); - model.put("rotZ", toBase64(this.axisAngle.toQuat().getZ())); - model.put("rotW", toBase64(this.axisAngle.toQuat().getW())); - - } catch (IOException e) { - //TODO ? - } - } - - public void fillImpulseMap(HashMap model) { - if (logger.isTraceEnabled()) { - logger.trace(this.id+"|preparing speed update"); - } - - model.put("dVx", String.valueOf(this.vX)); - model.put("dVy", String.valueOf(this.vY)); - model.put("dVz", String.valueOf(this.vZ)); - - model.put("dRx", String.valueOf(this.deltaRotX)); - model.put("dRy", String.valueOf(this.deltaRotY)); - model.put("dRz", String.valueOf(this.deltaRotZ)); - } - - - -} +/* + Copyright (c) Lightstreamer Srl + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ +package com.lightstreamer.adapters.LeapMotionDemo.engine3D; + +import java.io.IOException; +import java.nio.ByteBuffer; +import java.util.HashMap; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import com.croftsoft.core.lang.EnumUnknownException; +import com.croftsoft.core.math.MathLib; +import com.croftsoft.core.math.axis.AxisAngle; +import com.croftsoft.core.math.axis.AxisAngleImp; +import com.croftsoft.core.math.axis.AxisAngleMut; +import com.lightstreamer.adapters.LeapMotionDemo.Constants; + +public class BaseModelBody implements IBody { + + private Logger logger = LogManager.getLogger(Constants.WORLD_CAT); + + private static final double ROTATE_DELTA = 0.5; + private static final double TRANSLATE_DELTA = 0.002; + private static final double WORLD_SIZE_X = 160; + private static final double WORLD_SIZE_Y = 90; + private static final double WORLD_SIZE_Z = 120; + + private String id; + + private long lifeSpan = 0; + private long lastCmdRcvd = 0; + + private double x, y, z; // position Vector3 + + private double vX, vY, vZ; // velocity Vector3 + private final AxisAngleMut axisAngle; // Spin Quaternion/Matrix3x3 + private double deltaRotX, deltaRotY, deltaRotZ; // angularMomentum Vector3 + + public BaseModelBody(String id) { + this(id,new AxisAngleImp(), + (double)((Math.random() * 50) - 25),(double)((Math.random() * 50) - 25),(double)((Math.random() * 50) - 25)); + } + + public BaseModelBody(BaseModelBody orig) { + this(orig.getId(),orig.getAxisAngle(), + orig.getX(),orig.getY(),orig.getZ(), + orig.getvX(),orig.getvY(),orig.getvZ(), + orig.getDeltaRotX(),orig.getDeltaRotY(),orig.getDeltaRotZ()); + } + + public BaseModelBody(String id, AxisAngle axisAngle, double x, double y, double z) { + this(id,axisAngle,x,y,z,0,0,0,0,0,0); + } + + public BaseModelBody(String id, AxisAngle axisAngle, + double x, double y, double z, + double vX, double vY, double vZ, + double deltaRotX, double deltaRotY, double deltaRotZ) { + + this.id = id; + + this.axisAngle = new AxisAngleImp(axisAngle); + + this.x = x; + this.y = y; + this.z = z; + + this.vX = vX; + this.vY = vY; + this.vZ = vZ; + + this.deltaRotX = deltaRotX; + this.deltaRotY = deltaRotY; + this.deltaRotZ = deltaRotZ; + + } + + // --> simple getters/setters + + public String getId() { + return this.id; + } + + public long getLifeSpan() { + return lifeSpan; + } + + @Override + public AxisAngle getAxisAngle() { + return axisAngle; + } + + @Override + public double getX() { + return x; + } + + @Override + public double getY() { + return y; + } + + @Override + public double getZ() { + return z; + } + + @Override + public void setAxisAngle(AxisAngle axisAngle) { + this.axisAngle.copy( axisAngle ); + } + + @Override + public void setX(double x) { + this.x = x; + } + + @Override + public void setY(double y) { + this.y = y; + } + + @Override + public void setZ(double z) { + this.z = z; + } + + public double getvX() { + return vX; + } + + public double getvY() { + return vY; + } + + public double getvZ() { + return vZ; + } + + public double getDeltaRotX() { + return deltaRotX; + } + + public double getDeltaRotY() { + return deltaRotY; + } + + public double getDeltaRotZ() { + return deltaRotZ; + } + + public long getInactivityPeriod() { + return (this.lifeSpan - this.lastCmdRcvd); + } + + // --> model transformations + + @Override + public void rotate(AxisAngle axisAngleRot) { + AxisAngleMut newAxisAngleMut = this.axisAngle.toQuat( ).multiply(axisAngleRot.toQuat()).toAxisAngle ( ); + + newAxisAngleMut.normalize (); + + this.axisAngle.copy(newAxisAngleMut); + } + + @Override + public void rotate(Axis axis, double degrees) { + // TODO Auto-generated method stub + + } + + @Override + public void rotate(Rotation rotation, double degrees) { + // TODO Auto-generated method stub + + } + + @Override + public void rotate() { + rotate ( new AxisAngleImp ( this.deltaRotX, 1, 0, 0 ) ); + rotate ( new AxisAngleImp ( this.deltaRotY, 0, 1, 0 ) ); + rotate ( new AxisAngleImp ( this.deltaRotZ, 0, 0, 1 ) ); + } + + @Override + public void rotate(double factor) { + rotate ( new AxisAngleImp ( this.deltaRotX * factor, 1, 0, 0 ) ); + rotate ( new AxisAngleImp ( this.deltaRotY * factor, 0, 1, 0 ) ); + rotate ( new AxisAngleImp ( this.deltaRotZ * factor, 0, 0, 1 ) ); + } + + @Override + public void translate(Axis axis, double distance) { + switch ( axis ) + { + case X: + this.x += distance; + + break; + + case Y: + this.y += distance; + + break; + + case Z: + this.z += distance; + + break; + + default: + // Skip. + } + } + + @Override + public void translate() { + this.x += (double)(this.vX * TRANSLATE_DELTA); + this.y += (double)(this.vY * TRANSLATE_DELTA); + this.z += (double)(this.vZ * TRANSLATE_DELTA); + + this.x = MathLib.wrap(this.x, (-0.5 * WORLD_SIZE_X), WORLD_SIZE_X); + this.y = MathLib.wrap(this.y, (-0.5 * WORLD_SIZE_Y), WORLD_SIZE_Y); + this.z = MathLib.wrap(this.z, (-0.5 * WORLD_SIZE_Z), WORLD_SIZE_Z); + + this.lifeSpan += 1; + } + + @Override + public void translate(double factor) { + this.x += (double)(this.vX * TRANSLATE_DELTA * factor); + this.y += (double)(this.vY * TRANSLATE_DELTA * factor); + this.z += (double)(this.vZ * TRANSLATE_DELTA * factor); + + this.x = MathLib.wrap(this.x, (-0.5 * WORLD_SIZE_X), WORLD_SIZE_X); + this.y = MathLib.wrap(this.y, (-0.5 * WORLD_SIZE_Y), WORLD_SIZE_Y); + this.z = MathLib.wrap(this.z, (-0.5 * WORLD_SIZE_Z), WORLD_SIZE_Z); + + this.lifeSpan += 1; + } + + @Override + public void translate(Translation translation, double distance) { + // TODO Auto-generated method stub + + } + + //user inputs --> + + public void setImpulse(Axis axis, double intensity) { + switch ( axis ) + { + case X: + this.vX += intensity; + + break; + + case Y: + this.vY += intensity; + + break; + + case Z: + this.vZ += intensity; + + break; + + default: + throw new EnumUnknownException ( axis ); + } + this.lastCmdRcvd = this.lifeSpan; + } + + public void setTourque(Axis axis, double intensity) { + switch ( axis ) + { + case X: + this.deltaRotX += (intensity * ROTATE_DELTA); + + break; + + case Y: + this.deltaRotY += (intensity * ROTATE_DELTA); + + break; + + case Z: + this.deltaRotZ += (intensity * ROTATE_DELTA); + + break; + + default: + throw new EnumUnknownException ( axis ); + } + this.lastCmdRcvd = this.lifeSpan; + } + + public void block() { + this.vX = 0; + this.vY = 0; + this.vZ = 0; + + this.deltaRotX = 0; + this.deltaRotY = 0; + this.deltaRotZ = 0; + } + + public void forcePosition(double x, double y, double z) { + this.x = x; + this.y = y; + this.z = z; + } + + // data extraction --> + + private static byte[] toByteArray(double value) { + byte[] bytes = new byte[8]; + ByteBuffer.wrap(bytes).putDouble(value); + return bytes; + } + + private static String toBase64(double value) throws IOException { + String s = (new Base64Manager()).encodeBytes(toByteArray((float)value),true); + return s.substring(0, s.indexOf("=")); + } + + public void fillPositionMap(HashMap model) { + if (logger.isTraceEnabled()) { + logger.trace(this.id+"|preparing position update"); + } + + try { + model.put("posX", toBase64(this.x)); + model.put("posY", toBase64(this.y)); + model.put("posZ", toBase64(this.z)); + + model.put("rotX", toBase64(this.axisAngle.toQuat().getX())); + model.put("rotY", toBase64(this.axisAngle.toQuat().getY())); + model.put("rotZ", toBase64(this.axisAngle.toQuat().getZ())); + model.put("rotW", toBase64(this.axisAngle.toQuat().getW())); + + } catch (IOException e) { + //TODO ? + } + } + + public void fillImpulseMap(HashMap model) { + if (logger.isTraceEnabled()) { + logger.trace(this.id+"|preparing speed update"); + } + + model.put("dVx", String.valueOf(this.vX)); + model.put("dVy", String.valueOf(this.vY)); + model.put("dVz", String.valueOf(this.vZ)); + + model.put("dRx", String.valueOf(this.deltaRotX)); + model.put("dRy", String.valueOf(this.deltaRotY)); + model.put("dRz", String.valueOf(this.deltaRotZ)); + } + + + +} diff --git a/src/com/lightstreamer/adapters/LeapMotionDemo/engine3D/IBody.java b/src/main/java/com/lightstreamer/adapters/LeapMotionDemo/engine3D/IBody.java similarity index 96% rename from src/com/lightstreamer/adapters/LeapMotionDemo/engine3D/IBody.java rename to src/main/java/com/lightstreamer/adapters/LeapMotionDemo/engine3D/IBody.java index 1ab6d1f..a8e83d0 100644 --- a/src/com/lightstreamer/adapters/LeapMotionDemo/engine3D/IBody.java +++ b/src/main/java/com/lightstreamer/adapters/LeapMotionDemo/engine3D/IBody.java @@ -1,91 +1,91 @@ -/* - Copyright (c) Lightstreamer Srl - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ -package com.lightstreamer.adapters.LeapMotionDemo.engine3D; -import com.croftsoft.core.math.axis.AxisAngle; - -/*********************************************************************** -* Mutator interface for a JoglCamera. -* -* @version -* $Id: JoglCameraMut.java,v 1.3 2008/05/16 20:25:34 croft Exp $ -* @since -* 2008-05-16 -* @author -* David Wallace Croft -***********************************************************************/ - -public interface IBody { - - public enum Axis { - X, - Y, - Z - } - - public enum Rotation { - PITCH_DOWN, - PITCH_UP, - ROLL_LEFT, - ROLL_RIGHT, - YAW_LEFT, - YAW_RIGHT - } - - public enum Translation { - BACKWARD, - DOWN, - FORWARD, - LEFT, - RIGHT, - UP - } - - AxisAngle getAxisAngle ( ); - - double getX ( ); - - double getY ( ); - - double getZ ( ); - - void setAxisAngle ( AxisAngle axisAngle ); - - void setX ( double x ); - - void setY ( double y ); - - void setZ ( double z ); - - // relative mutator methods - - void rotate (AxisAngle axisAngle); - - void rotate (Axis axis, double degrees); - - void rotate (Rotation rotation, double degrees); - - void rotate(); - - void rotate(double factor); - - void translate (Axis axis, double distance); - - void translate (Translation translation, double distance); - - void translate(); - - void translate(double factor); +/* + Copyright (c) Lightstreamer Srl + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ +package com.lightstreamer.adapters.LeapMotionDemo.engine3D; +import com.croftsoft.core.math.axis.AxisAngle; + +/*********************************************************************** +* Mutator interface for a JoglCamera. +* +* @version +* $Id: JoglCameraMut.java,v 1.3 2008/05/16 20:25:34 croft Exp $ +* @since +* 2008-05-16 +* @author +* David Wallace Croft +***********************************************************************/ + +public interface IBody { + + public enum Axis { + X, + Y, + Z + } + + public enum Rotation { + PITCH_DOWN, + PITCH_UP, + ROLL_LEFT, + ROLL_RIGHT, + YAW_LEFT, + YAW_RIGHT + } + + public enum Translation { + BACKWARD, + DOWN, + FORWARD, + LEFT, + RIGHT, + UP + } + + AxisAngle getAxisAngle ( ); + + double getX ( ); + + double getY ( ); + + double getZ ( ); + + void setAxisAngle ( AxisAngle axisAngle ); + + void setX ( double x ); + + void setY ( double y ); + + void setZ ( double z ); + + // relative mutator methods + + void rotate (AxisAngle axisAngle); + + void rotate (Axis axis, double degrees); + + void rotate (Rotation rotation, double degrees); + + void rotate(); + + void rotate(double factor); + + void translate (Axis axis, double distance); + + void translate (Translation translation, double distance); + + void translate(); + + void translate(double factor); } \ No newline at end of file diff --git a/src/com/lightstreamer/adapters/LeapMotionDemo/engine3D/Universe.java b/src/main/java/com/lightstreamer/adapters/LeapMotionDemo/engine3D/Universe.java similarity index 94% rename from src/com/lightstreamer/adapters/LeapMotionDemo/engine3D/Universe.java rename to src/main/java/com/lightstreamer/adapters/LeapMotionDemo/engine3D/Universe.java index 7a630c6..8bff2a9 100644 --- a/src/com/lightstreamer/adapters/LeapMotionDemo/engine3D/Universe.java +++ b/src/main/java/com/lightstreamer/adapters/LeapMotionDemo/engine3D/Universe.java @@ -1,132 +1,133 @@ -/* - Copyright (c) Lightstreamer Srl - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ -package com.lightstreamer.adapters.LeapMotionDemo.engine3D; - -import java.util.HashMap; -import java.util.Map; - -import org.apache.log4j.Logger; - -import com.lightstreamer.adapters.LeapMotionDemo.Constants; - -public class Universe { - - private static Logger logger = Logger.getLogger(Constants.WORLD_CAT); - - private Map worlds = new HashMap(); - - - private UniverseListener listener; - - public Universe(UniverseListener listener) { - this.listener = listener; - } - - private synchronized World getWorldForced(String id) { - if (worlds.containsKey(id)) { - return worlds.get(id); - - } else { - World world = new World(id,listener, Constants.FRAME_INTERVAL); - worlds.put(id, world); - world.start(); - return world; - } - } - - public synchronized void removePlayerFromWorld(String id, String room) { - if (!worlds.containsKey(room)) { - return; - } - logger.info("User exiting world " + id + ": " + room); - - World world = worlds.get(room); - world.removeUser(id); - - this.verifyWorld(id,world); - } - - public synchronized void addPlayerToWorld(String id, String room) { - logger.info("User entering world " + id + ": " + room); - - World world = this.getWorldForced(room); - world.addUser(id); - } - - public synchronized void startWatchingWorld(String id, Object handle) { - logger.info("Start watching world: " + id); - - World world = this.getWorldForced(id); - - world.setHandle(handle); - } - - public synchronized void stopWatchingWorld(String id) { - if (!worlds.containsKey(id)) { - return; - } - logger.info("Stop watching world: " + id); - - World world = worlds.get(id); - world.setHandle(null); - - this.verifyWorld(id,world); - - } - - private synchronized void verifyWorld(String id, World world) { - if (world.isEmpty() && !world.isListened()) { - logger.info("World is now useless: " + id); - world.armageddon(); - worlds.remove(id); - } - } - - public void block(String playerId, String worldId) { - if (!worlds.containsKey(worldId)) { - return; - } - if (logger.isTraceEnabled()) { - logger.trace("Forwarding grab command for player" + playerId + " to world " + worldId); - } - World world = worlds.get(worldId); - world.block(playerId); - } - - public void release(String playerId, String worldId, double x, double y, double z) { - if (!worlds.containsKey(worldId)) { - return; - } - if (logger.isTraceEnabled()) { - logger.trace("Forwarding release command for player" + playerId + " to world " + worldId); - } - World world = worlds.get(worldId); - world.release(playerId,x,y,z); - } - - public void move(String playerId, String worldId, double x, double y, double z) { - if (!worlds.containsKey(worldId)) { - return; - } - if (logger.isTraceEnabled()) { - logger.trace("Forwarding move command for player" + playerId + " to world " + worldId); - } - World world = worlds.get(worldId); - world.move(playerId,x,y,z); - } - - -} +/* + Copyright (c) Lightstreamer Srl + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ +package com.lightstreamer.adapters.LeapMotionDemo.engine3D; + +import java.util.HashMap; +import java.util.Map; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import com.lightstreamer.adapters.LeapMotionDemo.Constants; + +public class Universe { + + private static Logger logger = LogManager.getLogger(Constants.WORLD_CAT); + + private Map worlds = new HashMap(); + + + private UniverseListener listener; + + public Universe(UniverseListener listener) { + this.listener = listener; + } + + private synchronized World getWorldForced(String id) { + if (worlds.containsKey(id)) { + return worlds.get(id); + + } else { + World world = new World(id,listener, Constants.FRAME_INTERVAL); + worlds.put(id, world); + world.start(); + return world; + } + } + + public synchronized void removePlayerFromWorld(String id, String room) { + if (!worlds.containsKey(room)) { + return; + } + logger.info("User exiting world " + id + ": " + room); + + World world = worlds.get(room); + world.removeUser(id); + + this.verifyWorld(id,world); + } + + public synchronized void addPlayerToWorld(String id, String room) { + logger.info("User entering world " + id + ": " + room); + + World world = this.getWorldForced(room); + world.addUser(id); + } + + public synchronized void startWatchingWorld(String id, Object handle) { + logger.info("Start watching world: " + id); + + World world = this.getWorldForced(id); + + world.setHandle(handle); + } + + public synchronized void stopWatchingWorld(String id) { + if (!worlds.containsKey(id)) { + return; + } + logger.info("Stop watching world: " + id); + + World world = worlds.get(id); + world.setHandle(null); + + this.verifyWorld(id,world); + + } + + private synchronized void verifyWorld(String id, World world) { + if (world.isEmpty() && !world.isListened()) { + logger.info("World is now useless: " + id); + world.armageddon(); + worlds.remove(id); + } + } + + public void block(String playerId, String worldId) { + if (!worlds.containsKey(worldId)) { + return; + } + if (logger.isTraceEnabled()) { + logger.trace("Forwarding grab command for player" + playerId + " to world " + worldId); + } + World world = worlds.get(worldId); + world.block(playerId); + } + + public void release(String playerId, String worldId, double x, double y, double z) { + if (!worlds.containsKey(worldId)) { + return; + } + if (logger.isTraceEnabled()) { + logger.trace("Forwarding release command for player" + playerId + " to world " + worldId); + } + World world = worlds.get(worldId); + world.release(playerId,x,y,z); + } + + public void move(String playerId, String worldId, double x, double y, double z) { + if (!worlds.containsKey(worldId)) { + return; + } + if (logger.isTraceEnabled()) { + logger.trace("Forwarding move command for player" + playerId + " to world " + worldId); + } + World world = worlds.get(worldId); + world.move(playerId,x,y,z); + } + + +} diff --git a/src/com/lightstreamer/adapters/LeapMotionDemo/engine3D/UniverseListener.java b/src/main/java/com/lightstreamer/adapters/LeapMotionDemo/engine3D/UniverseListener.java similarity index 97% rename from src/com/lightstreamer/adapters/LeapMotionDemo/engine3D/UniverseListener.java rename to src/main/java/com/lightstreamer/adapters/LeapMotionDemo/engine3D/UniverseListener.java index 04c4938..d2c53e5 100644 --- a/src/com/lightstreamer/adapters/LeapMotionDemo/engine3D/UniverseListener.java +++ b/src/main/java/com/lightstreamer/adapters/LeapMotionDemo/engine3D/UniverseListener.java @@ -1,32 +1,32 @@ -/* - Copyright (c) Lightstreamer Srl - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ -package com.lightstreamer.adapters.LeapMotionDemo.engine3D; - -import java.util.HashMap; - -public interface UniverseListener { - - void onWorldComplete(String fid, Object fhandle); - - void onPlayerCreated(String id, String worldId, Object worldHandle, HashMap currentPosition, HashMap currentImpulses, boolean realTimeEvent); - - void onPlayerDisposed(String id, String worldId, Object worldHandle); - - void onPlayerMoved(String id, String worldId, Object worldHandle, HashMap currentPosition, boolean forced); - - void onPlayerActed(String id, String worldId, Object worldHandle, HashMap currentImpulses); - -} +/* + Copyright (c) Lightstreamer Srl + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ +package com.lightstreamer.adapters.LeapMotionDemo.engine3D; + +import java.util.HashMap; + +public interface UniverseListener { + + void onWorldComplete(String fid, Object fhandle); + + void onPlayerCreated(String id, String worldId, Object worldHandle, HashMap currentPosition, HashMap currentImpulses, boolean realTimeEvent); + + void onPlayerDisposed(String id, String worldId, Object worldHandle); + + void onPlayerMoved(String id, String worldId, Object worldHandle, HashMap currentPosition, boolean forced); + + void onPlayerActed(String id, String worldId, Object worldHandle, HashMap currentImpulses); + +} diff --git a/src/com/lightstreamer/adapters/LeapMotionDemo/engine3D/World.java b/src/main/java/com/lightstreamer/adapters/LeapMotionDemo/engine3D/World.java similarity index 96% rename from src/com/lightstreamer/adapters/LeapMotionDemo/engine3D/World.java rename to src/main/java/com/lightstreamer/adapters/LeapMotionDemo/engine3D/World.java index 154ae90..2915f6d 100644 --- a/src/com/lightstreamer/adapters/LeapMotionDemo/engine3D/World.java +++ b/src/main/java/com/lightstreamer/adapters/LeapMotionDemo/engine3D/World.java @@ -1,270 +1,271 @@ -/* - Copyright (c) Lightstreamer Srl - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ -package com.lightstreamer.adapters.LeapMotionDemo.engine3D; - -import java.util.Enumeration; -import java.util.HashMap; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.Executor; -import java.util.concurrent.Executors; - -import org.apache.log4j.Logger; - -import com.lightstreamer.adapters.LeapMotionDemo.Constants; - -public class World extends Thread { - - private static final int ENTER = 1; - private static final int EXIT = 2; - - - private static final boolean REALTIME = true; - private static final boolean SNAPSHOT = false; - - private static final boolean NOT_FORCED = false; - private static final boolean FORCED = true; - - private Logger logger = Logger.getLogger(Constants.WORLD_CAT); - - private Executor executor = Executors.newSingleThreadExecutor(); - - private String id; - private UniverseListener listener; - - private ConcurrentHashMap users = new ConcurrentHashMap(); - private Object handle = null; - - private int frameInterval = 0; - private double factorWorld = 1.0; - - private boolean stop = false; - - World(String id, UniverseListener listener, int frameInterval) { - this.id = id; - this.listener = listener; - - this.frameInterval = frameInterval; - this.factorWorld = (double)(this.frameInterval / Constants.BASE_RATE); - - logger.info("A new world is born: " + id + " ("+this.frameInterval+")"); - } - - synchronized void setFrameInterval(int frameInterval) { - logger.info(this.id+"|Frame interval changed: " + frameInterval); - this.frameInterval = frameInterval; - this.factorWorld = (double)(this.frameInterval / Constants.BASE_RATE); - } - - synchronized boolean isListened() { - return this.handle != null; - } - - synchronized boolean isEmpty() { - return users.isEmpty(); - } - - synchronized void setHandle(Object handle) { - - this.handle = handle; - - if (this.handle != null) { - logger.info(this.id+"|New handle set"); - logger.debug(this.id+"|preparing snapshot events"); - - Enumeration players = this.users.elements(); - while(players.hasMoreElements()) { - BaseModelBody player = players.nextElement(); - this.sendPlayerStatus(player.getId(), this.id, this.handle, player, ENTER, SNAPSHOT); - } - - final String fid = this.id; - final Object fhandle = this.handle; - executor.execute(new Runnable() { - public void run() { - listener.onWorldComplete(fid,fhandle); - } - }); - - } else { - logger.info(this.id+"|Handle removed"); - } - } - - synchronized void addUser(String id) { - if (this.users.containsKey(id)) { - return; - } - BaseModelBody player = new BaseModelBody(id); - - this.users.put(id,player); - logger.info(this.id+"|A new user entered" + id); - - this.sendPlayerStatus(id, this.id, this.handle, player, ENTER, REALTIME); - - } - - - synchronized void removeUser(String id) { - this.users.remove(id); - logger.info(this.id+"|A user left" + id); - - this.sendPlayerStatus(id, this.id, this.handle, null, EXIT, REALTIME); - } - - synchronized void armageddon() { - this.stop = true; - logger.info(this.id+"|World ended"); - } - - @Override - public void run () { - - while (!stop) { - - if (logger.isTraceEnabled()) { - logger.trace(this.id+"|generating frame"); - } - - Enumeration players = this.users.elements(); - while(players.hasMoreElements()) { - BaseModelBody player = players.nextElement(); - - player.translate(this.factorWorld); - player.rotate(this.factorWorld); - - this.sendPlayerPosition(player.getId(), this.id, this.handle, player, NOT_FORCED); - } - - if (logger.isTraceEnabled()) { - logger.trace(this.id+"|frame generated"); - } - - try { - Thread.sleep(this.frameInterval); - } catch (InterruptedException ie) { - } - } - } - - private synchronized void sendPlayerStatus(final String id, final String worldId, final Object worldHandle, BaseModelBody player, final int updateType, final boolean isRealTime) { - if (updateType == ENTER) { - logger.debug(this.id+"|Preparing enter event for " + id); - - final HashMap currentPosition = new HashMap(); - player.fillPositionMap(currentPosition); - - final HashMap currentImpulses = new HashMap(); - player.fillImpulseMap(currentImpulses); - - executor.execute(new Runnable() { - @Override - public void run() { - listener.onPlayerCreated(id,worldId,worldHandle,currentPosition,currentImpulses,isRealTime); - } - }); - } else if (updateType == EXIT) { - logger.debug(this.id+"|Preparing exit event for " + id); - - executor.execute(new Runnable() { - @Override - public void run() { - listener.onPlayerDisposed(id,worldId,worldHandle); - } - }); - } - } - - private synchronized void sendPlayerPosition(final String id, final String worldId, final Object worldHandle, BaseModelBody player, final boolean forced) { - if (logger.isTraceEnabled()) { - logger.trace(this.id+"|Preparing position event for " + id); - } - - - final HashMap currentPosition = new HashMap(); - player.fillPositionMap(currentPosition); - - executor.execute(new Runnable() { - @Override - public void run() { - listener.onPlayerMoved(id, worldId, worldHandle, currentPosition, forced); - } - }); - } - - private synchronized void sendPlayerAction(final String id, final String worldId, final Object worldHandle, BaseModelBody player) { - if (logger.isTraceEnabled()) { - logger.trace(this.id+"|Preparing action event for " + id); - } - - final HashMap currentImpulses = new HashMap(); - player.fillImpulseMap(currentImpulses); - - executor.execute(new Runnable() { - @Override - public void run() { - listener.onPlayerActed(id, worldId, worldHandle, currentImpulses); - } - }); - } - - public synchronized void block(String playerId) { - if (!this.users.containsKey(playerId)) { - return; - } - if (logger.isTraceEnabled()) { - logger.trace(this.id+"|blocking " + id); - } - BaseModelBody player = this.users.get(playerId); - player.block(); - this.sendPlayerAction(playerId, this.id, this.handle, player); - } - - public synchronized void release(String playerId, double x, double y, double z) { - if (!this.users.containsKey(playerId)) { - return; - } - if (logger.isTraceEnabled()) { - logger.trace(this.id+"|releasing " + id); - } - BaseModelBody player = this.users.get(playerId); - player.setImpulse(IBody.Axis.X, x); - player.setImpulse(IBody.Axis.Y, y); - player.setImpulse(IBody.Axis.Z, z); - - player.setTourque(IBody.Axis.X, Math.round(x/2)); - player.setTourque(IBody.Axis.Y, Math.round(y/2)); - player.setTourque(IBody.Axis.Z, Math.round(z/2)); - - this.sendPlayerAction(playerId, this.id, this.handle, player); - } - - public synchronized void move(String playerId, double x, double y, double z) { - if (!this.users.containsKey(playerId)) { - return; - } - if (logger.isTraceEnabled()) { - logger.trace(this.id+"|moving " + id); - } - BaseModelBody player = this.users.get(playerId); - - player.setX(x); - player.setY(y); - player.setZ(z); - - this.sendPlayerPosition(playerId, this.id, this.handle, player, FORCED); - } - -} +/* + Copyright (c) Lightstreamer Srl + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ +package com.lightstreamer.adapters.LeapMotionDemo.engine3D; + +import java.util.Enumeration; +import java.util.HashMap; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import com.lightstreamer.adapters.LeapMotionDemo.Constants; + +public class World extends Thread { + + private static final int ENTER = 1; + private static final int EXIT = 2; + + + private static final boolean REALTIME = true; + private static final boolean SNAPSHOT = false; + + private static final boolean NOT_FORCED = false; + private static final boolean FORCED = true; + + private Logger logger = LogManager.getLogger(Constants.WORLD_CAT); + + private Executor executor = Executors.newSingleThreadExecutor(); + + private String id; + private UniverseListener listener; + + private ConcurrentHashMap users = new ConcurrentHashMap(); + private Object handle = null; + + private int frameInterval = 0; + private double factorWorld = 1.0; + + private boolean stop = false; + + World(String id, UniverseListener listener, int frameInterval) { + this.id = id; + this.listener = listener; + + this.frameInterval = frameInterval; + this.factorWorld = (double)(this.frameInterval / Constants.BASE_RATE); + + logger.info("A new world is born: " + id + " ("+this.frameInterval+")"); + } + + synchronized void setFrameInterval(int frameInterval) { + logger.info(this.id+"|Frame interval changed: " + frameInterval); + this.frameInterval = frameInterval; + this.factorWorld = (double)(this.frameInterval / Constants.BASE_RATE); + } + + synchronized boolean isListened() { + return this.handle != null; + } + + synchronized boolean isEmpty() { + return users.isEmpty(); + } + + synchronized void setHandle(Object handle) { + + this.handle = handle; + + if (this.handle != null) { + logger.info(this.id+"|New handle set"); + logger.debug(this.id+"|preparing snapshot events"); + + Enumeration players = this.users.elements(); + while(players.hasMoreElements()) { + BaseModelBody player = players.nextElement(); + this.sendPlayerStatus(player.getId(), this.id, this.handle, player, ENTER, SNAPSHOT); + } + + final String fid = this.id; + final Object fhandle = this.handle; + executor.execute(new Runnable() { + public void run() { + listener.onWorldComplete(fid,fhandle); + } + }); + + } else { + logger.info(this.id+"|Handle removed"); + } + } + + synchronized void addUser(String id) { + if (this.users.containsKey(id)) { + return; + } + BaseModelBody player = new BaseModelBody(id); + + this.users.put(id,player); + logger.info(this.id+"|A new user entered" + id); + + this.sendPlayerStatus(id, this.id, this.handle, player, ENTER, REALTIME); + + } + + + synchronized void removeUser(String id) { + this.users.remove(id); + logger.info(this.id+"|A user left" + id); + + this.sendPlayerStatus(id, this.id, this.handle, null, EXIT, REALTIME); + } + + synchronized void armageddon() { + this.stop = true; + logger.info(this.id+"|World ended"); + } + + @Override + public void run () { + + while (!stop) { + + if (logger.isTraceEnabled()) { + logger.trace(this.id+"|generating frame"); + } + + Enumeration players = this.users.elements(); + while(players.hasMoreElements()) { + BaseModelBody player = players.nextElement(); + + player.translate(this.factorWorld); + player.rotate(this.factorWorld); + + this.sendPlayerPosition(player.getId(), this.id, this.handle, player, NOT_FORCED); + } + + if (logger.isTraceEnabled()) { + logger.trace(this.id+"|frame generated"); + } + + try { + Thread.sleep(this.frameInterval); + } catch (InterruptedException ie) { + } + } + } + + private synchronized void sendPlayerStatus(final String id, final String worldId, final Object worldHandle, BaseModelBody player, final int updateType, final boolean isRealTime) { + if (updateType == ENTER) { + logger.debug(this.id+"|Preparing enter event for " + id); + + final HashMap currentPosition = new HashMap(); + player.fillPositionMap(currentPosition); + + final HashMap currentImpulses = new HashMap(); + player.fillImpulseMap(currentImpulses); + + executor.execute(new Runnable() { + @Override + public void run() { + listener.onPlayerCreated(id,worldId,worldHandle,currentPosition,currentImpulses,isRealTime); + } + }); + } else if (updateType == EXIT) { + logger.debug(this.id+"|Preparing exit event for " + id); + + executor.execute(new Runnable() { + @Override + public void run() { + listener.onPlayerDisposed(id,worldId,worldHandle); + } + }); + } + } + + private synchronized void sendPlayerPosition(final String id, final String worldId, final Object worldHandle, BaseModelBody player, final boolean forced) { + if (logger.isTraceEnabled()) { + logger.trace(this.id+"|Preparing position event for " + id); + } + + + final HashMap currentPosition = new HashMap(); + player.fillPositionMap(currentPosition); + + executor.execute(new Runnable() { + @Override + public void run() { + listener.onPlayerMoved(id, worldId, worldHandle, currentPosition, forced); + } + }); + } + + private synchronized void sendPlayerAction(final String id, final String worldId, final Object worldHandle, BaseModelBody player) { + if (logger.isTraceEnabled()) { + logger.trace(this.id+"|Preparing action event for " + id); + } + + final HashMap currentImpulses = new HashMap(); + player.fillImpulseMap(currentImpulses); + + executor.execute(new Runnable() { + @Override + public void run() { + listener.onPlayerActed(id, worldId, worldHandle, currentImpulses); + } + }); + } + + public synchronized void block(String playerId) { + if (!this.users.containsKey(playerId)) { + return; + } + if (logger.isTraceEnabled()) { + logger.trace(this.id+"|blocking " + id); + } + BaseModelBody player = this.users.get(playerId); + player.block(); + this.sendPlayerAction(playerId, this.id, this.handle, player); + } + + public synchronized void release(String playerId, double x, double y, double z) { + if (!this.users.containsKey(playerId)) { + return; + } + if (logger.isTraceEnabled()) { + logger.trace(this.id+"|releasing " + id); + } + BaseModelBody player = this.users.get(playerId); + player.setImpulse(IBody.Axis.X, x); + player.setImpulse(IBody.Axis.Y, y); + player.setImpulse(IBody.Axis.Z, z); + + player.setTourque(IBody.Axis.X, Math.round(x/2)); + player.setTourque(IBody.Axis.Y, Math.round(y/2)); + player.setTourque(IBody.Axis.Z, Math.round(z/2)); + + this.sendPlayerAction(playerId, this.id, this.handle, player); + } + + public synchronized void move(String playerId, double x, double y, double z) { + if (!this.users.containsKey(playerId)) { + return; + } + if (logger.isTraceEnabled()) { + logger.trace(this.id+"|moving " + id); + } + BaseModelBody player = this.users.get(playerId); + + player.setX(x); + player.setY(y); + player.setZ(z); + + this.sendPlayerPosition(playerId, this.id, this.handle, player, FORCED); + } + +} diff --git a/src/com/lightstreamer/adapters/LeapMotionDemo/room/ChatRoom.java b/src/main/java/com/lightstreamer/adapters/LeapMotionDemo/room/ChatRoom.java similarity index 95% rename from src/com/lightstreamer/adapters/LeapMotionDemo/room/ChatRoom.java rename to src/main/java/com/lightstreamer/adapters/LeapMotionDemo/room/ChatRoom.java index 1bfdc5d..fa2e163 100644 --- a/src/com/lightstreamer/adapters/LeapMotionDemo/room/ChatRoom.java +++ b/src/main/java/com/lightstreamer/adapters/LeapMotionDemo/room/ChatRoom.java @@ -1,466 +1,467 @@ -/* -Copyright (c) Lightstreamer Srl - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ -package com.lightstreamer.adapters.LeapMotionDemo.room; - -import java.util.HashMap; -import java.util.Iterator; -import java.util.Map; -import java.util.concurrent.Executor; -import java.util.concurrent.Executors; - -import org.apache.log4j.Logger; - -import com.lightstreamer.adapters.LeapMotionDemo.Constants; - - - -public class ChatRoom { - - private ChatRoomListener listener; - - static final boolean ENTER = true; - static final boolean EXIT = false; - - static final boolean REALTIME = true; - static final boolean SNAPSHOT = false; - - private Logger logger = Logger.getLogger(Constants.CHAT_CAT); - - - private HashMap users = new HashMap(); - private HashMap rooms = new HashMap(); - - private Executor executor = Executors.newSingleThreadExecutor(); - - public ChatRoom(ChatRoomListener listener) { - this.listener = listener; - } - - // USER INTERNAL HANDLING - - private User getUserForced(String id) { - synchronized(users) { - User user; - if (!users.containsKey(id)) { - user = this.addUser(id); - } else { - user = users.get(id); - } - return user; - } - } - - private User addUser(final String id) { - synchronized(users) { - logger.info("Creating new user " + id); - - User user = new User(this, id); - users.put(id, user); - - executor.execute(new Runnable() { - public void run() { - listener.onNewUser(id); - } - }); - - return user; - } - } - - private void removeUser(final String id) { - synchronized(users) { - if (!users.containsKey(id)) { - return; - } - logger.info("Destroying user " + id); - - User user = users.remove(id); - - this.removeUserFromRooms(user); - - executor.execute(new Runnable() { - public void run() { - listener.onUserDeleted(id); - } - }); - } - } - - //synchronized(users) - private void removeUserFromRooms(User user) { - synchronized(rooms) { - Iterator userRooms = user.getRooms(); - while(userRooms.hasNext()) { - Room room = userRooms.next(); - user.leaveRoom(room); - } - } - } - - private void checkUser(User user) { - if (!user.isListened() && !user.isActive()) { - this.removeUser(user.getId()); - } - } - - // USER LIFE-CYCLE - // IN - - public void startUser(String id) { - synchronized(users) { - logger.trace("User startup " + id); - - User user = this.getUserForced(id); - user.setActive(true); - } - } - - public void startUserMessageListen(String id, Object handle) { - synchronized(users) { - logger.trace("User private messages startup " + id); - - User user = this.getUserForced(id); - user.setHandle(handle); - } - } - - public void startUserStatusListen(String id, Object userStatusHandle) { - synchronized(users) { - logger.trace("User status messages startup " + id); - - User user = this.getUserForced(id); - user.setStatusHandle(userStatusHandle); - - Map extra = new HashMap(); - extra.putAll(user.getExtraProps()); - this.sendUserStatusEvent(user, user.getNick(), user.getStatusId(), user.getStatus(), extra, userStatusHandle, SNAPSHOT); - } - } - - // USER LIFE-CYCLE - // OUT - - public void stopUser(String id) { - synchronized(users) { - if (!users.containsKey(id)) { - return; - } - logger.trace("User stop " + id); - - User user = users.get(id); - user.setActive(false); - - this.checkUser(user); - } - } - - public void stopUserMessageListen(String id) { - synchronized(users) { - if (!users.containsKey(id)) { - return; - } - logger.trace("User private messages stop " + id); - - User user = users.get(id); - user.setHandle(null); - - this.checkUser(user); - } - } - - public void stopUserStatusListen(String id) { - synchronized(users) { - if (!users.containsKey(id)) { - return; - } - logger.trace("User status messages stop " + id); - - User user = users.get(id); - user.setStatusHandle(null); - - this.checkUser(user); - } - } - - // USER ACTIONS - // PROPERTIES - - public void changeUserNick(String id, String nick, String IP) { - synchronized(users) { - if (!users.containsKey(id)) { - return; - } - logger.debug(IP+"| User new nick " + id + ": " + nick); - - User user = users.get(id); - user.setNick(nick); - } - } - - public void changeUserStatus(String id, String status, String statusId, String IP) { - synchronized(users) { - if (!users.containsKey(id)) { - return; - } - logger.debug(IP+"| User new status " + id + ": " + status); - - User user = users.get(id); - user.setStatus(status,statusId); - } - } - - // USER ACTIONS - // ROOMS - - public void enterRoom(String id, String roomId) { - synchronized(users) { - if (!users.containsKey(id)) { - return; - } - logger.trace("User entering room " + id + ": " + roomId); - - User user = users.get(id); - Room room = this.getRoomForced(roomId); - user.enterRoom(room); - - } - } - - public void leaveRoom(String id, String roomId) { - synchronized(users) { - synchronized(rooms) { - if (!users.containsKey(id) || !rooms.containsKey(roomId)) { - return; - } - logger.trace("User leaving room " + id + ": " + roomId); - - User user = users.get(id); - Room room = rooms.get(roomId); - user.leaveRoom(room); - - if (room.isEmpty() && !room.isListened()) { - rooms.remove(roomId); - } - } - } - } - - public void leaveAllRooms(String id) { - synchronized(users) { - if (!users.containsKey(id)) { - return; - } - User user = this.getUserForced(id); - - this.removeUserFromRooms(user); - - if (!user.isListened()) { - this.removeUser(id); - } - - } - } - - // USER ACTIONS - // CHAT - - public void broadcastMessage(String id, String roomId, String message, String IP) { - synchronized(users) { - synchronized(rooms) { - if (!users.containsKey(id) || !rooms.containsKey(roomId)) { - return; - } - logger.debug(IP + "| User " + id + " message to room " + roomId + ": " + message); - - User user = users.get(id); - Room room = rooms.get(roomId); - room.broadcastMessage(user,message); - } - } - - } - - // USER EVENTS - - //synchronized(users) { - void sendUserStatusEvent(final User user, final String nick, final String statusId, final String status, final Map extra, final Object userStatusHandle, final boolean realTimeEvent) { - executor.execute(new Runnable() { - public void run() { - listener.onUserStatusChange(user, nick, statusId, status, extra, userStatusHandle, realTimeEvent); - } - }); - } - - // ROOM INTERNAL HANDLING - - private Room getRoomForced(String roomId) { - synchronized(rooms) { - Room room = null; - if (!rooms.containsKey(roomId)) { - room = this.addRoom(roomId); - } else { - room = rooms.get(roomId); - } - return room; - } - } - - private Room addRoom(String roomId) { - synchronized(rooms) { - logger.info("Creating new room " + roomId); - - Room room = new Room(this, roomId); - rooms.put(roomId, room); - return room; - } - } - - private void removeRoom(String roomId) { - synchronized(rooms) { - if (!rooms.containsKey(roomId)) { - return; - } - logger.info("Destroying room " + roomId); - - rooms.remove(roomId); - } - } - - private void checkRoom(Room room) { - if (room.isEmpty() && !room.isListened()) { - this.removeRoom(room.getId()); - } - } - - // ROOM LIFECYCLE - // IN - - public void startRoomListen(final String roomId, final Object roomStatusHandle) { - - synchronized(rooms) { - Room room = this.getRoomForced(roomId); - - logger.trace("Room user-list startup " + roomId); - - room.setStatusHandle(roomStatusHandle); - - Iterator roomUsers = room.getUsers(); - while(roomUsers.hasNext()) { - String id = roomUsers.next(); - User user = this.getUserForced(id); - this.sendRoomStatusEvent(user,roomId,roomStatusHandle,ENTER,SNAPSHOT); - } - - executor.execute(new Runnable() { - public void run() { - listener.onRoomListComplete(roomId, roomStatusHandle); - } - }); - } - } - - public void startRoomChatListen(String roomId, Object roomChatHandle) { - synchronized(rooms) { - logger.trace("Room chat startup " + roomId); - - Room room = this.getRoomForced(roomId); - - room.setMessageHandle(roomChatHandle); - } - - } - - // ROOM LIFECYCLE - // OUT - - public void stopRoomListen(String roomId) { - synchronized(rooms) { - if (!rooms.containsKey(roomId)) { - return; - } - logger.trace("Room user-list stop " + roomId); - - Room room = rooms.get(roomId); - room.setStatusHandle(null); - - this.checkRoom(room); - } - } - - public void stopRoomChatListen(String roomId) { - synchronized(rooms) { - if (!rooms.containsKey(roomId)) { - return; - } - logger.trace("Room chat stop " + roomId); - - Room room = rooms.get(roomId); - room.setMessageHandle(null); - - this.checkRoom(room); - } - - } - - // ROOM EVENTS - - //synchronized(rooms) { - void sendRoomStatusEvent(final User user, final String roomId, final Object roomStatusHandle, boolean entering, final boolean realTimeEvent) { - if (entering) { - executor.execute(new Runnable() { - public void run() { - listener.onUserEnter(user, roomId, roomStatusHandle, realTimeEvent); - } - }); - } else { - executor.execute(new Runnable() { - public void run() { - listener.onUserExit(user, roomId, roomStatusHandle); - } - }); - } - } - - - void sendRoomMessageEvent(final User user, final String room, final Object messageHandle, final String message) { - final String nick = user.getNick(); - executor.execute(new Runnable() { - public void run() { - listener.onUserMessage(nick,message,room,messageHandle,true); - } - }); - } - - - //This method is not in the original implementation --> we should use the "extra" channel - public Object getUserStatusHandle(String id) { - synchronized(users) { - if (!users.containsKey(id)) { - return null; - } - - User user = users.get(id); - return user.getStatusHandle(); - } - } - - -} - - - - - +/* +Copyright (c) Lightstreamer Srl + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ +package com.lightstreamer.adapters.LeapMotionDemo.room; + +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import com.lightstreamer.adapters.LeapMotionDemo.Constants; + + + +public class ChatRoom { + + private ChatRoomListener listener; + + static final boolean ENTER = true; + static final boolean EXIT = false; + + static final boolean REALTIME = true; + static final boolean SNAPSHOT = false; + + private Logger logger = LogManager.getLogger(Constants.CHAT_CAT); + + + private HashMap users = new HashMap(); + private HashMap rooms = new HashMap(); + + private Executor executor = Executors.newSingleThreadExecutor(); + + public ChatRoom(ChatRoomListener listener) { + this.listener = listener; + } + + // USER INTERNAL HANDLING + + private User getUserForced(String id) { + synchronized(users) { + User user; + if (!users.containsKey(id)) { + user = this.addUser(id); + } else { + user = users.get(id); + } + return user; + } + } + + private User addUser(final String id) { + synchronized(users) { + logger.info("Creating new user " + id); + + User user = new User(this, id); + users.put(id, user); + + executor.execute(new Runnable() { + public void run() { + listener.onNewUser(id); + } + }); + + return user; + } + } + + private void removeUser(final String id) { + synchronized(users) { + if (!users.containsKey(id)) { + return; + } + logger.info("Destroying user " + id); + + User user = users.remove(id); + + this.removeUserFromRooms(user); + + executor.execute(new Runnable() { + public void run() { + listener.onUserDeleted(id); + } + }); + } + } + + //synchronized(users) + private void removeUserFromRooms(User user) { + synchronized(rooms) { + Iterator userRooms = user.getRooms(); + while(userRooms.hasNext()) { + Room room = userRooms.next(); + user.leaveRoom(room); + } + } + } + + private void checkUser(User user) { + if (!user.isListened() && !user.isActive()) { + this.removeUser(user.getId()); + } + } + + // USER LIFE-CYCLE + // IN + + public void startUser(String id) { + synchronized(users) { + logger.trace("User startup " + id); + + User user = this.getUserForced(id); + user.setActive(true); + } + } + + public void startUserMessageListen(String id, Object handle) { + synchronized(users) { + logger.trace("User private messages startup " + id); + + User user = this.getUserForced(id); + user.setHandle(handle); + } + } + + public void startUserStatusListen(String id, Object userStatusHandle) { + synchronized(users) { + logger.trace("User status messages startup " + id); + + User user = this.getUserForced(id); + user.setStatusHandle(userStatusHandle); + + Map extra = new HashMap(); + extra.putAll(user.getExtraProps()); + this.sendUserStatusEvent(user, user.getNick(), user.getStatusId(), user.getStatus(), extra, userStatusHandle, SNAPSHOT); + } + } + + // USER LIFE-CYCLE + // OUT + + public void stopUser(String id) { + synchronized(users) { + if (!users.containsKey(id)) { + return; + } + logger.trace("User stop " + id); + + User user = users.get(id); + user.setActive(false); + + this.checkUser(user); + } + } + + public void stopUserMessageListen(String id) { + synchronized(users) { + if (!users.containsKey(id)) { + return; + } + logger.trace("User private messages stop " + id); + + User user = users.get(id); + user.setHandle(null); + + this.checkUser(user); + } + } + + public void stopUserStatusListen(String id) { + synchronized(users) { + if (!users.containsKey(id)) { + return; + } + logger.trace("User status messages stop " + id); + + User user = users.get(id); + user.setStatusHandle(null); + + this.checkUser(user); + } + } + + // USER ACTIONS + // PROPERTIES + + public void changeUserNick(String id, String nick, String IP) { + synchronized(users) { + if (!users.containsKey(id)) { + return; + } + logger.debug(IP+"| User new nick " + id + ": " + nick); + + User user = users.get(id); + user.setNick(nick); + } + } + + public void changeUserStatus(String id, String status, String statusId, String IP) { + synchronized(users) { + if (!users.containsKey(id)) { + return; + } + logger.debug(IP+"| User new status " + id + ": " + status); + + User user = users.get(id); + user.setStatus(status,statusId); + } + } + + // USER ACTIONS + // ROOMS + + public void enterRoom(String id, String roomId) { + synchronized(users) { + if (!users.containsKey(id)) { + return; + } + logger.trace("User entering room " + id + ": " + roomId); + + User user = users.get(id); + Room room = this.getRoomForced(roomId); + user.enterRoom(room); + + } + } + + public void leaveRoom(String id, String roomId) { + synchronized(users) { + synchronized(rooms) { + if (!users.containsKey(id) || !rooms.containsKey(roomId)) { + return; + } + logger.trace("User leaving room " + id + ": " + roomId); + + User user = users.get(id); + Room room = rooms.get(roomId); + user.leaveRoom(room); + + if (room.isEmpty() && !room.isListened()) { + rooms.remove(roomId); + } + } + } + } + + public void leaveAllRooms(String id) { + synchronized(users) { + if (!users.containsKey(id)) { + return; + } + User user = this.getUserForced(id); + + this.removeUserFromRooms(user); + + if (!user.isListened()) { + this.removeUser(id); + } + + } + } + + // USER ACTIONS + // CHAT + + public void broadcastMessage(String id, String roomId, String message, String IP) { + synchronized(users) { + synchronized(rooms) { + if (!users.containsKey(id) || !rooms.containsKey(roomId)) { + return; + } + logger.debug(IP + "| User " + id + " message to room " + roomId + ": " + message); + + User user = users.get(id); + Room room = rooms.get(roomId); + room.broadcastMessage(user,message); + } + } + + } + + // USER EVENTS + + //synchronized(users) { + void sendUserStatusEvent(final User user, final String nick, final String statusId, final String status, final Map extra, final Object userStatusHandle, final boolean realTimeEvent) { + executor.execute(new Runnable() { + public void run() { + listener.onUserStatusChange(user, nick, statusId, status, extra, userStatusHandle, realTimeEvent); + } + }); + } + + // ROOM INTERNAL HANDLING + + private Room getRoomForced(String roomId) { + synchronized(rooms) { + Room room = null; + if (!rooms.containsKey(roomId)) { + room = this.addRoom(roomId); + } else { + room = rooms.get(roomId); + } + return room; + } + } + + private Room addRoom(String roomId) { + synchronized(rooms) { + logger.info("Creating new room " + roomId); + + Room room = new Room(this, roomId); + rooms.put(roomId, room); + return room; + } + } + + private void removeRoom(String roomId) { + synchronized(rooms) { + if (!rooms.containsKey(roomId)) { + return; + } + logger.info("Destroying room " + roomId); + + rooms.remove(roomId); + } + } + + private void checkRoom(Room room) { + if (room.isEmpty() && !room.isListened()) { + this.removeRoom(room.getId()); + } + } + + // ROOM LIFECYCLE + // IN + + public void startRoomListen(final String roomId, final Object roomStatusHandle) { + + synchronized(rooms) { + Room room = this.getRoomForced(roomId); + + logger.trace("Room user-list startup " + roomId); + + room.setStatusHandle(roomStatusHandle); + + Iterator roomUsers = room.getUsers(); + while(roomUsers.hasNext()) { + String id = roomUsers.next(); + User user = this.getUserForced(id); + this.sendRoomStatusEvent(user,roomId,roomStatusHandle,ENTER,SNAPSHOT); + } + + executor.execute(new Runnable() { + public void run() { + listener.onRoomListComplete(roomId, roomStatusHandle); + } + }); + } + } + + public void startRoomChatListen(String roomId, Object roomChatHandle) { + synchronized(rooms) { + logger.trace("Room chat startup " + roomId); + + Room room = this.getRoomForced(roomId); + + room.setMessageHandle(roomChatHandle); + } + + } + + // ROOM LIFECYCLE + // OUT + + public void stopRoomListen(String roomId) { + synchronized(rooms) { + if (!rooms.containsKey(roomId)) { + return; + } + logger.trace("Room user-list stop " + roomId); + + Room room = rooms.get(roomId); + room.setStatusHandle(null); + + this.checkRoom(room); + } + } + + public void stopRoomChatListen(String roomId) { + synchronized(rooms) { + if (!rooms.containsKey(roomId)) { + return; + } + logger.trace("Room chat stop " + roomId); + + Room room = rooms.get(roomId); + room.setMessageHandle(null); + + this.checkRoom(room); + } + + } + + // ROOM EVENTS + + //synchronized(rooms) { + void sendRoomStatusEvent(final User user, final String roomId, final Object roomStatusHandle, boolean entering, final boolean realTimeEvent) { + if (entering) { + executor.execute(new Runnable() { + public void run() { + listener.onUserEnter(user, roomId, roomStatusHandle, realTimeEvent); + } + }); + } else { + executor.execute(new Runnable() { + public void run() { + listener.onUserExit(user, roomId, roomStatusHandle); + } + }); + } + } + + + void sendRoomMessageEvent(final User user, final String room, final Object messageHandle, final String message) { + final String nick = user.getNick(); + executor.execute(new Runnable() { + public void run() { + listener.onUserMessage(nick,message,room,messageHandle,true); + } + }); + } + + + //This method is not in the original implementation --> we should use the "extra" channel + public Object getUserStatusHandle(String id) { + synchronized(users) { + if (!users.containsKey(id)) { + return null; + } + + User user = users.get(id); + return user.getStatusHandle(); + } + } + + +} + + + + + diff --git a/src/com/lightstreamer/adapters/LeapMotionDemo/room/ChatRoomListener.java b/src/main/java/com/lightstreamer/adapters/LeapMotionDemo/room/ChatRoomListener.java similarity index 97% rename from src/com/lightstreamer/adapters/LeapMotionDemo/room/ChatRoomListener.java rename to src/main/java/com/lightstreamer/adapters/LeapMotionDemo/room/ChatRoomListener.java index 8319301..6dcd094 100644 --- a/src/com/lightstreamer/adapters/LeapMotionDemo/room/ChatRoomListener.java +++ b/src/main/java/com/lightstreamer/adapters/LeapMotionDemo/room/ChatRoomListener.java @@ -1,35 +1,35 @@ -/* -Copyright (c) Lightstreamer Srl - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ -package com.lightstreamer.adapters.LeapMotionDemo.room; - -import java.util.Map; - -public interface ChatRoomListener { - - public void onUserEnter(User user, String room, Object roomStatusHandle, boolean realTimeEvent); - public void onRoomListComplete(String id, Object roomStatusHandle); - public void onUserExit(User user, String room, Object roomStatusHandle); - - public void onUserStatusChange(User user, String nick, String statusId, String status, Map extra, Object userStatusHandle, boolean realTimeEvent); - - public void onUserMessage(String user, String message, String room, Object roomHandle, boolean realTimeEvent); - public void onPrivateMessage(String fromId, String toId, String message, Object userHandle); //not implemented - - public void onNewUser(String id); - public void onUserDeleted(String id); - - -} +/* +Copyright (c) Lightstreamer Srl + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ +package com.lightstreamer.adapters.LeapMotionDemo.room; + +import java.util.Map; + +public interface ChatRoomListener { + + public void onUserEnter(User user, String room, Object roomStatusHandle, boolean realTimeEvent); + public void onRoomListComplete(String id, Object roomStatusHandle); + public void onUserExit(User user, String room, Object roomStatusHandle); + + public void onUserStatusChange(User user, String nick, String statusId, String status, Map extra, Object userStatusHandle, boolean realTimeEvent); + + public void onUserMessage(String user, String message, String room, Object roomHandle, boolean realTimeEvent); + public void onPrivateMessage(String fromId, String toId, String message, Object userHandle); //not implemented + + public void onNewUser(String id); + public void onUserDeleted(String id); + + +} diff --git a/src/com/lightstreamer/adapters/LeapMotionDemo/room/Room.java b/src/main/java/com/lightstreamer/adapters/LeapMotionDemo/room/Room.java similarity index 96% rename from src/com/lightstreamer/adapters/LeapMotionDemo/room/Room.java rename to src/main/java/com/lightstreamer/adapters/LeapMotionDemo/room/Room.java index 3dcfd2b..b6bf580 100644 --- a/src/com/lightstreamer/adapters/LeapMotionDemo/room/Room.java +++ b/src/main/java/com/lightstreamer/adapters/LeapMotionDemo/room/Room.java @@ -1,79 +1,79 @@ -/* -Copyright (c) Lightstreamer Srl - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ -package com.lightstreamer.adapters.LeapMotionDemo.room; - -import java.util.HashSet; -import java.util.Iterator; -import java.util.Set; - -class Room { - - private final ChatRoom chatRoom; - private String roomId; - private Set users = new HashSet(); - private Object statusHandle = null; - private Object messageHandle = null; - - public Room(ChatRoom chatRoom, String roomId) { - this.chatRoom = chatRoom; - this.roomId = roomId; - } - - public String getId() { - return this.roomId; - } - - public void broadcastMessage(User user, String message) { - if (this.messageHandle != null) { - this.chatRoom.sendRoomMessageEvent(user,this.roomId,this.messageHandle,message); - } - } - - public boolean isEmpty() { - return users.isEmpty(); - } - - public boolean isListened() { - return this.statusHandle != null || this.messageHandle != null; - } - - public void setStatusHandle(Object roomStatusHandle) { - this.statusHandle = roomStatusHandle; - } - - public void setMessageHandle(Object messageHandle) { - this.messageHandle = messageHandle; - } - - void addUser(User user) { - this.users.add(user.getId()); - if (this.statusHandle != null) { - this.chatRoom.sendRoomStatusEvent(user, this.roomId, this.statusHandle, ChatRoom.ENTER, ChatRoom.REALTIME); - } - } - - void removeUser(User user) { - if (this.users.remove(user.getId()) && this.statusHandle != null ) { - this.chatRoom.sendRoomStatusEvent(user, this.roomId, this.statusHandle, ChatRoom.EXIT, ChatRoom.REALTIME); - } - } - - public Iterator getUsers() { - return this.users.iterator(); - } - - +/* +Copyright (c) Lightstreamer Srl + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ +package com.lightstreamer.adapters.LeapMotionDemo.room; + +import java.util.HashSet; +import java.util.Iterator; +import java.util.Set; + +class Room { + + private final ChatRoom chatRoom; + private String roomId; + private Set users = new HashSet(); + private Object statusHandle = null; + private Object messageHandle = null; + + public Room(ChatRoom chatRoom, String roomId) { + this.chatRoom = chatRoom; + this.roomId = roomId; + } + + public String getId() { + return this.roomId; + } + + public void broadcastMessage(User user, String message) { + if (this.messageHandle != null) { + this.chatRoom.sendRoomMessageEvent(user,this.roomId,this.messageHandle,message); + } + } + + public boolean isEmpty() { + return users.isEmpty(); + } + + public boolean isListened() { + return this.statusHandle != null || this.messageHandle != null; + } + + public void setStatusHandle(Object roomStatusHandle) { + this.statusHandle = roomStatusHandle; + } + + public void setMessageHandle(Object messageHandle) { + this.messageHandle = messageHandle; + } + + void addUser(User user) { + this.users.add(user.getId()); + if (this.statusHandle != null) { + this.chatRoom.sendRoomStatusEvent(user, this.roomId, this.statusHandle, ChatRoom.ENTER, ChatRoom.REALTIME); + } + } + + void removeUser(User user) { + if (this.users.remove(user.getId()) && this.statusHandle != null ) { + this.chatRoom.sendRoomStatusEvent(user, this.roomId, this.statusHandle, ChatRoom.EXIT, ChatRoom.REALTIME); + } + } + + public Iterator getUsers() { + return this.users.iterator(); + } + + } \ No newline at end of file diff --git a/src/com/lightstreamer/adapters/LeapMotionDemo/room/User.java b/src/main/java/com/lightstreamer/adapters/LeapMotionDemo/room/User.java similarity index 96% rename from src/com/lightstreamer/adapters/LeapMotionDemo/room/User.java rename to src/main/java/com/lightstreamer/adapters/LeapMotionDemo/room/User.java index 60a0279..191966f 100644 --- a/src/com/lightstreamer/adapters/LeapMotionDemo/room/User.java +++ b/src/main/java/com/lightstreamer/adapters/LeapMotionDemo/room/User.java @@ -1,130 +1,130 @@ -/* -Copyright (c) Lightstreamer Srl - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ -package com.lightstreamer.adapters.LeapMotionDemo.room; - -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.Map; -import java.util.Set; - - -public class User { - - private final ChatRoom chatRoom; - private String id; - private String nick; - private String statusId = ""; - private String status = ""; - - private Map extraProp = new HashMap(); - - private Object statusHandle = null; - private Object messagesHandle = null; - - private Set rooms = new HashSet(); - private boolean active = false; - - - User(ChatRoom chatRoom, String id) { - this.chatRoom = chatRoom; - this.id = id; - } - - public String getId() { - return this.id; - } - - Map getExtraProps() { - return this.extraProp; - } - - Object getStatusHandle() { - return this.statusHandle; - } - - void enterRoom(Room room) { - room.addUser(this); - this.rooms.add(room); - } - - void leaveRoom(Room room) { - room.removeUser(this); - this.rooms.remove(room); - } - - String getStatusId() { - return this.statusId; - } - - void setNick(String nick) { - this.nick = nick; - - if (this.statusHandle != null) { - this.chatRoom.sendUserStatusEvent(this, this.nick, null, null, null, this.statusHandle, ChatRoom.REALTIME); - } - } - - void setStatus(String status, String statusId) { - this.status = status; - this.statusId = statusId; - - if (this.statusHandle != null) { - this.chatRoom.sendUserStatusEvent(this, null, this.statusId, this.status, null, this.statusHandle, ChatRoom.REALTIME); - } - } - - public synchronized void setExtraProps(Map map) { - extraProp.putAll(map); - if (this.statusHandle != null) { - this.chatRoom.sendUserStatusEvent(this, null, null, null, map, this.statusHandle, ChatRoom.REALTIME); - } - } - - boolean isListened() { - return this.statusHandle != null || this.messagesHandle != null; - } - - String getNick() { - return this.nick; - } - - String getStatus() { - return this.status; - } - - void setStatusHandle(Object statusHandle) { - this.statusHandle = statusHandle; - } - - void setHandle(Object messagesHandle) { - this.messagesHandle = messagesHandle; - } - - Iterator getRooms() { - return rooms.iterator(); - } - - public void setActive(boolean active) { - this.active = active; - } - - public boolean isActive() { - return this.active; - } - - +/* +Copyright (c) Lightstreamer Srl + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ +package com.lightstreamer.adapters.LeapMotionDemo.room; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; + + +public class User { + + private final ChatRoom chatRoom; + private String id; + private String nick; + private String statusId = ""; + private String status = ""; + + private Map extraProp = new HashMap(); + + private Object statusHandle = null; + private Object messagesHandle = null; + + private Set rooms = new HashSet(); + private boolean active = false; + + + User(ChatRoom chatRoom, String id) { + this.chatRoom = chatRoom; + this.id = id; + } + + public String getId() { + return this.id; + } + + Map getExtraProps() { + return this.extraProp; + } + + Object getStatusHandle() { + return this.statusHandle; + } + + void enterRoom(Room room) { + room.addUser(this); + this.rooms.add(room); + } + + void leaveRoom(Room room) { + room.removeUser(this); + this.rooms.remove(room); + } + + String getStatusId() { + return this.statusId; + } + + void setNick(String nick) { + this.nick = nick; + + if (this.statusHandle != null) { + this.chatRoom.sendUserStatusEvent(this, this.nick, null, null, null, this.statusHandle, ChatRoom.REALTIME); + } + } + + void setStatus(String status, String statusId) { + this.status = status; + this.statusId = statusId; + + if (this.statusHandle != null) { + this.chatRoom.sendUserStatusEvent(this, null, this.statusId, this.status, null, this.statusHandle, ChatRoom.REALTIME); + } + } + + public synchronized void setExtraProps(Map map) { + extraProp.putAll(map); + if (this.statusHandle != null) { + this.chatRoom.sendUserStatusEvent(this, null, null, null, map, this.statusHandle, ChatRoom.REALTIME); + } + } + + boolean isListened() { + return this.statusHandle != null || this.messagesHandle != null; + } + + String getNick() { + return this.nick; + } + + String getStatus() { + return this.status; + } + + void setStatusHandle(Object statusHandle) { + this.statusHandle = statusHandle; + } + + void setHandle(Object messagesHandle) { + this.messagesHandle = messagesHandle; + } + + Iterator getRooms() { + return rooms.iterator(); + } + + public void setActive(boolean active) { + this.active = active; + } + + public boolean isActive() { + return this.active; + } + + } \ No newline at end of file