entry : tagsMap.entrySet()) {
+ if (entry.getValue().getClass() == String.class) {
+ tagsData.addProperty(entry.getKey(), (String) entry.getValue());
+ } else if (entry.getValue().getClass() == Double.class ||
+ entry.getValue().getClass() == Integer.class) {
+ tagsData.addProperty(entry.getKey(), (Number) entry.getValue());
+ } else if (entry.getValue().getClass() == Boolean.class) {
+ tagsData.addProperty(entry.getKey(), (Boolean) entry.getValue());
+ }
+ }
+
+ activityData.add("tags", tagsData);
+ }
+
+ requestData.add("activity", activityData);
+
+ /*
+ * further data...
+ */
+ if (BreinUtil.containsValue(getConfig())) {
+ if (BreinUtil.containsValue(getConfig().getApiKey())) {
+ requestData.addProperty("apiKey", getConfig().getApiKey());
+ }
+ }
+
+ requestData.addProperty("unixTimestamp", getUnixTimestamp());
+
+ // if sign is active
+ if (isSign()) {
+ requestData.addProperty("signatureType", createSignature());
+ }
+
+ final Gson gson = new GsonBuilder()
+ .setPrettyPrinting()
+ .serializeNulls()
+ .setFieldNamingPolicy(FieldNamingPolicy.UPPER_CAMEL_CASE)
+ .create();
+
+ return gson.toJson(requestData);
+ }
+
+ /**
+ * Generates the signature for the request
+ *
+ * @return full signature
+ */
+ @Override
+ public String createSignature() {
+
+ final String message = String.format("%s%d%d",
+ getBreinActivityType() == null ? "" : getBreinActivityType(),
+ getUnixTimestamp(), 1);
+ // activities.size());
+
+ return BreinUtil.generateSignature(message, getConfig().getSecret());
+ }
+}
+
diff --git a/brein-api-library-android/src/main/java/com/brein/api/BreinBase.java b/brein-api-library-android/src/main/java/com/brein/api/BreinBase.java
new file mode 100644
index 0000000..ea552b9
--- /dev/null
+++ b/brein-api-library-android/src/main/java/com/brein/api/BreinBase.java
@@ -0,0 +1,147 @@
+package com.brein.api;
+
+import com.brein.domain.BreinConfig;
+import com.brein.domain.BreinUser;
+import com.brein.engine.BreinEngine;
+
+
+/**
+ * Base Class for activity and lookup operations.
+ *
+ */
+public class BreinBase {
+
+ /**
+ * contains the User that will be used for the request
+ */
+ private BreinUser breinUser;
+
+ /**
+ * Configuration
+ */
+ private BreinConfig breinConfig;
+
+ /**
+ * contains the timestamp when the request will be generated
+ */
+ private long unixTimestamp = 0;
+
+ /**
+ * if set to yes then a secret has to bo sent
+ */
+ private boolean sign;
+
+ /**
+ * retrieves the configuration
+ *
+ * @return brein config
+ */
+ public BreinConfig getConfig() {
+ return breinConfig;
+ }
+
+ /**
+ * sets the brein config
+ * @param breinConfig object
+ */
+ public void setConfig(final BreinConfig breinConfig) {
+ this.breinConfig = breinConfig;
+ }
+
+ /**
+ * retrieves the breinuser
+ *
+ * @return breinuser
+ */
+ public BreinUser getBreinUser() {
+ return breinUser;
+ }
+
+ /**
+ * sets the brein user
+ *~
+ * @param breinUser user data
+ */
+ public BreinBase setBreinUser(final BreinUser breinUser) {
+ this.breinUser = breinUser;
+ return this;
+ }
+
+ /**
+ * returns the configured brein com.brein.engine
+ * @return brein com.brein.engine
+ */
+ public BreinEngine getBreinEngine() {
+ return null == breinConfig ? null : getConfig().getBreinEngine();
+ }
+
+ /**
+ * prepares the json request string
+ * @return empty
+ */
+ public String prepareJsonRequest() {
+
+ if (this.getUnixTimestamp() == 0) {
+ final long now = System.currentTimeMillis() / 1000L;
+ setUnixTimestamp(now);
+ }
+ return "";
+ }
+
+ /**
+ * retrieves the endpoint. this depends of the kind of BreinBase type.
+ *
+ * @return endpoint
+ */
+ public String getEndPoint() {
+ return "";
+ }
+
+ /**
+ *
+ * @return
+ */
+ public long getUnixTimestamp() {
+ return unixTimestamp;
+ }
+
+ /**
+ * retrieves the timestamp
+ *
+ * @param unixTimestamp
+ */
+ public void setUnixTimestamp(final long unixTimestamp) {
+ this.unixTimestamp = unixTimestamp;
+ }
+
+ /**
+ * retrieves the sign flag
+ *
+ * @return flag (true / false)
+ */
+ public boolean isSign() {
+ return sign;
+ }
+
+ /**
+ * sets the sign flag
+ *
+ * @param sign value to set
+ */
+ public BreinBase setSign(final boolean sign) {
+ this.sign = sign;
+ return this;
+ }
+
+ /**
+ * Initializes all values
+ */
+ public void init() {
+ breinUser = null;
+ breinConfig = null;
+ unixTimestamp = 0;
+ sign = false;
+ }
+
+
+}
diff --git a/brein-api-library-android/src/main/java/com/brein/api/BreinException.java b/brein-api-library-android/src/main/java/com/brein/api/BreinException.java
new file mode 100644
index 0000000..8c095d3
--- /dev/null
+++ b/brein-api-library-android/src/main/java/com/brein/api/BreinException.java
@@ -0,0 +1,39 @@
+package com.brein.api;
+
+/**
+ * BreinException
+ */
+public class BreinException extends RuntimeException {
+
+ /*
+ * Error Messages
+ */
+ public static final String URL_IS_NULL = "URL in request contains null";
+ public static final String REQUEST_FAILED = "Failed request!";
+ public static final String VALIDATE_ACTIVITY_OR_CONFIG_FAILED = "either activity or config not valid";
+ public static final String BREIN_BASE_VALIDATION_FAILED = "activity or lookup object is null";
+ public static final String CONFIG_VALIDATION_FAILED = "activity object is null";
+ public static final String REQUEST_BODY_FAILED = "request body is null or wrong";
+ public static final String LOOKUP_EXCEPTION = "lookup exception has occurred";
+ public static final String ENGINE_NOT_INITIALIZED = "Rest engine not initialized. You have to configure BreinConfig with a valid engine.";
+ public static final String USER_NOT_SET = "User not set.";
+ public static final String ACTIVITY_TYPE_NOT_SET = "ActivityType not set.";
+ public static final String CATEGORY_TYPE_NOT_SET = "CategoryType not set.";
+
+ /*
+ * Exception methods...
+ *
+ */
+ public BreinException(final Throwable e) {
+ super(e);
+ }
+
+ public BreinException(final String msg) {
+ super(msg);
+ }
+
+ public BreinException(final String msg, final Exception cause) {
+ super(msg, cause);
+ }
+
+}
diff --git a/brein-api-library-android/src/main/java/com/brein/api/BreinInvalidConfigurationException.java b/brein-api-library-android/src/main/java/com/brein/api/BreinInvalidConfigurationException.java
new file mode 100644
index 0000000..2728730
--- /dev/null
+++ b/brein-api-library-android/src/main/java/com/brein/api/BreinInvalidConfigurationException.java
@@ -0,0 +1,24 @@
+package com.brein.api;
+
+/**
+ * Exception in case of wrong configuration
+ */
+public class BreinInvalidConfigurationException extends RuntimeException {
+
+ /**
+ * Exception methods...
+ */
+ public BreinInvalidConfigurationException(final Throwable e) {
+ super(e);
+ }
+
+ public BreinInvalidConfigurationException(final String msg) {
+ super(msg);
+ }
+
+ public BreinInvalidConfigurationException(final String msg, final Exception cause) {
+ super(msg, cause);
+ }
+
+
+}
diff --git a/brein-api-library-android/src/main/java/com/brein/api/BreinLookup.java b/brein-api-library-android/src/main/java/com/brein/api/BreinLookup.java
new file mode 100644
index 0000000..984902d
--- /dev/null
+++ b/brein-api-library-android/src/main/java/com/brein/api/BreinLookup.java
@@ -0,0 +1,169 @@
+package com.brein.api;
+
+import com.brein.domain.BreinDimension;
+import com.brein.domain.BreinResult;
+import com.brein.domain.BreinUser;
+import com.brein.util.BreinUtil;
+import com.google.gson.FieldNamingPolicy;
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import com.google.gson.JsonArray;
+import com.google.gson.JsonObject;
+
+/**
+ * Provides the lookup functionality
+ */
+public class BreinLookup extends BreinBase implements ISecretStrategy {
+
+ /**
+ * used for lookup request
+ */
+ private BreinDimension breinDimension;
+
+ /**
+ * retrieves the Brein dimension object
+ *
+ * @return object
+ */
+ public BreinDimension getBreinDimension() {
+ return breinDimension;
+ }
+
+ /**
+ * sets the breindimension object - will be used for lookup
+ *
+ * @param breinDimension object to set
+ */
+ public BreinLookup setBreinDimension(BreinDimension breinDimension) {
+ this.breinDimension = breinDimension;
+ return this;
+ }
+
+ /**
+ * initializes the values of this instance
+ */
+ public void init() {
+ breinDimension = null;
+ }
+
+ /**
+ * resets all values of this class and base class to initial values.
+ * This will lead to empty strings or null objects
+ */
+ public void resetAllValues() {
+ // reset init values
+ init();
+
+ // reset base values (User & Config)
+ super.init();
+ }
+
+ /**
+ * Lookup implementation. For a given user (BreinUser) a lookup will be performed with the requested dimensions
+ * (BreinDimension)
+ *
+ * @param breinUser contains the breinify user
+ * @param breinDimension contains the dimensions to look after
+ * @param sign if set to true a secret will be sent as well
+ * @return response from request or null if no data can be retrieved
+ */
+ public BreinResult lookUp(final BreinUser breinUser,
+ final BreinDimension breinDimension,
+ final boolean sign) {
+
+ setBreinUser(breinUser);
+ setBreinDimension(breinDimension);
+ setSign(sign);
+
+ if (null == getBreinEngine()) {
+ throw new BreinException(BreinException.ENGINE_NOT_INITIALIZED);
+ }
+
+ return getBreinEngine().performLookUp(this);
+ }
+
+ /**
+ * prepares a JSON request for a lookup
+ *
+ * @return well formed json request
+ */
+ @Override
+ public String prepareJsonRequest() {
+
+ // call base class
+ super.prepareJsonRequest();
+
+ final JsonObject requestData = new JsonObject();
+ final BreinUser breinUser = getBreinUser();
+ if (breinUser != null) {
+ JsonObject userData = new JsonObject();
+ userData.addProperty("email", breinUser.getEmail());
+ requestData.add("user", userData);
+ }
+
+ /*
+ * Dimensions
+ */
+ if (BreinUtil.containsValue(getBreinDimension())) {
+ final JsonObject lookupData = new JsonObject();
+ final JsonArray dimensions = new JsonArray();
+ for (String field : getBreinDimension().getDimensionFields()) {
+ dimensions.add(field);
+ }
+ lookupData.add("dimensions", dimensions);
+ requestData.add("lookup", lookupData);
+ }
+
+ /*
+ * API key
+ */
+ if (BreinUtil.containsValue(getConfig().getApiKey())) {
+ requestData.addProperty("apiKey", getConfig().getApiKey());
+ }
+
+ // Unix time stamp
+ requestData.addProperty("unixTimestamp", getUnixTimestamp());
+
+ // set secret
+ if (isSign()) {
+ requestData.addProperty("signatureType", createSignature());
+ }
+
+ final Gson gson = new GsonBuilder()
+ .setPrettyPrinting()
+ .serializeNulls()
+ .setFieldNamingPolicy(FieldNamingPolicy.UPPER_CAMEL_CASE)
+ .create();
+
+ return gson.toJson(requestData);
+ }
+
+ /**
+ * retrieves the configured lookup endpoint (e.g. \lookup)
+ *
+ * @return endpoint
+ */
+ @Override
+ public String getEndPoint() {
+ return getConfig().getLookupEndpoint();
+ }
+
+ /**
+ * Creates the signature for lookup
+ *
+ * @return signature
+ */
+ @Override
+ public String createSignature() {
+
+ final String[] dimensions = getBreinDimension().getDimensionFields();
+
+ // we need the first one
+ final String message = String.format("%s%d%d",
+ dimensions == null ? 0 : dimensions[0],
+ getUnixTimestamp(),
+ dimensions == null ? 0 : dimensions.length);
+
+ return BreinUtil.generateSignature(message, getConfig().getSecret());
+ }
+}
diff --git a/brein-api-library-android/src/main/java/com/brein/api/Breinify.java b/brein-api-library-android/src/main/java/com/brein/api/Breinify.java
new file mode 100644
index 0000000..0125890
--- /dev/null
+++ b/brein-api-library-android/src/main/java/com/brein/api/Breinify.java
@@ -0,0 +1,199 @@
+package com.brein.api;
+
+import com.brein.domain.BreinConfig;
+import com.brein.domain.BreinDimension;
+import com.brein.domain.BreinResult;
+import com.brein.domain.BreinUser;
+import com.brein.util.BreinUtil;
+
+/**
+ * Static Implementation of Breinify activity & lookup calls
+ */
+public class Breinify {
+
+ /**
+ * contains the current version of the usage library
+ */
+ private static final String VERSION = "1.0.0-snapshot";
+
+ /**
+ * contains the configuration
+ */
+ private static BreinConfig config;
+
+ /**
+ * contains the activity object
+ */
+ private static final BreinActivity breinActivity = new BreinActivity();
+
+ /**
+ * contains the lookup object
+ */
+ private static final BreinLookup breinLookup = new BreinLookup();
+
+ /**
+ * sets the configuration
+ *
+ * @param breinConfig config object
+ */
+ public static void setConfig(final BreinConfig breinConfig) {
+ config = breinConfig;
+ breinActivity.setConfig(breinConfig);
+ breinLookup.setConfig(breinConfig);
+ }
+
+ /**
+ * gets the config
+ *
+ * @return config
+ */
+ public static BreinConfig getConfig() {
+ return config;
+ }
+
+ /**
+ * returns the version
+ *
+ * @return version
+ */
+ public String getVersion() {
+ return VERSION;
+ }
+
+ /**
+ * @return breinActivity instance
+ */
+ public static BreinActivity getBreinActivity() {
+ return breinActivity;
+ }
+
+ /**
+ * @return breinLookup instance
+ */
+ public static BreinLookup getBreinLookup() {
+ return breinLookup;
+ }
+
+ /**
+ * Sends an activity to the engine utilizing the API. The call is done asynchronously as a POST request. It is
+ * important that a valid API-key is configured prior to using this function.
+ *
+ * This request is asynchronous.
+ *
+ * @param user a plain object specifying the user information the activity belongs to
+ * @param activityType the type of the activity collected, i.e., one of search, login, logout, addToCart,
+ * removeFromCart, checkOut, selectProduct, or other. if not specified, the default other will
+ * be used
+ * @param categoryType the category of the platform/service/products, i.e., one of apparel, home, education, family,
+ * food, health, job, services, or other
+ * @param description a string with further information about the activity performed
+ * @param sign a boolean value specifying if the call should be signed
+ */
+ public static void activity(final BreinUser user,
+ final String activityType,
+ final String categoryType,
+ final String description,
+ final boolean sign) {
+ breinActivity.setBreinUser(user);
+ breinActivity.setBreinActivityType(activityType);
+ breinActivity.setBreinCategoryType(categoryType);
+ breinActivity.setDescription(description);
+ breinActivity.setSign(sign);
+
+ // invoke the request, "this" has all necessary information
+ if (null == breinActivity.getBreinEngine()) {
+ throw new BreinException(BreinException.ENGINE_NOT_INITIALIZED);
+ }
+ breinActivity.getBreinEngine().sendActivity(breinActivity);
+ }
+
+ /**
+ * Sends an activity to the engine utilizing the API. The call is done asynchronously as a POST request. It is
+ * important that a valid API-key is configured prior to using this function.
+ * Furthermore it uses the internal instance of BreinActivity. In order to use this method correctly you have
+ * to do the following:
+ *
+ * // retrieve BreinActivity instance from Breinify class
+ * BreinActivity breinActivity = Breinify.getBreinActivity();
+ *
+ * // set methods as desired to breinActivity (for instance)
+ * breinActivity.setBreinUser(new BreinUser("user.name@email.com");
+ * ...
+ *
+ * // invoke this method
+ * Breinify.activity();
+ *
+ *
+ * This request is asynchronous.
+ */
+ public static void activity() {
+
+ // use the own instance
+ final BreinActivity breinActivity = getBreinActivity();
+
+ if (breinActivity.getBreinUser() == null) {
+ throw new BreinException(BreinException.USER_NOT_SET);
+ }
+
+ if (breinActivity.getBreinActivityType() == null) {
+ throw new BreinException(BreinException.ACTIVITY_TYPE_NOT_SET);
+ }
+
+ if (breinActivity.getBreinCategoryType() == null) {
+ // check if there is an default category set
+ final String defaultCategory = getConfig().getDefaultCategory();
+ if (BreinUtil.containsValue(defaultCategory)) {
+ breinActivity.setBreinCategoryType(defaultCategory);
+ }
+ }
+
+ if (null == breinActivity.getBreinEngine()) {
+ throw new BreinException(BreinException.ENGINE_NOT_INITIALIZED);
+ }
+
+ breinActivity.getBreinEngine().sendActivity(breinActivity);
+ }
+
+ /**
+ * Retrieves a lookup result from the engine. The function needs a valid API-key to be configured to succeed.
+ *
+ * This request is synchronous.
+ *
+ * @param user a plain object specifying information about the user to retrieve data for.
+ * @param dimension an object (with an array) containing the names of the dimensions to lookup.
+ * @param sign a boolean value specifying if the call should be signed.
+ * @return response from request wrapped in an object called BreinResponse
+ */
+ public static BreinResult lookup(final BreinUser user,
+ final BreinDimension dimension,
+ final boolean sign) {
+ return lookup(breinLookup, user, dimension, sign);
+ }
+
+ public static BreinResult lookup(final BreinLookup breinLookup,
+ final BreinUser user,
+ final BreinDimension dimension,
+ final boolean sign) {
+ breinLookup.setBreinUser(user);
+ breinLookup.setBreinDimension(dimension);
+ breinLookup.setSign(sign);
+
+ /*
+ * invoke the lookup request
+ */
+ if (null == breinLookup.getBreinEngine()) {
+ throw new BreinException(BreinException.ENGINE_NOT_INITIALIZED);
+ }
+ return breinLookup.getBreinEngine().performLookUp(breinLookup);
+ }
+
+ /**
+ * Shutdown Breinify services
+ */
+ public static void shutdown() {
+ if (getConfig() != null) {
+ getConfig().shutdownEngine();
+ }
+ }
+
+}
diff --git a/brein-api-library-android/src/main/java/com/brein/api/BreinifyExecutor.java b/brein-api-library-android/src/main/java/com/brein/api/BreinifyExecutor.java
new file mode 100644
index 0000000..fb87207
--- /dev/null
+++ b/brein-api-library-android/src/main/java/com/brein/api/BreinifyExecutor.java
@@ -0,0 +1,173 @@
+package com.brein.api;
+
+import com.brein.domain.BreinConfig;
+import com.brein.domain.BreinDimension;
+import com.brein.domain.BreinResult;
+import com.brein.domain.BreinUser;
+
+/**
+ * Static Implementation of Breinify activity & lookup calls
+ */
+public class BreinifyExecutor {
+
+ /**
+ * contains the current version of the library
+ */
+ private static final String VERSION = "1.0.0-snapshot";
+ /**
+ * contains the activity object
+ */
+ private final BreinActivity breinActivity = new BreinActivity();
+ /**
+ * contains the lookup object
+ */
+ private final BreinLookup breinLookup = new BreinLookup();
+ /**
+ * contains the configuration
+ */
+ private BreinConfig config;
+
+ /**
+ * gets the config
+ *
+ * @return config
+ */
+ public BreinConfig getConfig() {
+ return config;
+ }
+
+ /**
+ * sets the configuration
+ *
+ * @param breinConfig config object
+ */
+ public void setConfig(final BreinConfig breinConfig) {
+ config = breinConfig;
+ breinActivity.setConfig(breinConfig);
+ breinLookup.setConfig(breinConfig);
+ }
+
+ /**
+ * returns the version
+ *
+ * @return version
+ */
+ public String getVersion() {
+ return VERSION;
+ }
+
+ /**
+ * Retrieves the instance of the brein activity
+ *
+ * @return breinActivity object
+ */
+ public BreinActivity getBreinActivity() {
+ return breinActivity;
+ }
+
+ /**
+ * Retrieves the instance of the brein lookup
+ *
+ * @return breinLookup object
+ */
+ public BreinLookup getBreinLookup() {
+ return breinLookup;
+ }
+
+ /**
+ * Sends an activity to the com.brein.engine utilizing the API. The call is done asynchronously as a POST request. It is
+ * important that a valid API-key is configured prior to using this function.
+ *
+ * This request is asynchronous.
+ *
+ * @param user a plain object specifying the user information the activity belongs to
+ * @param activityType the type of the activity collected, i.e., one of search, login, logout, addToCart,
+ * removeFromCart, checkOut, selectProduct, or other. if not specified, the default other will
+ * be used
+ * @param category the category of the platform/service/products, i.e., one of apparel, home, education, family,
+ * food, health, job, services, or other
+ * @param description a string with further information about the activity performed
+ * @param sign a boolean value specifying if the call should be signed
+ */
+ public void activity(final BreinUser user,
+ final String activityType,
+ final String category,
+ final String description,
+ final boolean sign) {
+
+ // set the appropriate configuration
+ applyConfiguration();
+
+ Breinify.activity(user, activityType, category, description, sign);
+ }
+
+ /**
+ * This is necessary because the configuration from
+ * class BreinifyExecutor needs to be transferred to
+ * class Breinify in order to invoke the activity and
+ * lookup calls within class Breinify.
+ */
+ public void applyConfiguration() {
+ Breinify.getBreinActivity().setConfig(getConfig());
+ }
+
+ /**
+ * Sends an activity to the engine utilizing the API. The call is done asynchronously as a POST request. It is
+ * important that a valid API-key is configured prior to using this function.
+ *
+ * This request is asynchronous.
+ *
+ */
+ public void activity() {
+
+ // set the appropriate configuration
+ applyConfiguration();
+
+ if (breinActivity.getBreinUser() == null) {
+ throw new BreinException(BreinException.USER_NOT_SET);
+ }
+
+ if (breinActivity.getBreinActivityType() == null) {
+ throw new BreinException(BreinException.ACTIVITY_TYPE_NOT_SET);
+ }
+
+ if (breinActivity.getBreinCategoryType() == null) {
+ throw new BreinException(BreinException.CATEGORY_TYPE_NOT_SET);
+ }
+
+ if (null == breinActivity.getBreinEngine()) {
+ throw new BreinException(BreinException.ENGINE_NOT_INITIALIZED);
+ }
+
+ activity(breinActivity.getBreinUser(),
+ breinActivity.getBreinActivityType(),
+ breinActivity.getBreinCategoryType(),
+ breinActivity.getDescription(),
+ breinActivity.isSign());
+ }
+
+ /**
+ * Retrieves a lookup result from the com.brein.engine. The function needs a valid API-key to be configured to succeed.
+ *
+ * This request is synchronous.
+ *
+ * @param user a plain object specifying information about the user to retrieve data for.
+ * @param dimension an object (with an array) containing the names of the dimensions to lookup.
+ * @param sign a boolean value specifying if the call should be signed.
+ *
+ * @return response from request wrapped in an object called BreinResponse
+ */
+ public BreinResult lookup(final BreinUser user,
+ final BreinDimension dimension,
+ final boolean sign) {
+
+ return Breinify.lookup(breinLookup, user, dimension, sign);
+ }
+
+ /**
+ * Shutdown Breinify services
+ */
+ public void shutdown() {
+ this.config.shutdownEngine();
+ }
+}
diff --git a/brein-api-library-android/src/main/java/com/brein/api/ISecretStrategy.java b/brein-api-library-android/src/main/java/com/brein/api/ISecretStrategy.java
new file mode 100644
index 0000000..21f206e
--- /dev/null
+++ b/brein-api-library-android/src/main/java/com/brein/api/ISecretStrategy.java
@@ -0,0 +1,15 @@
+package com.brein.api;
+
+/**
+ * Base class for the secret strategy
+ */
+public interface ISecretStrategy {
+
+ /**
+ * Creates the appropriate signature that is part of the request to
+ * the Breinify server.
+ *
+ * @return creates signature
+ */
+ String createSignature();
+}
diff --git a/brein-api-library-android/src/main/java/com/brein/domain/BreinActivityType.java b/brein-api-library-android/src/main/java/com/brein/domain/BreinActivityType.java
new file mode 100644
index 0000000..e24abae
--- /dev/null
+++ b/brein-api-library-android/src/main/java/com/brein/domain/BreinActivityType.java
@@ -0,0 +1,20 @@
+package com.brein.domain;
+
+/**
+ * The type of the activity collected, i.e., one of search, login, logout, addToCart, removeFromCart, checkOut,
+ * selectProduct, or other.
+ */
+public class BreinActivityType {
+
+ // pre-defined activity types
+ public static final String SEARCH = "search";
+ public static final String LOGIN = "login";
+ public static final String LOGOUT = "logout";
+ public static final String ADD_TO_CART = "addToCart";
+ public static final String REMOVE_FROM_CART = "removeFromCart";
+ public static final String SELECT_PRODUCT = "selectProduct";
+ public static final String CHECKOUT = "checkOut";
+ public static final String PAGEVISIT = "pageVisit";
+ public static final String OTHER = "other";
+
+}
diff --git a/brein-api-library-android/src/main/java/com/brein/domain/BreinCategoryType.java b/brein-api-library-android/src/main/java/com/brein/domain/BreinCategoryType.java
new file mode 100644
index 0000000..5afe47f
--- /dev/null
+++ b/brein-api-library-android/src/main/java/com/brein/domain/BreinCategoryType.java
@@ -0,0 +1,20 @@
+package com.brein.domain;
+
+/**
+ * The categoryType of the platform/service/products, i.e., one of apparel,
+ * home, education, family, food, health, job, services, or other.
+ */
+public class BreinCategoryType {
+
+ // pre-defined category types
+ public static final String APPAREL = "apparel";
+ public static final String HOME = "home";
+ public static final String EDUCATION = "education";
+ public static final String FAMILY = "family";
+ public static final String FOOD = "food";
+ public static final String HEALTH = "health";
+ public static final String JOB = "job";
+ public static final String SERVICES = "services";
+ public static final String OTHER = "other";
+
+}
\ No newline at end of file
diff --git a/brein-api-library-android/src/main/java/com/brein/domain/BreinConfig.java b/brein-api-library-android/src/main/java/com/brein/domain/BreinConfig.java
new file mode 100755
index 0000000..382ef07
--- /dev/null
+++ b/brein-api-library-android/src/main/java/com/brein/domain/BreinConfig.java
@@ -0,0 +1,395 @@
+package com.brein.domain;
+
+import com.brein.api.BreinInvalidConfigurationException;
+import com.brein.api.BreinifyExecutor;
+import com.brein.engine.BreinEngine;
+import com.brein.engine.BreinEngineType;
+import com.brein.util.BreinUtil;
+
+/**
+ * Contains Breinify Endpoint configuration
+ */
+public class BreinConfig {
+
+ /**
+ * default endpoint of activity
+ */
+ public static final String DEFAULT_ACTIVITY_ENDPOINT = "/activity";
+
+ /**
+ * default endpoint of lookup
+ */
+ public static final String DEFAULT_LOOKUP_ENDPOINT = "/lookup";
+
+ /**
+ * default connection timeout
+ */
+ public static final long DEFAULT_CONNECTION_TIMEOUT = 1000;
+
+ /**
+ * default socket timeout
+ */
+ public static final long DEFAULT_SOCKET_TIMEOUT = 6000;
+
+ /**
+ * default breinify base url
+ */
+ public static final String DEFAULT_BASE_URL = "https://api.breinify.com";
+
+ /**
+ * Logger instance
+ */
+ // private static final Logger LOG = LoggerFactory.getLogger(BreinConfig.class);
+
+ /**
+ * BASE URL
+ */
+ private String baseUrl = DEFAULT_BASE_URL;
+
+ /**
+ * contains the com.brein.api key
+ */
+ private String apiKey;
+
+ /**
+ * Default REST client
+ */
+ private BreinEngineType restEngineType = BreinEngineType.HTTP_URL_CONNECTION_ENGINE;
+
+ /**
+ * contains the activity endpoint (default = ACTIVITY_ENDPOINT)
+ */
+ private String activityEndpoint = DEFAULT_ACTIVITY_ENDPOINT;
+
+ /**
+ * contains the lookup endpoint (default = LOOKUP_ENDPOINT)
+ */
+ private String lookupEndpoint = DEFAULT_LOOKUP_ENDPOINT;
+
+ /**
+ * connection timeout
+ */
+ private long connectionTimeout = DEFAULT_CONNECTION_TIMEOUT;
+
+ /**
+ * Engine with default value
+ */
+ private BreinEngine breinEngine;
+
+ /**
+ * socket timeout
+ */
+ private long socketTimeout = DEFAULT_SOCKET_TIMEOUT;
+
+ /**
+ * default category
+ */
+ private String defaultCategory = "";
+
+ /**
+ * contains the secret
+ */
+ private String secret;
+
+ /**
+ * @param apiKey contains the Breinify com.brein.api-key
+ * @param baseUrl contains the base url
+ */
+ public BreinConfig(final String apiKey,
+ final String baseUrl) {
+
+ setApiKey(apiKey);
+ setBaseUrl(baseUrl);
+ setRestEngineType(BreinEngineType.NO_ENGINE);
+ }
+
+ /**
+ * Configuration object
+ *
+ * @param apiKey contains the Breinify com.brein.api-key
+ * @param baseUrl contains the base url
+ * @param breinEngineType selected com.brein.engine
+ */
+ public BreinConfig(final String apiKey,
+ final String baseUrl,
+ final BreinEngineType breinEngineType) {
+
+ setApiKey(apiKey);
+ setBaseUrl(baseUrl);
+ setRestEngineType(breinEngineType);
+ initEngine();
+ }
+
+ /**
+ * Empty Ctor - necessary
+ */
+ public BreinConfig() {
+ }
+
+ /**
+ * initializes the rest client
+ */
+ public void initEngine() {
+ breinEngine = new BreinEngine(getRestEngineType());
+ }
+
+ /**
+ * builder method - based on th configuration an universal executer
+ * will be created.
+ *
+ * @return new created executer
+ */
+ public BreinifyExecutor build() {
+
+ BreinifyExecutor breinifyExecutor = new BreinifyExecutor();
+ breinifyExecutor.setConfig(this);
+
+ initEngine();
+
+ return breinifyExecutor;
+ }
+
+ /**
+ * retrieves the base url
+ *
+ * @return base url
+ */
+ public String getBaseUrl() {
+ return baseUrl;
+ }
+
+ /**
+ * set the base url of the breinify backend and will check
+ * if the URL is valid.
+ *
+ * @param baseUrl contains the url
+ * @return the config object itself
+ */
+ public BreinConfig setBaseUrl(final String baseUrl) {
+ this.baseUrl = baseUrl;
+ checkBaseUrl(baseUrl);
+ return this;
+ }
+
+ /**
+ * checks if the url is valid. If not a BreinInvalidConfigurationException will
+ * be thrown.
+ *
+ * @param baseUrl url to check
+ */
+ public void checkBaseUrl(final String baseUrl) throws BreinInvalidConfigurationException {
+
+ if (!isUrlValid(baseUrl)) {
+ final String msg = "BreinConfig issue. Value for BaseUrl is not valid. Value is: "
+ + baseUrl;
+ // Log.d("BreinUtil", msg);
+
+ throw new BreinInvalidConfigurationException(msg);
+ }
+ }
+
+ /**
+ * retrieves rest type client
+ *
+ * @return configured rest type client
+ */
+ public BreinEngineType getRestEngineType() {
+ return restEngineType;
+ }
+
+ /**
+ * set rest type client
+ *
+ * @param restEngineType of the rest impl
+ * @return the config object itself
+ */
+ public BreinConfig setRestEngineType(final BreinEngineType restEngineType) {
+ this.restEngineType = restEngineType;
+ return this;
+ }
+
+ /**
+ * returns the configured brein com.brein.engine for the rest calls
+ *
+ * @return brein com.brein.engine
+ */
+ public BreinEngine getBreinEngine() {
+ return breinEngine;
+ }
+
+ /**
+ * retrieves the apikey
+ *
+ * @return apikey
+ */
+ public String getApiKey() {
+ return apiKey;
+ }
+
+ /**
+ * sets the apikey
+ *
+ * @param apiKey the apikey
+ * @return the config object itself
+ */
+ public BreinConfig setApiKey(final String apiKey) {
+
+ if (BreinUtil.containsValue(apiKey)) {
+ this.apiKey = apiKey;
+ }
+ return this;
+ }
+
+ /**
+ * retrieves the url for the post requests
+ *
+ * @return base url
+ */
+ public String getUrl() {
+ return baseUrl;
+ }
+
+ /**
+ * retrieves the configured timeout values
+ *
+ * @return connection time out
+ */
+ public long getConnectionTimeout() {
+ return connectionTimeout;
+ }
+
+ /**
+ * set the connection timeout
+ *
+ * @param connectionTimeout value
+ */
+ public void setConnectionTimeout(final long connectionTimeout) {
+ this.connectionTimeout = connectionTimeout;
+ }
+
+ /**
+ * socket timeout values
+ *
+ * @return connection time out values
+ */
+ public long getSocketTimeout() {
+ return socketTimeout;
+ }
+
+ /**
+ * set the socket timeout
+ *
+ * @param socketTimeout value
+ */
+ public void setSocketTimeout(final long socketTimeout) {
+ this.socketTimeout = socketTimeout;
+ }
+
+ /**
+ * retrieves the activity endpoint
+ *
+ * @return endpoint
+ */
+ public String getActivityEndpoint() {
+ return activityEndpoint;
+ }
+
+ /**
+ * sets the activity endpoint
+ *
+ * @param activityEndpoint endpoint
+ * @return the config object itself
+ */
+ public BreinConfig setActivityEndpoint(final String activityEndpoint) {
+ this.activityEndpoint = activityEndpoint;
+ return this;
+ }
+
+ /**
+ * retrieves the lookup endpoint
+ *
+ * @return lookup endpoint
+ */
+ public String getLookupEndpoint() {
+ return lookupEndpoint;
+ }
+
+ /**
+ * sets the lookup endpoint
+ *
+ * @param lookupEndpoint endpoint
+ * @return the config object itself
+ */
+ public BreinConfig setLookupEndpoint(final String lookupEndpoint) {
+ this.lookupEndpoint = lookupEndpoint;
+ return this;
+ }
+
+ /**
+ * returns the configured secret
+ *
+ * @return raw secret
+ */
+ public String getSecret() {
+ return secret;
+ }
+
+ /**
+ * set the secret
+ *
+ * @param secret raw secret
+ */
+ public BreinConfig setSecret(final String secret) {
+ this.secret = secret;
+ return this;
+ }
+
+ /**
+ * returns the default category (if set)
+ *
+ * @return default category
+ */
+ public String getDefaultCategory() {
+ return defaultCategory;
+ }
+
+ /**
+ * sets the default category
+ *
+ * @param defaultCategory default to set
+ */
+ public BreinConfig setDefaultCategory(final String defaultCategory) {
+ this.defaultCategory = defaultCategory;
+ return this;
+ }
+
+ /**
+ * invokes the termination of the rest com.brein.engine.
+ * Depending of the configured com.brein.engine additional threads might
+ * have been allocated and this will close those threads.
+ */
+ public void shutdownEngine() {
+
+ // check valid objects
+ if (this.breinEngine == null) {
+ return;
+ }
+
+ if (this.breinEngine.getRestEngine() == null) {
+ return;
+ }
+
+ // invoke termination of the com.brein.engine
+ this.breinEngine.getRestEngine().terminate();
+ }
+
+ /**
+ * Validates if the URL is correct.
+ *
+ * @param url to check
+ * @return true if ok otherwise false
+ */
+ public boolean isUrlValid(final String url) {
+ return BreinUtil.isUrlValid(url);
+ }
+}
diff --git a/brein-api-library-android/src/main/java/com/brein/domain/BreinDimension.java b/brein-api-library-android/src/main/java/com/brein/domain/BreinDimension.java
new file mode 100644
index 0000000..67bf240
--- /dev/null
+++ b/brein-api-library-android/src/main/java/com/brein/domain/BreinDimension.java
@@ -0,0 +1,39 @@
+package com.brein.domain;
+
+/**
+ * Contains the Dimension to ask for
+ */
+public class BreinDimension {
+
+ /**
+ * contains the array of dimensions
+ */
+ private String[] dimensionFields;
+
+ /**
+ * Ctor with array of requested dimensions
+ *
+ * @param dimensionFields array of dimensions
+ */
+ public BreinDimension(final String... dimensionFields) {
+ this.dimensionFields = dimensionFields;
+ }
+
+ /**
+ * retrieve dimension array
+ *
+ * @return dimension array
+ */
+ public String[] getDimensionFields() {
+ return dimensionFields;
+ }
+
+ /**
+ * sets dimension array
+ *
+ * @param dimensionFields data of dimensions
+ */
+ public void setDimensionFields(final String... dimensionFields) {
+ this.dimensionFields = dimensionFields;
+ }
+}
diff --git a/brein-api-library-android/src/main/java/com/brein/domain/BreinResult.java b/brein-api-library-android/src/main/java/com/brein/domain/BreinResult.java
new file mode 100644
index 0000000..073a67f
--- /dev/null
+++ b/brein-api-library-android/src/main/java/com/brein/domain/BreinResult.java
@@ -0,0 +1,47 @@
+package com.brein.domain;
+
+import com.google.gson.Gson;
+
+import java.util.Map;
+
+/**
+ * Contains the result of an Brein Request when invoking a
+ * lookup
+ */
+public class BreinResult {
+
+ /**
+ * contains the collected data as map
+ */
+ private final Map map;
+
+ /**
+ * creates a brein result object
+ * @param jsonResponse as json string
+ */
+ @SuppressWarnings("unchecked")
+ public BreinResult(final String jsonResponse) {
+ map = new Gson().fromJson(jsonResponse, Map.class);
+ }
+
+ /**
+ * retrieves the object according to the requested key
+ * @param key to look for
+ * @param Object
+ * @return Object retrieved
+ */
+ @SuppressWarnings("unchecked")
+ public T get(final String key) {
+ return (T) map.get(key);
+ }
+
+ /**
+ * checks if key exists in map
+ * @param key to check
+ * @return true or false
+ */
+ public boolean has(final String key) {
+ return get(key) != null;
+ }
+
+}
diff --git a/brein-api-library-android/src/main/java/com/brein/domain/BreinUser.java b/brein-api-library-android/src/main/java/com/brein/domain/BreinUser.java
new file mode 100644
index 0000000..c5eefcc
--- /dev/null
+++ b/brein-api-library-android/src/main/java/com/brein/domain/BreinUser.java
@@ -0,0 +1,330 @@
+package com.brein.domain;
+
+/**
+ * A plain object specifying the user information the activity belongs to
+ */
+public class BreinUser {
+
+ /**
+ * user email
+ */
+ private String email;
+
+ /**
+ * user first name
+ */
+ private String firstName;
+
+ /**
+ * user last name
+ */
+ private String lastName;
+
+ /**
+ * user date of birth
+ */
+ private String dateOfBirth;
+
+ /**
+ * user imei number
+ */
+ private String imei;
+
+ /**
+ * user deviceId
+ */
+ private String deviceId;
+
+ /**
+ * user sessionId
+ */
+ private String sessionId;
+
+ /**
+ * contains the userAgent in additional part
+ */
+ private String userAgent;
+
+ /**
+ * contains the referrer in additional part
+ */
+ private String referrer;
+
+ /**
+ * contains the url in additional part
+ */
+ private String url;
+
+ /**
+ * contains the url in additional part
+ */
+ private String ipAddress;
+
+ /**
+ * create a brein user with mandatory field email.
+ *
+ * @param email of the user
+ */
+ public BreinUser(final String email) {
+ setEmail(email);
+ }
+
+ /**
+ * create a brein user
+ *
+ */
+ public BreinUser() {
+ }
+
+ /**
+ * setter & getter of the properties
+ */
+ public String getEmail() {
+ return email;
+ }
+
+ /**
+ * sets the email of the user
+ * @param email to set (will not be checked)
+ * @return this -> allows chaining
+ */
+ public BreinUser setEmail(final String email) {
+ this.email = email;
+ return this;
+ }
+
+ /**
+ * Retrieves the first name of the user
+ * @return first name
+ */
+ public String getFirstName() {
+ return firstName;
+ }
+
+ /**
+ * set the first name of the user
+ * @param firstName name to set
+ * @return this -> allows chaining
+ */
+ public BreinUser setFirstName(final String firstName) {
+ this.firstName = firstName;
+ return this;
+ }
+
+ /**
+ * Retrieves the last name of the user
+ * @return last name
+ */
+ public String getLastName() {
+ return lastName;
+ }
+
+ /**
+ * set the last name of the user
+ * @param lastName last name
+ * @return thi -> allows chaining
+ */
+ public BreinUser setLastName(final String lastName) {
+ this.lastName = lastName;
+ return this;
+ }
+
+ /**
+ * retrieves the additional userAgent value
+ *
+ * @return value
+ */
+ public String getUserAgent() {
+ return userAgent;
+ }
+
+ /**
+ * sets the additional user agent value
+ *
+ * @param userAgent value
+ */
+ public BreinUser setUserAgent(final String userAgent) {
+ this.userAgent = userAgent;
+ return this;
+ }
+
+ /**
+ * retrieves the ipAddress (additional part)
+ * @return ipAddress
+ */
+ public String getIpAddress() {
+ return ipAddress;
+ }
+
+ /**
+ * sets the ipAddress
+ * @param ipAddress value
+ */
+ public BreinUser setIpAddress(final String ipAddress) {
+ this.ipAddress = ipAddress;
+ return this;
+ }
+
+ /**
+ * retrieves the additional referrer value
+ *
+ * @return value
+ */
+ public String getReferrer() {
+ return referrer;
+ }
+
+ /**
+ * sets the additional referrer value
+ *
+ * @param referrer value
+ */
+ public BreinUser setReferrer(final String referrer) {
+ this.referrer = referrer;
+ return this;
+ }
+
+ /**
+ * retrieves the additional url
+ *
+ * @return value
+ */
+ public String getUrl() {
+ return url;
+ }
+
+ /**
+ * sets the additional url
+ *
+ * @param url value
+ * @return self
+ */
+ public BreinUser setUrl(final String url) {
+ this.url = url;
+ return this;
+ }
+
+ /**
+ * Returns the date of birth
+ * @return date of birth
+ */
+ public String getDateOfBirth() {
+ return dateOfBirth;
+ }
+
+ /**
+ * Set's the date of birth
+ * There is no check if the month - day combination is valid, only
+ * the range for day, month and year will be checked
+ *
+ * @param month (1..12)
+ * @param day (1..31)
+ * @param year (1900..2100)
+ * @return this -> allows chaining
+ */
+ public BreinUser setDateOfBirth(final int month, final int day, final int year) {
+
+ if (month >= 1 && month <= 12) {
+ if (day >= 1 && day <= 31) {
+ if (year >= 1900 && year <= 2100) {
+ this.dateOfBirth = Integer.toString(month)
+ + "/"
+ + Integer.toString(day)
+ + "/"
+ + Integer.toString(year);
+ }
+ }
+ }
+ return this;
+ }
+
+ /**
+ * resets the dateOfBirth to an empty value
+ */
+ public void resetDateOfBirth() {
+ this.dateOfBirth = "";
+ }
+
+ /**
+ * Retrieves imei (International Mobile Equipment Identity)
+ * @return serial number as string
+ */
+ public String getImei() {
+ return imei;
+ }
+
+ /**
+ * Sets the imei number
+ * @param imei number
+ * @return this -> allows chaining
+ */
+ public BreinUser setImei(final String imei) {
+ this.imei = imei;
+ return this;
+ }
+
+ /**
+ * retrieves the deviceid
+ * @return device id
+ */
+ public String getDeviceId() {
+ return deviceId;
+ }
+
+ /**
+ * sets the device id
+ * @param deviceId the id of the device
+ * @return this -> allows chaining
+ */
+ public BreinUser setDeviceId(final String deviceId) {
+ this.deviceId = deviceId;
+ return this;
+ }
+
+ /**
+ * retrieves the session id
+ * @return id of the session
+ */
+ public String getSessionId() {
+ return sessionId;
+ }
+
+ /**
+ * sets the sessionid
+ * @param sessionId id of the session
+ * @return this -> allows chaining
+ */
+ public BreinUser setSessionId(final String sessionId) {
+ this.sessionId = sessionId;
+ return this;
+ }
+
+ /**
+ * provides a nicer output of the user details
+ * @return output
+ */
+ @Override
+ public String toString() {
+
+ return "BreinUser details: "
+ + "\n"
+ + " name: "
+ + (this.firstName == null ? "n/a" : this.firstName)
+ + " "
+ + (this.lastName == null ? "n/a" : this.lastName)
+ + " email: "
+ + (this.email == null ? "n/a" : this.email)
+ + " dateOfBirth: "
+ + (this.dateOfBirth == null ? "n/a" : this.dateOfBirth)
+ + "\n"
+ + " imei: "
+ + (this.imei == null ? "n/a" : this.imei)
+ + " deviceId: "
+ + (this.deviceId == null ? "n/a" : this.deviceId)
+ + "\n"
+ + " sessionId: "
+ + (this.sessionId == null ? "n/a" : this.sessionId);
+
+ }
+}
+
diff --git a/brein-api-library-android/src/main/java/com/brein/engine/BreinEngine.java b/brein-api-library-android/src/main/java/com/brein/engine/BreinEngine.java
new file mode 100644
index 0000000..b4aea72
--- /dev/null
+++ b/brein-api-library-android/src/main/java/com/brein/engine/BreinEngine.java
@@ -0,0 +1,90 @@
+package com.brein.engine;
+
+import com.brein.api.BreinActivity;
+import com.brein.api.BreinException;
+import com.brein.api.BreinLookup;
+import com.brein.domain.BreinConfig;
+import com.brein.domain.BreinResult;
+
+/**
+ * Creates the Rest Engine (currently only unirest) and provides the methods to
+ * invoke activity and lookup calls
+ */
+public class BreinEngine {
+
+ /**
+ * creation of rest com.brein.engine. I know that this implementation only allows UNIREST and nothing
+ * else. Configuration of parameter needs to be done.
+ */
+ private IRestEngine restEngine = null;
+
+ /**
+ * Creates the com.brein.engine
+ *
+ * @param engineType (e.g. Jersey...)
+ */
+ public BreinEngine(final BreinEngineType engineType) {
+
+ switch (engineType) {
+ case HTTP_URL_CONNECTION_ENGINE:
+ restEngine = new HttpUrlRestEngine();
+ break;
+
+ /*
+ case JERSEY_ENGINE:
+ restEngine = new JerseyRestEngine();
+ break;
+
+ */
+ // case VOLLEY:
+
+ default:
+ throw new BreinException("no rest engine specified!");
+ }
+ }
+
+ /**
+ * sends an activity to the breinify server
+ *
+ * @param activity data
+ */
+ public void sendActivity(final BreinActivity activity) {
+ if (activity != null) {
+ restEngine.doRequest(activity);
+ }
+ }
+
+ /**
+ * performs a lookup. This will be delegated to the
+ * configured restEngine.
+ *
+ * @param breinLookup contains the appropriate data for the lookup
+ * request
+ * @return if succeeded a BreinResponse object or null
+ */
+ public BreinResult performLookUp(final BreinLookup breinLookup) {
+ if (breinLookup != null) {
+ return restEngine.doLookup(breinLookup);
+ }
+
+ return null;
+ }
+
+ /**
+ * returns the brein com.brein.engine
+ *
+ * @return com.brein.engine itself
+ */
+ public IRestEngine getRestEngine() {
+ return restEngine;
+ }
+
+ /**
+ * configuration of com.brein.engine
+ *
+ * @param breinConfig configuration object
+ */
+ public void configure(final BreinConfig breinConfig) {
+ restEngine.configure(breinConfig);
+ }
+}
diff --git a/brein-api-library-android/src/main/java/com/brein/engine/BreinEngineType.java b/brein-api-library-android/src/main/java/com/brein/engine/BreinEngineType.java
new file mode 100644
index 0000000..15aa596
--- /dev/null
+++ b/brein-api-library-android/src/main/java/com/brein/engine/BreinEngineType.java
@@ -0,0 +1,12 @@
+package com.brein.engine;
+
+/**
+ * Specifies the possible Engine Types
+ */
+public enum BreinEngineType {
+
+ // JERSEY_ENGINE,
+ HTTP_URL_CONNECTION_ENGINE,
+ // VOLLEY_ENGINE,
+ NO_ENGINE
+}
diff --git a/brein-api-library-android/src/main/java/com/brein/engine/HttpUrlRestEngine.java b/brein-api-library-android/src/main/java/com/brein/engine/HttpUrlRestEngine.java
new file mode 100644
index 0000000..3e787ad
--- /dev/null
+++ b/brein-api-library-android/src/main/java/com/brein/engine/HttpUrlRestEngine.java
@@ -0,0 +1,161 @@
+package com.brein.engine;
+
+import com.brein.api.BreinActivity;
+import com.brein.api.BreinException;
+import com.brein.api.BreinLookup;
+import com.brein.domain.BreinConfig;
+import com.brein.domain.BreinResult;
+import com.brein.util.BreinUtil;
+
+import java.io.InputStream;
+import java.io.PrintWriter;
+import java.net.HttpURLConnection;
+import java.net.URL;
+
+/**
+ * could be the jersey rest com.brein.engine implementation
+ */
+public class HttpUrlRestEngine implements IRestEngine {
+
+ /**
+ * Logger
+ */
+ // private static final Logger LOG = LoggerFactory.getLogger(JerseyRestEngine.class);
+
+ /**
+ * header info
+ */
+ private static final String HEADER_APP_JSON = "application/json";
+
+ /**
+ * create HttpURLConnection client
+ */
+ private HttpURLConnection conn;
+
+ /**
+ * invokes the post request
+ *
+ * @param breinActivity data
+ */
+ @Override
+ public void doRequest(final BreinActivity breinActivity) {
+
+ // validate the input objects
+ BreinUtil.validate(breinActivity);
+
+ final String fullUrl = BreinUtil.getFullyQualifiedUrl(breinActivity);
+ final String requestBody = BreinUtil.getRequestBody(breinActivity);
+
+ new Thread(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ URL url = new URL(fullUrl);
+
+ final HttpURLConnection conn = (HttpURLConnection) url.openConnection();
+ conn.setReadTimeout(15000 /* milliseconds */);
+ conn.setConnectTimeout(15000 /* milliseconds */);
+ conn.setRequestMethod("POST");
+ conn.setDoInput(true);
+ conn.setDoOutput(true);
+
+ // final String data = requestObject.toString();
+
+ final PrintWriter out = new PrintWriter(conn.getOutputStream());
+ out.print(requestBody);
+ out.close();
+
+ conn.connect();
+ int response = conn.getResponseCode();
+ // System.out.println("response is: " + response);
+
+ } catch (final Exception e) {
+ // System.out.println("Exception is: " + e);
+ throw new BreinException("REST rest call exception");
+ }
+
+ }
+ }).start();
+ }
+
+ /**
+ * performs a lookup and provides details
+ *
+ * @param breinLookup contains request data
+ * @return response from Breinify
+ */
+ @Override
+ public BreinResult doLookup(final BreinLookup breinLookup) {
+
+ // validate the input objects
+ BreinUtil.validate(breinLookup);
+
+ final String fullUrl = BreinUtil.getFullyQualifiedUrl(breinLookup);
+ final String requestBody = BreinUtil.getRequestBody(breinLookup);
+
+ new Thread(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ URL url = new URL(fullUrl);
+
+ final HttpURLConnection conn = (HttpURLConnection) url.openConnection();
+ conn.setReadTimeout(15000 /* milliseconds */);
+ conn.setConnectTimeout(15000 /* milliseconds */);
+ conn.setRequestMethod("POST");
+ conn.setDoInput(true);
+ conn.setDoOutput(true);
+
+ // final String data = requestObject.toString();
+
+ final PrintWriter out = new PrintWriter(conn.getOutputStream());
+ out.print(requestBody);
+ out.close();
+
+ conn.connect();
+ int response = conn.getResponseCode();
+ // System.out.println("response code is: " + response);
+
+ if (response == 200) {
+ String jsonString = "";
+
+ InputStream mInputStream = conn.getInputStream();
+
+ int i = 0;
+ while ((i = mInputStream.read()) != -1) {
+ jsonString += (char) i;
+ }
+ // System.out.println("Response data is: " + jsonString);
+ // return new BreinResult(response.getEntity(String.class));
+
+ } else {
+ throw new BreinException("REST rest call exception");
+ }
+
+ } catch (final Exception e) {
+ // System.out.println("Exception is: " + e);
+ throw new BreinException("REST rest call exception");
+ }
+
+ }
+ }).start();
+
+ return null;
+ }
+
+
+ /**
+ * stops possible functionality (e.g. threads)
+ */
+ @Override
+ public void terminate() {
+ }
+
+ /**
+ * configuration of the rest client
+ */
+ @Override
+ public void configure(final BreinConfig breinConfig) {
+ }
+
+}
diff --git a/brein-api-library-android/src/main/java/com/brein/engine/IRestEngine.java b/brein-api-library-android/src/main/java/com/brein/engine/IRestEngine.java
new file mode 100644
index 0000000..adbecfd
--- /dev/null
+++ b/brein-api-library-android/src/main/java/com/brein/engine/IRestEngine.java
@@ -0,0 +1,47 @@
+package com.brein.engine;
+
+import com.brein.api.BreinActivity;
+import com.brein.api.BreinException;
+import com.brein.api.BreinLookup;
+import com.brein.domain.BreinConfig;
+import com.brein.domain.BreinResult;
+
+
+/**
+ * Interface for all possible rest engines
+ */
+public interface IRestEngine {
+
+ /**
+ * configures the rest engine
+ *
+ * @param breinConfig configuration object
+ */
+ void configure(final BreinConfig breinConfig);
+
+ /**
+ * invokes the post request
+ *
+ * @param breinActivity data
+ */
+ void doRequest(final BreinActivity breinActivity) throws BreinException;
+
+ /**
+ * performs a lookup and provides details
+ *
+ * @param breinLookup contains request data
+ * @return response from Breinify
+ */
+ BreinResult doLookup(final BreinLookup breinLookup) throws BreinException;
+
+ /**
+ * terminates the rest engine
+ */
+ void terminate();
+
+
+
+
+
+
+}
diff --git a/brein-api-library-android/src/main/java/com/brein/util/Base64.java b/brein-api-library-android/src/main/java/com/brein/util/Base64.java
new file mode 100644
index 0000000..8f2eb7c
--- /dev/null
+++ b/brein-api-library-android/src/main/java/com/brein/util/Base64.java
@@ -0,0 +1,2065 @@
+package com.brein.util;
+
+/**
+ * Encodes and decodes to and from Base64 notation.
+ * Homepage: http://iharder.net/base64.
+ *
+ * Example:
+ *
+ * String encoded = Base64.encode( myByteArray );
+ *
+ * byte[] myByteArray = Base64.decode( encoded );
+ *
+ * The options parameter, which appears in a few places, is used to pass
+ * several pieces of information to the encoder. In the "higher level" methods such as
+ * encodeBytes( bytes, options ) the options parameter can be used to indicate such
+ * things as first gzipping the bytes before encoding them, not inserting linefeeds,
+ * and encoding using the URL-safe and Ordered dialects.
+ *
+ * Note, according to RFC3548,
+ * Section 2.1, implementations should not add line feeds unless explicitly told
+ * to do so. I've got Base64 set to this behavior now, although earlier versions
+ * broke lines by default.
+ *
+ * The constants defined in Base64 can be OR-ed together to combine options, so you
+ * might make a call like this:
+ *
+ * String encoded = Base64.encodeBytes( mybytes, Base64.GZIP | Base64.DO_BREAK_LINES );
+ * to compress the data before encoding it and then making the output have newline characters.
+ * Also...
+ * String encoded = Base64.encodeBytes( crazyString.getBytes() );
+ *
+ *
+ *
+ *
+ * Change Log:
+ *
+ *
+ * - v2.3.7 - Fixed subtle bug when base 64 input stream contained the
+ * value 01111111, which is an invalid base 64 character but should not
+ * throw an ArrayIndexOutOfBoundsException either. Led to discovery of
+ * mishandling (or potential for better handling) of other bad input
+ * characters. You should now get an IOException if you try decoding
+ * something that has bad characters in it.
+ * - v2.3.6 - Fixed bug when breaking lines and the final byte of the encoded
+ * string ended in the last column; the buffer was not properly shrunk and
+ * contained an extra (null) byte that made it into the string.
+ * - v2.3.5 - Fixed bug in {@link #encodeFromFile} where estimated buffer size
+ * was wrong for files of size 31, 34, and 37 bytes.
+ * - v2.3.4 - Fixed bug when working with gzipped streams whereby flushing
+ * the Base64.OutputStream closed the Base64 encoding (by padding with equals
+ * signs) too soon. Also added an option to suppress the automatic decoding
+ * of gzipped streams. Also added experimental support for specifying a
+ * class loader when using the
+ * {@link #decodeToObject(String, int, ClassLoader)}
+ * method.
+ * - v2.3.3 - Changed default char encoding to US-ASCII which reduces the internal Java
+ * footprint with its CharEncoders and so forth. Fixed some javadocs that were
+ * inconsistent. Removed imports and specified things like java.io.IOException
+ * explicitly inline.
+ * - v2.3.2 - Reduced memory footprint! Finally refined the "guessing" of how big the
+ * final encoded data will be so that the code doesn't have to create two output
+ * arrays: an oversized initial one and then a final, exact-sized one. Big win
+ * when using the {@link #encodeBytesToBytes(byte[])} family of methods (and not
+ * using the gzip options which uses a different mechanism with streams and stuff).
+ * - v2.3.1 - Added {@link #encodeBytesToBytes(byte[], int, int, int)} and some
+ * similar helper methods to be more efficient with memory by not returning a
+ * String but just a byte array.
+ * - v2.3 - This is not a drop-in replacement! This is two years of comments
+ * and bug fixes queued up and finally executed. Thanks to everyone who sent
+ * me stuff, and I'm sorry I wasn't able to distribute your fixes to everyone else.
+ * Much bad coding was cleaned up including throwing exceptions where necessary
+ * instead of returning null values or something similar. Here are some changes
+ * that may affect you:
+ *
+ * - Does not break lines, by default. This is to keep in compliance with
+ * RFC3548.
+ * - Throws exceptions instead of returning null values. Because some operations
+ * (especially those that may permit the GZIP option) use IO streams, there
+ * is a possiblity of an java.io.IOException being thrown. After some discussion and
+ * thought, I've changed the behavior of the methods to throw java.io.IOExceptions
+ * rather than return null if ever there's an error. I think this is more
+ * appropriate, though it will require some changes to your code. Sorry,
+ * it should have been done this way to begin with.
+ * - Removed all references to System.out, System.err, and the like.
+ * Shame on me. All I can say is sorry they were ever there.
+ * - Throws NullPointerExceptions and IllegalArgumentExceptions as needed
+ * such as when passed arrays are null or offsets are invalid.
+ * - Cleaned up as much javadoc as I could to avoid any javadoc warnings.
+ * This was especially annoying before for people who were thorough in their
+ * own projects and then had gobs of javadoc warnings on this file.
+ *
+ * - v2.2.1 - Fixed bug using URL_SAFE and ORDERED encodings. Fixed bug
+ * when using very small files (~< 40 bytes).
+ * - v2.2 - Added some helper methods for encoding/decoding directly from
+ * one file to the next. Also added a main() method to support command line
+ * encoding/decoding from one file to the next. Also added these Base64 dialects:
+ *
+ * - The default is RFC3548 format.
+ * - Calling Base64.setFormat(Base64.BASE64_FORMAT.URLSAFE_FORMAT) generates
+ * URL and file name friendly format as described in Section 4 of RFC3548.
+ * http://www.faqs.org/rfcs/rfc3548.html
+ * - Calling Base64.setFormat(Base64.BASE64_FORMAT.ORDERED_FORMAT) generates
+ * URL and file name friendly format that preserves lexical ordering as described
+ * in http://www.faqs.org/qa/rfcc-1940.html
+ *
+ * Special thanks to Jim Kellerman at http://www.powerset.com/
+ * for contributing the new Base64 dialects.
+ *
+ *
+ * - 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.3.7
+ */
+public 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 in first bit. Value is one. */
+ public final static int ENCODE = 1;
+
+
+ /** Specify decoding in first bit. Value is zero. */
+ public final static int DECODE = 0;
+
+
+ /** Specify that data should be gzip-compressed in second bit. Value is two. */
+ public final static int GZIP = 2;
+
+ /** Specify that gzipped data should not be automatically gunzipped. */
+ public final static int DONT_GUNZIP = 4;
+
+
+ /** Do break lines when encoding. Value is 8. */
+ public final static int DO_BREAK_LINES = 8;
+
+ /**
+ * Encode using Base64-like encoding that is URL- and Filename-safe as described
+ * in Section 4 of RFC3548:
+ * http://www.faqs.org/rfcs/rfc3548.html.
+ * It is important to note that data encoded this way is not officially valid Base64,
+ * or at the very least should not be called Base64 without also specifying that is
+ * was encoded using the URL- and Filename-safe dialect.
+ */
+ public final static int URL_SAFE = 16;
+
+
+ /**
+ * Encode using the special "ordered" dialect of Base64 described here:
+ * http://www.faqs.org/qa/rfcc-1940.html.
+ */
+ public final static int ORDERED = 32;
+
+
+/* ******** 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 = "US-ASCII";
+
+
+ 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
+
+
+/* ******** S T A N D A R D B A S E 6 4 A L P H A B E T ******** */
+
+ /** The 64 valid Base64 values. */
+ /* Host platform me be something funny like EBCDIC, so we hardcode these values. */
+ private final static byte[] _STANDARD_ALPHABET = {
+ (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)'/'
+ };
+
+
+ /**
+ * Translates a Base64 value to either its 6-bit reconstruction value
+ * or a negative number indicating some other meaning.
+ **/
+ private final static byte[] _STANDARD_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,-9 // Decimal 123 - 127
+ ,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 128 - 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
+ };
+
+
+/* ******** U R L S A F E B A S E 6 4 A L P H A B E T ******** */
+
+ /**
+ * Used in the URL- and Filename-safe dialect described in Section 4 of RFC3548:
+ * http://www.faqs.org/rfcs/rfc3548.html.
+ * Notice that the last two bytes become "hyphen" and "underscore" instead of "plus" and "slash."
+ */
+ private final static byte[] _URL_SAFE_ALPHABET = {
+ (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)'_'
+ };
+
+ /**
+ * Used in decoding URL- and Filename-safe dialects of Base64.
+ */
+ private final static byte[] _URL_SAFE_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
+ -9, // Plus sign at decimal 43
+ -9, // Decimal 44
+ 62, // Minus sign at decimal 45
+ -9, // Decimal 46
+ -9, // 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, // Decimal 91 - 94
+ 63, // Underscore at decimal 95
+ -9, // Decimal 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,-9 // Decimal 123 - 127
+ ,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 128 - 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
+ };
+
+
+
+/* ******** O R D E R E D B A S E 6 4 A L P H A B E T ******** */
+
+ /**
+ * I don't get the point of this technique, but someone requested it,
+ * and it is described here:
+ * http://www.faqs.org/qa/rfcc-1940.html.
+ */
+ private final static byte[] _ORDERED_ALPHABET = {
+ (byte)'-',
+ (byte)'0', (byte)'1', (byte)'2', (byte)'3', (byte)'4',
+ (byte)'5', (byte)'6', (byte)'7', (byte)'8', (byte)'9',
+ (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)'_',
+ (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'
+ };
+
+ /**
+ * Used in decoding the "ordered" dialect of Base64.
+ */
+ private final static byte[] _ORDERED_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
+ -9, // Plus sign at decimal 43
+ -9, // Decimal 44
+ 0, // Minus sign at decimal 45
+ -9, // Decimal 46
+ -9, // Slash at decimal 47
+ 1,2,3,4,5,6,7,8,9,10, // Numbers zero through nine
+ -9,-9,-9, // Decimal 58 - 60
+ -1, // Equals sign at decimal 61
+ -9,-9,-9, // Decimal 62 - 64
+ 11,12,13,14,15,16,17,18,19,20,21,22,23, // Letters 'A' through 'M'
+ 24,25,26,27,28,29,30,31,32,33,34,35,36, // Letters 'N' through 'Z'
+ -9,-9,-9,-9, // Decimal 91 - 94
+ 37, // Underscore at decimal 95
+ -9, // Decimal 96
+ 38,39,40,41,42,43,44,45,46,47,48,49,50, // Letters 'a' through 'm'
+ 51,52,53,54,55,56,57,58,59,60,61,62,63, // Letters 'n' through 'z'
+ -9,-9,-9,-9,-9 // Decimal 123 - 127
+ ,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 128 - 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
+ };
+
+
+/* ******** D E T E R M I N E W H I C H A L H A B E T ******** */
+
+
+ /**
+ * Returns one of the _SOMETHING_ALPHABET byte arrays depending on
+ * the options specified.
+ * It's possible, though silly, to specify ORDERED and URLSAFE
+ * in which case one of them will be picked, though there is
+ * no guarantee as to which one will be picked.
+ */
+ private final static byte[] getAlphabet( int options ) {
+ if ((options & URL_SAFE) == URL_SAFE) {
+ return _URL_SAFE_ALPHABET;
+ } else if ((options & ORDERED) == ORDERED) {
+ return _ORDERED_ALPHABET;
+ } else {
+ return _STANDARD_ALPHABET;
+ }
+ } // end getAlphabet
+
+
+ /**
+ * Returns one of the _SOMETHING_DECODABET byte arrays depending on
+ * the options specified.
+ * It's possible, though silly, to specify ORDERED and URL_SAFE
+ * in which case one of them will be picked, though there is
+ * no guarantee as to which one will be picked.
+ */
+ private final static byte[] getDecodabet( int options ) {
+ if( (options & URL_SAFE) == URL_SAFE) {
+ return _URL_SAFE_DECODABET;
+ } else if ((options & ORDERED) == ORDERED) {
+ return _ORDERED_DECODABET;
+ } else {
+ return _STANDARD_DECODABET;
+ }
+ } // end getAlphabet
+
+
+
+ /** Defeats instantiation. */
+ private Base64(){}
+
+
+
+
+/* ******** 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, int options ) {
+ encode3to4( threeBytes, 0, numSigBytes, b4, 0, options );
+ 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.
+ * This is the lowest level of the encoding methods with
+ * all possible parameters.
+ *
+ * @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, int options ) {
+
+ byte[] ALPHABET = getAlphabet( options );
+
+ // 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
+
+
+
+ /**
+ * Performs Base64 encoding on the raw
ByteBuffer,
+ * writing it to the encoded
ByteBuffer.
+ * This is an experimental feature. Currently it does not
+ * pass along any options (such as {@link #DO_BREAK_LINES}
+ * or {@link #GZIP}.
+ *
+ * @param raw input buffer
+ * @param encoded output buffer
+ * @since 2.3
+ */
+ public static void encode( java.nio.ByteBuffer raw, java.nio.ByteBuffer encoded ){
+ byte[] raw3 = new byte[3];
+ byte[] enc4 = new byte[4];
+
+ while( raw.hasRemaining() ){
+ int rem = Math.min(3,raw.remaining());
+ raw.get(raw3,0,rem);
+ Base64.encode3to4(enc4, raw3, rem, Base64.NO_OPTIONS );
+ encoded.put(enc4);
+ } // end input remaining
+ }
+
+
+ /**
+ * Performs Base64 encoding on the raw
ByteBuffer,
+ * writing it to the encoded
CharBuffer.
+ * This is an experimental feature. Currently it does not
+ * pass along any options (such as {@link #DO_BREAK_LINES}
+ * or {@link #GZIP}.
+ *
+ * @param raw input buffer
+ * @param encoded output buffer
+ * @since 2.3
+ */
+ public static void encode( java.nio.ByteBuffer raw, java.nio.CharBuffer encoded ){
+ byte[] raw3 = new byte[3];
+ byte[] enc4 = new byte[4];
+
+ while( raw.hasRemaining() ){
+ int rem = Math.min(3,raw.remaining());
+ raw.get(raw3,0,rem);
+ Base64.encode3to4(enc4, raw3, rem, Base64.NO_OPTIONS );
+ for( int i = 0; i < 4; i++ ){
+ encoded.put( (char)(enc4[i] & 0xFF) );
+ }
+ } // end input remaining
+ }
+
+
+
+
+ /**
+ * Serializes an object and returns the Base64-encoded
+ * version of that serialized object.
+ *
+ * As of v 2.3, if the object
+ * cannot be serialized or there is another error,
+ * the method will throw an java.io.IOException. This is new to v2.3!
+ * In earlier versions, it just returned a null value, but
+ * in retrospect that's a pretty poor way to handle it.
+ *
+ * The object is not GZip-compressed before being encoded.
+ *
+ * @param serializableObject The object to encode
+ * @return The Base64-encoded object
+ * @throws java.io.IOException if there is an error
+ * @throws NullPointerException if serializedObject is null
+ * @since 1.4
+ */
+ public static String encodeObject( java.io.Serializable serializableObject )
+ throws java.io.IOException {
+ return encodeObject( serializableObject, NO_OPTIONS );
+ } // end encodeObject
+
+
+
+ /**
+ * Serializes an object and returns the Base64-encoded
+ * version of that serialized object.
+ *
+ * As of v 2.3, if the object
+ * cannot be serialized or there is another error,
+ * the method will throw an java.io.IOException. This is new to v2.3!
+ * In earlier versions, it just returned a null value, but
+ * in retrospect that's a pretty poor way to handle it.
+ *
+ * The object is not GZip-compressed before being encoded.
+ *
+ * Example options:
+ * GZIP: gzip-compresses object before encoding it.
+ * DO_BREAK_LINES: break lines at 76 characters
+ *
+ *
+ * Example: encodeObject( myObj, Base64.GZIP )
or
+ *
+ * Example: encodeObject( myObj, Base64.GZIP | Base64.DO_BREAK_LINES )
+ *
+ * @param serializableObject The object to encode
+ * @param options Specified options
+ * @return The Base64-encoded object
+ * @see Base64#GZIP
+ * @see Base64#DO_BREAK_LINES
+ * @throws java.io.IOException if there is an error
+ * @since 2.0
+ */
+ public static String encodeObject( java.io.Serializable serializableObject, int options )
+ throws java.io.IOException {
+
+ if( serializableObject == null ){
+ throw new NullPointerException( "Cannot serialize a null object." );
+ } // end if: null
+
+ // Streams
+ java.io.ByteArrayOutputStream baos = null;
+ java.io.OutputStream b64os = null;
+ java.util.zip.GZIPOutputStream gzos = null;
+ java.io.ObjectOutputStream oos = null;
+
+
+ try {
+ // ObjectOutputStream -> (GZIP) -> Base64 -> ByteArrayOutputStream
+ baos = new java.io.ByteArrayOutputStream();
+ b64os = new OutputStream( baos, ENCODE | options );
+ if( (options & GZIP) != 0 ){
+ // Gzip
+ gzos = new java.util.zip.GZIPOutputStream(b64os);
+ oos = new java.io.ObjectOutputStream( gzos );
+ } else {
+ // Not gzipped
+ oos = new java.io.ObjectOutputStream( b64os );
+ }
+ oos.writeObject( serializableObject );
+ } // end try
+ catch( java.io.IOException e ) {
+ // Catch it and then throw it immediately so that
+ // the finally{} block is called for cleanup.
+ throw e;
+ } // end catch
+ finally {
+ try{ oos.close(); } catch( Exception e ){}
+ try{ gzos.close(); } catch( Exception e ){}
+ try{ b64os.close(); } catch( Exception e ){}
+ try{ baos.close(); } catch( Exception e ){}
+ } // end finally
+
+ // Return value according to relevant encoding.
+ try {
+ return new String( baos.toByteArray(), PREFERRED_ENCODING );
+ } // end try
+ catch (java.io.UnsupportedEncodingException uue){
+ // Fall back to some Java default
+ 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
+ * @return The data in Base64-encoded form
+ * @throws NullPointerException if source array is null
+ * @since 1.4
+ */
+ public static String encodeBytes( byte[] source ) {
+ // Since we're not going to have the GZIP encoding turned on,
+ // we're not going to have an java.io.IOException thrown, so
+ // we should not force the user to have to catch it.
+ String encoded = null;
+ try {
+ encoded = encodeBytes(source, 0, source.length, NO_OPTIONS);
+ } catch (java.io.IOException ex) {
+ assert false : ex.getMessage();
+ } // end catch
+ assert encoded != null;
+ return encoded;
+ } // end encodeBytes
+
+
+
+ /**
+ * Encodes a byte array into Base64 notation.
+ *
+ * Example options:
+ * GZIP: gzip-compresses object before encoding it.
+ * DO_BREAK_LINES: 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.DO_BREAK_LINES )
+ *
+ *
+ *
As of v 2.3, if there is an error with the GZIP stream,
+ * the method will throw an java.io.IOException. This is new to v2.3!
+ * In earlier versions, it just returned a null value, but
+ * in retrospect that's a pretty poor way to handle it.
+ *
+ *
+ * @param source The data to convert
+ * @param options Specified options
+ * @return The Base64-encoded data as a String
+ * @see Base64#GZIP
+ * @see Base64#DO_BREAK_LINES
+ * @throws java.io.IOException if there is an error
+ * @throws NullPointerException if source array is null
+ * @since 2.0
+ */
+ public static String encodeBytes( byte[] source, int options ) throws java.io.IOException {
+ return encodeBytes( source, 0, source.length, options );
+ } // end encodeBytes
+
+
+ /**
+ * Encodes a byte array into Base64 notation.
+ * Does not GZip-compress data.
+ *
+ * As of v 2.3, if there is an error,
+ * the method will throw an java.io.IOException. This is new to v2.3!
+ * In earlier versions, it just returned a null value, but
+ * in retrospect that's a pretty poor way to handle it.
+ *
+ *
+ * @param source The data to convert
+ * @param off Offset in array where conversion should begin
+ * @param len Length of data to convert
+ * @return The Base64-encoded data as a String
+ * @throws NullPointerException if source array is null
+ * @throws IllegalArgumentException if source array, offset, or length are invalid
+ * @since 1.4
+ */
+ public static String encodeBytes( byte[] source, int off, int len ) {
+ // Since we're not going to have the GZIP encoding turned on,
+ // we're not going to have an java.io.IOException thrown, so
+ // we should not force the user to have to catch it.
+ String encoded = null;
+ try {
+ encoded = encodeBytes( source, off, len, NO_OPTIONS );
+ } catch (java.io.IOException ex) {
+ assert false : ex.getMessage();
+ } // end catch
+ assert encoded != null;
+ return encoded;
+ } // end encodeBytes
+
+
+
+ /**
+ * Encodes a byte array into Base64 notation.
+ *
+ * Example options:
+ * GZIP: gzip-compresses object before encoding it.
+ * DO_BREAK_LINES: 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.DO_BREAK_LINES )
+ *
+ *
+ *
As of v 2.3, if there is an error with the GZIP stream,
+ * the method will throw an java.io.IOException. This is new to v2.3!
+ * In earlier versions, it just returned a null value, but
+ * in retrospect that's a pretty poor way to handle it.
+ *
+ *
+ * @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
+ * @return The Base64-encoded data as a String
+ * @see Base64#GZIP
+ * @see Base64#DO_BREAK_LINES
+ * @throws java.io.IOException if there is an error
+ * @throws NullPointerException if source array is null
+ * @throws IllegalArgumentException if source array, offset, or length are invalid
+ * @since 2.0
+ */
+ public static String encodeBytes( byte[] source, int off, int len, int options ) throws java.io.IOException {
+ byte[] encoded = encodeBytesToBytes( source, off, len, options );
+
+ // Return value according to relevant encoding.
+ try {
+ return new String( encoded, PREFERRED_ENCODING );
+ } // end try
+ catch (java.io.UnsupportedEncodingException uue) {
+ return new String( encoded );
+ } // end catch
+
+ } // end encodeBytes
+
+
+
+
+ /**
+ * Similar to {@link #encodeBytes(byte[])} but returns
+ * a byte array instead of instantiating a String. This is more efficient
+ * if you're working with I/O streams and have large data sets to encode.
+ *
+ *
+ * @param source The data to convert
+ * @return The Base64-encoded data as a byte[] (of ASCII characters)
+ * @throws NullPointerException if source array is null
+ * @since 2.3.1
+ */
+ public static byte[] encodeBytesToBytes( byte[] source ) {
+ byte[] encoded = null;
+ try {
+ encoded = encodeBytesToBytes( source, 0, source.length, Base64.NO_OPTIONS );
+ } catch( java.io.IOException ex ) {
+ assert false : "IOExceptions only come from GZipping, which is turned off: " + ex.getMessage();
+ }
+ return encoded;
+ }
+
+
+ /**
+ * Similar to {@link #encodeBytes(byte[], int, int, int)} but returns
+ * a byte array instead of instantiating a String. This is more efficient
+ * if you're working with I/O streams and have large data sets to encode.
+ *
+ *
+ * @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
+ * @return The Base64-encoded data as a String
+ * @see Base64#GZIP
+ * @see Base64#DO_BREAK_LINES
+ * @throws java.io.IOException if there is an error
+ * @throws NullPointerException if source array is null
+ * @throws IllegalArgumentException if source array, offset, or length are invalid
+ * @since 2.3.1
+ */
+ public static byte[] encodeBytesToBytes( byte[] source, int off, int len, int options ) throws java.io.IOException {
+
+ if( source == null ){
+ throw new NullPointerException( "Cannot serialize a null array." );
+ } // end if: null
+
+ if( off < 0 ){
+ throw new IllegalArgumentException( "Cannot have negative offset: " + off );
+ } // end if: off < 0
+
+ if( len < 0 ){
+ throw new IllegalArgumentException( "Cannot have length offset: " + len );
+ } // end if: len < 0
+
+ if( off + len > source.length ){
+ throw new IllegalArgumentException(
+ String.format( "Cannot have offset of %d and length of %d with array of length %d", off,len,source.length));
+ } // end if: off < 0
+
+
+
+ // Compress?
+ if( (options & GZIP) != 0 ) {
+ java.io.ByteArrayOutputStream baos = null;
+ java.util.zip.GZIPOutputStream gzos = null;
+ OutputStream b64os = null;
+
+ try {
+ // GZip -> Base64 -> ByteArray
+ baos = new java.io.ByteArrayOutputStream();
+ b64os = new OutputStream( baos, ENCODE | options );
+ gzos = new java.util.zip.GZIPOutputStream( b64os );
+
+ gzos.write( source, off, len );
+ gzos.close();
+ } // end try
+ catch( java.io.IOException e ) {
+ // Catch it and then throw it immediately so that
+ // the finally{} block is called for cleanup.
+ throw e;
+ } // end catch
+ finally {
+ try{ gzos.close(); } catch( Exception e ){}
+ try{ b64os.close(); } catch( Exception e ){}
+ try{ baos.close(); } catch( Exception e ){}
+ } // end finally
+
+ return baos.toByteArray();
+ } // end if: compress
+
+ // Else, don't compress. Better not to use streams at all then.
+ else {
+ boolean breakLines = (options & DO_BREAK_LINES) != 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
+ // Try to determine more precisely how big the array needs to be.
+ // If we get it right, we don't have to do an array copy, and
+ // we save a bunch of memory.
+ int encLen = ( len / 3 ) * 4 + ( len % 3 > 0 ? 4 : 0 ); // Bytes needed for actual encoding
+ if( breakLines ){
+ encLen += encLen / MAX_LINE_LENGTH; // Plus extra newline characters
+ }
+ byte[] outBuff = new byte[ encLen ];
+
+
+ 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, options );
+
+ 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, options );
+ e += 4;
+ } // end if: some padding needed
+
+
+ // Only resize array if we didn't guess it right.
+ if( e <= outBuff.length - 1 ){
+ // If breaking lines and the last byte falls right at
+ // the line length (76 bytes per line), there will be
+ // one extra byte, and the array will need to be resized.
+ // Not too bad of an estimate on array size, I'd say.
+ byte[] finalOut = new byte[e];
+ System.arraycopy(outBuff,0, finalOut,0,e);
+ //System.err.println("Having to resize array from " + outBuff.length + " to " + e );
+ return finalOut;
+ } else {
+ //System.err.println("No need to resize array.");
+ return outBuff;
+ }
+
+ } // end else: don't compress
+
+ } // end encodeBytesToBytes
+
+
+
+
+
+/* ******** 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.
+ * This is the lowest level of the decoding methods with
+ * all possible parameters.
+ *
+ *
+ * @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
+ * @param options alphabet type is pulled from this (standard, url-safe, ordered)
+ * @return the number of decoded bytes converted
+ * @throws NullPointerException if source or destination arrays are null
+ * @throws IllegalArgumentException if srcOffset or destOffset are invalid
+ * or there is not enough room in the array.
+ * @since 1.3
+ */
+ private static int decode4to3(
+ byte[] source, int srcOffset,
+ byte[] destination, int destOffset, int options ) {
+
+ // Lots of error checking and exception throwing
+ if( source == null ){
+ throw new NullPointerException( "Source array was null." );
+ } // end if
+ if( destination == null ){
+ throw new NullPointerException( "Destination array was null." );
+ } // end if
+ if( srcOffset < 0 || srcOffset + 3 >= source.length ){
+ throw new IllegalArgumentException( String.format(
+ "Source array with length %d cannot have offset of %d and still process four bytes.", source.length, srcOffset ) );
+ } // end if
+ if( destOffset < 0 || destOffset +2 >= destination.length ){
+ throw new IllegalArgumentException( String.format(
+ "Destination array with length %d cannot have offset of %d and still store three bytes.", destination.length, destOffset ) );
+ } // end if
+
+
+ byte[] DECODABET = getDecodabet( options );
+
+ // 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
+
+
+
+
+
+ /**
+ * Low-level access to decoding ASCII characters in
+ * the form of a byte array. Ignores GUNZIP option, if
+ * it's set. This is not generally a recommended method,
+ * although it is used internally as part of the decoding process.
+ * Special case: if len = 0, an empty array is returned. Still,
+ * if you need more speed and reduced memory footprint (and aren't
+ * gzipping), consider this method.
+ *
+ * @param source The Base64 encoded data
+ * @return decoded data
+ * @since 2.3.1
+ */
+ public static byte[] decode( byte[] source )
+ throws java.io.IOException {
+ byte[] decoded = null;
+// try {
+ decoded = decode( source, 0, source.length, Base64.NO_OPTIONS );
+// } catch( java.io.IOException ex ) {
+// assert false : "IOExceptions only come from GZipping, which is turned off: " + ex.getMessage();
+// }
+ return decoded;
+ }
+
+
+
+ /**
+ * Low-level access to decoding ASCII characters in
+ * the form of a byte array. Ignores GUNZIP option, if
+ * it's set. This is not generally a recommended method,
+ * although it is used internally as part of the decoding process.
+ * Special case: if len = 0, an empty array is returned. Still,
+ * if you need more speed and reduced memory footprint (and aren't
+ * gzipping), consider this method.
+ *
+ * @param source The Base64 encoded data
+ * @param off The offset of where to begin decoding
+ * @param len The length of characters to decode
+ * @param options Can specify options such as alphabet type to use
+ * @return decoded data
+ * @throws java.io.IOException If bogus characters exist in source data
+ * @since 1.3
+ */
+ public static byte[] decode( byte[] source, int off, int len, int options )
+ throws java.io.IOException {
+
+ // Lots of error checking and exception throwing
+ if( source == null ){
+ throw new NullPointerException( "Cannot decode null source array." );
+ } // end if
+ if( off < 0 || off + len > source.length ){
+ throw new IllegalArgumentException( String.format(
+ "Source array with length %d cannot have offset of %d and process %d bytes.", source.length, off, len ) );
+ } // end if
+
+ if( len == 0 ){
+ return new byte[0];
+ }else if( len < 4 ){
+ throw new IllegalArgumentException(
+ "Base64-encoded string must have at least four characters, but length specified was " + len );
+ } // end if
+
+ byte[] DECODABET = getDecodabet( options );
+
+ int len34 = len * 3 / 4; // Estimate on array size
+ byte[] outBuff = new byte[ len34 ]; // Upper limit on size of output
+ int outBuffPosn = 0; // Keep track of where we're writing
+
+ byte[] b4 = new byte[4]; // Four byte buffer from source, eliminating white space
+ int b4Posn = 0; // Keep track of four byte input buffer
+ int i = 0; // Source array counter
+ byte sbiDecode = 0; // Special value from DECODABET
+
+ for( i = off; i < off+len; i++ ) { // Loop through source
+
+ sbiDecode = DECODABET[ source[i]&0xFF ];
+
+ // White space, Equals sign, or legit Base64 character
+ // Note the values such as -5 and -9 in the
+ // DECODABETs at the top of the file.
+ if( sbiDecode >= WHITE_SPACE_ENC ) {
+ if( sbiDecode >= EQUALS_SIGN_ENC ) {
+ b4[ b4Posn++ ] = source[i]; // Save non-whitespace
+ if( b4Posn > 3 ) { // Time to decode?
+ outBuffPosn += decode4to3( b4, 0, outBuff, outBuffPosn, options );
+ b4Posn = 0;
+
+ // If that was the equals sign, break out of 'for' loop
+ if( source[i] == EQUALS_SIGN ) {
+ break;
+ } // end if: equals sign
+ } // end if: quartet built
+ } // end if: equals sign or better
+ } // end if: white space, equals sign or better
+ else {
+ // There's a bad input character in the Base64 stream.
+ throw new java.io.IOException( String.format(
+ "Bad Base64 input character decimal %d in array position %d", ((int)source[i])&0xFF, i ) );
+ } // 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 java.io.IOException If there is a problem
+ * @since 1.4
+ */
+ public static byte[] decode( String s ) throws java.io.IOException {
+ return decode( s, NO_OPTIONS );
+ }
+
+
+
+ /**
+ * Decodes data from Base64 notation, automatically
+ * detecting gzip-compressed data and decompressing it.
+ *
+ * @param s the string to decode
+ * @param options encode options such as URL_SAFE
+ * @return the decoded data
+ * @throws java.io.IOException if there is an error
+ * @throws NullPointerException if s is null
+ * @since 1.4
+ */
+ public static byte[] decode( String s, int options ) throws java.io.IOException {
+
+ if( s == null ){
+ throw new NullPointerException( "Input string was null." );
+ } // end if
+
+ 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, options );
+
+ // Check to see if it's gzip-compressed
+ // GZIP Magic Two-Byte Number: 0x8b1f (35615)
+ boolean dontGunzip = (options & DONT_GUNZIP) != 0;
+ if( (bytes != null) && (bytes.length >= 4) && (!dontGunzip) ) {
+
+ 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 ) {
+ e.printStackTrace();
+ // Just return originally-decoded bytes
+ } // end catch
+ finally {
+ try{ baos.close(); } catch( Exception e ){}
+ try{ gzis.close(); } catch( Exception e ){}
+ try{ bais.close(); } catch( Exception 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 NullPointerException if encodedObject is null
+ * @throws java.io.IOException if there is a general error
+ * @throws ClassNotFoundException if the decoded object is of a
+ * class that cannot be found by the JVM
+ * @since 1.5
+ */
+ public static Object decodeToObject( String encodedObject )
+ throws java.io.IOException, ClassNotFoundException {
+ return decodeToObject(encodedObject,NO_OPTIONS,null);
+ }
+
+
+ /**
+ * Attempts to decode Base64 data and deserialize a Java
+ * Object within. Returns null if there was an error.
+ * If loader is not null, it will be the class loader
+ * used when deserializing.
+ *
+ * @param encodedObject The Base64 data to decode
+ * @param options Various parameters related to decoding
+ * @param loader Optional class loader to use in deserializing classes.
+ * @return The decoded and deserialized object
+ * @throws NullPointerException if encodedObject is null
+ * @throws java.io.IOException if there is a general error
+ * @throws ClassNotFoundException if the decoded object is of a
+ * class that cannot be found by the JVM
+ * @since 2.3.4
+ */
+ public static Object decodeToObject(
+ String encodedObject, int options, final ClassLoader loader )
+ throws java.io.IOException, ClassNotFoundException {
+
+ // Decode and gunzip if necessary
+ byte[] objBytes = decode( encodedObject, options );
+
+ java.io.ByteArrayInputStream bais = null;
+ java.io.ObjectInputStream ois = null;
+ Object obj = null;
+
+ try {
+ bais = new java.io.ByteArrayInputStream( objBytes );
+
+ // If no custom class loader is provided, use Java's builtin OIS.
+ if( loader == null ){
+ ois = new java.io.ObjectInputStream( bais );
+ } // end if: no loader provided
+
+ // Else make a customized object input stream that uses
+ // the provided class loader.
+ else {
+ ois = new java.io.ObjectInputStream(bais){
+ @Override
+ public Class> resolveClass(java.io.ObjectStreamClass streamClass)
+ throws java.io.IOException, ClassNotFoundException {
+ Class c = Class.forName(streamClass.getName(), false, loader);
+ if( c == null ){
+ return super.resolveClass(streamClass);
+ } else {
+ return c; // Class loader knows of this class.
+ } // end else: not null
+ } // end resolveClass
+ }; // end ois
+ } // end else: no custom class loader
+
+ obj = ois.readObject();
+ } // end try
+ catch( java.io.IOException e ) {
+ throw e; // Catch and throw in order to execute finally{}
+ } // end catch
+ catch( ClassNotFoundException e ) {
+ throw e; // Catch and throw in order to execute finally{}
+ } // end catch
+ finally {
+ try{ bais.close(); } catch( Exception e ){}
+ try{ ois.close(); } catch( Exception e ){}
+ } // end finally
+
+ return obj;
+ } // end decodeObject
+
+
+
+ /**
+ * Convenience method for encoding data to a file.
+ *
+ * As of v 2.3, if there is a error,
+ * the method will throw an java.io.IOException. This is new to v2.3!
+ * In earlier versions, it just returned false, but
+ * in retrospect that's a pretty poor way to handle it.
+ *
+ * @param dataToEncode byte array of data to encode in base64 form
+ * @param filename Filename for saving encoded data
+ * @throws java.io.IOException if there is an error
+ * @throws NullPointerException if dataToEncode is null
+ * @since 2.1
+ */
+ public static void encodeToFile( byte[] dataToEncode, String filename )
+ throws java.io.IOException {
+
+ if( dataToEncode == null ){
+ throw new NullPointerException( "Data to encode was null." );
+ } // end iff
+
+ OutputStream bos = null;
+ try {
+ bos = new OutputStream(
+ new java.io.FileOutputStream( filename ), Base64.ENCODE );
+ bos.write( dataToEncode );
+ } // end try
+ catch( java.io.IOException e ) {
+ throw e; // Catch and throw to execute finally{} block
+ } // end catch: java.io.IOException
+ finally {
+ try{ bos.close(); } catch( Exception e ){}
+ } // end finally
+
+ } // end encodeToFile
+
+
+ /**
+ * Convenience method for decoding data to a file.
+ *
+ * As of v 2.3, if there is a error,
+ * the method will throw an java.io.IOException. This is new to v2.3!
+ * In earlier versions, it just returned false, but
+ * in retrospect that's a pretty poor way to handle it.
+ *
+ * @param dataToDecode Base64-encoded data as a string
+ * @param filename Filename for saving decoded data
+ * @throws java.io.IOException if there is an error
+ * @since 2.1
+ */
+ public static void decodeToFile( String dataToDecode, String filename )
+ throws java.io.IOException {
+
+ OutputStream bos = null;
+ try{
+ bos = new OutputStream(
+ new java.io.FileOutputStream( filename ), Base64.DECODE );
+ bos.write( dataToDecode.getBytes( PREFERRED_ENCODING ) );
+ } // end try
+ catch( java.io.IOException e ) {
+ throw e; // Catch and throw to execute finally{} block
+ } // end catch: java.io.IOException
+ finally {
+ try{ bos.close(); } catch( Exception e ){}
+ } // end finally
+
+ } // end decodeToFile
+
+
+
+
+ /**
+ * Convenience method for reading a base64-encoded
+ * file and decoding it.
+ *
+ * As of v 2.3, if there is a error,
+ * the method will throw an java.io.IOException. This is new to v2.3!
+ * In earlier versions, it just returned false, but
+ * in retrospect that's a pretty poor way to handle it.
+ *
+ * @param filename Filename for reading encoded data
+ * @return decoded byte array
+ * @throws java.io.IOException if there is an error
+ * @since 2.1
+ */
+ public static byte[] decodeFromFile( String filename )
+ throws java.io.IOException {
+
+ byte[] decodedData = null;
+ 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 java.io.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 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;
+ } // end while
+
+ // Save in a variable to return
+ decodedData = new byte[ length ];
+ System.arraycopy( buffer, 0, decodedData, 0, length );
+
+ } // end try
+ catch( java.io.IOException e ) {
+ throw e; // Catch and release to execute finally{}
+ } // end catch: java.io.IOException
+ finally {
+ try{ bis.close(); } catch( Exception e) {}
+ } // end finally
+
+ return decodedData;
+ } // end decodeFromFile
+
+
+
+ /**
+ * Convenience method for reading a binary file
+ * and base64-encoding it.
+ *
+ * As of v 2.3, if there is a error,
+ * the method will throw an java.io.IOException. This is new to v2.3!
+ * In earlier versions, it just returned false, but
+ * in retrospect that's a pretty poor way to handle it.
+ *
+ * @param filename Filename for reading binary data
+ * @return base64-encoded string
+ * @throws java.io.IOException if there is an error
+ * @since 2.1
+ */
+ public static String encodeFromFile( String filename )
+ throws java.io.IOException {
+
+ String encodedData = null;
+ InputStream bis = null;
+ try
+ {
+ // Set up some useful variables
+ java.io.File file = new java.io.File( filename );
+ byte[] buffer = new byte[ Math.max((int)(file.length() * 1.4+1),40) ]; // Need max() for math on small files (v2.2.1); Need +1 for a few corner cases (v2.3.5)
+ int length = 0;
+ int numBytes = 0;
+
+ // Open a stream
+ bis = new 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;
+ } // end while
+
+ // Save in a variable to return
+ encodedData = new String( buffer, 0, length, Base64.PREFERRED_ENCODING );
+
+ } // end try
+ catch( java.io.IOException e ) {
+ throw e; // Catch and release to execute finally{}
+ } // end catch: java.io.IOException
+ finally {
+ try{ bis.close(); } catch( Exception e) {}
+ } // end finally
+
+ return encodedData;
+ } // end encodeFromFile
+
+ /**
+ * Reads infile and encodes it to outfile.
+ *
+ * @param infile Input file
+ * @param outfile Output file
+ * @throws java.io.IOException if there is an error
+ * @since 2.2
+ */
+ public static void encodeFileToFile( String infile, String outfile )
+ throws java.io.IOException {
+
+ String encoded = Base64.encodeFromFile( infile );
+ java.io.OutputStream out = null;
+ try{
+ out = new java.io.BufferedOutputStream(
+ new java.io.FileOutputStream( outfile ) );
+ out.write( encoded.getBytes("US-ASCII") ); // Strict, 7-bit output.
+ } // end try
+ catch( java.io.IOException e ) {
+ throw e; // Catch and release to execute finally{}
+ } // end catch
+ finally {
+ try { out.close(); }
+ catch( Exception ex ){}
+ } // end finally
+ } // end encodeFileToFile
+
+
+ /**
+ * Reads infile and decodes it to outfile.
+ *
+ * @param infile Input file
+ * @param outfile Output file
+ * @throws java.io.IOException if there is an error
+ * @since 2.2
+ */
+ public static void decodeFileToFile( String infile, String outfile )
+ throws java.io.IOException {
+
+ byte[] decoded = Base64.decodeFromFile( infile );
+ java.io.OutputStream out = null;
+ try{
+ out = new java.io.BufferedOutputStream(
+ new java.io.FileOutputStream( outfile ) );
+ out.write( decoded );
+ } // end try
+ catch( java.io.IOException e ) {
+ throw e; // Catch and release to execute finally{}
+ } // end catch
+ finally {
+ try { out.close(); }
+ catch( Exception ex ){}
+ } // end finally
+ } // end decodeFileToFile
+
+
+ /* ******** I N N E R C L A S S I N P U T S T R E A M ******** */
+
+
+
+ /**
+ * A {@link 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
+ private int options; // Record options used to create the stream.
+ private byte[] decodabet; // Local copies to avoid extra method calls
+
+
+ /**
+ * Constructs a {@link 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 InputStream} in
+ * either ENCODE or DECODE mode.
+ *
+ * Valid options:
+ * ENCODE or DECODE: Encode or Decode as data is read.
+ * DO_BREAK_LINES: break lines at 76 characters
+ * (only meaningful when encoding)
+ *
+ *
+ * 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#DO_BREAK_LINES
+ * @since 2.0
+ */
+ public InputStream( java.io.InputStream in, int options ) {
+
+ super( in );
+ this.options = options; // Record for later
+ this.breakLines = (options & DO_BREAK_LINES) > 0;
+ this.encode = (options & ENCODE) > 0;
+ this.bufferLength = encode ? 4 : 3;
+ this.buffer = new byte[ bufferLength ];
+ this.position = -1;
+ this.lineLength = 0;
+ this.decodabet = getDecodabet(options);
+ } // 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++ ) {
+ int b = in.read();
+
+ // If end of stream, b is -1.
+ if( b >= 0 ) {
+ b3[i] = (byte)b;
+ numBinaryBytes++;
+ } else {
+ break; // out of for loop
+ } // end else: end of stream
+
+ } // end for: each needed input byte
+
+ if( numBinaryBytes > 0 ) {
+ encode3to4( b3, 0, numBinaryBytes, buffer, 0, options );
+ position = 0;
+ numSigBytes = 4;
+ } // end if: got data
+ else {
+ return -1; // Must be end of stream
+ } // 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
+ } // end if: end of stream
+
+ b4[i] = (byte)b;
+ } // end for: each needed input byte
+
+ if( i == 4 ) {
+ numSigBytes = decode4to3( b4, 0, buffer, 0, options );
+ 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;
+ } // end if: got data
+
+ 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;
+ } // end if: end
+
+ 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 ) {
+ dest[off + i] = (byte) b;
+ }
+ else if( i == 0 ) {
+ return -1;
+ }
+ else {
+ break; // Out of 'for' loop
+ } // 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 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;
+ private int options; // Record for later
+ private byte[] decodabet; // Local copies to avoid extra method calls
+
+ /**
+ * Constructs a {@link 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 OutputStream} in
+ * either ENCODE or DECODE mode.
+ *
+ * Valid options:
+ * ENCODE or DECODE: Encode or Decode as data is read.
+ * DO_BREAK_LINES: don't break lines at 76 characters
+ * (only meaningful when encoding)
+ *
+ *
+ * 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#DO_BREAK_LINES
+ * @since 1.3
+ */
+ public OutputStream( java.io.OutputStream out, int options ) {
+ super( out );
+ this.breakLines = (options & DO_BREAK_LINES) != 0;
+ this.encode = (options & ENCODE) != 0;
+ this.bufferLength = encode ? 3 : 4;
+ this.buffer = new byte[ bufferLength ];
+ this.position = 0;
+ this.lineLength = 0;
+ this.suspendEncoding = false;
+ this.b4 = new byte[4];
+ this.options = options;
+ this.decodabet = getDecodabet(options);
+ } // 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 ) {
+ this.out.write( theByte );
+ return;
+ } // end if: supsended
+
+ // Encode?
+ if( encode ) {
+ buffer[ position++ ] = (byte)theByte;
+ if( position >= bufferLength ) { // Enough to encode.
+
+ this.out.write( encode3to4( b4, buffer, bufferLength, options ) );
+
+ lineLength += 4;
+ if( breakLines && lineLength >= MAX_LINE_LENGTH ) {
+ this.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, options );
+ out.write( b4, 0, len );
+ 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 ) {
+ this.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.
+ * @throws java.io.IOException if there's an error.
+ */
+ public void flushBase64() throws java.io.IOException {
+ if( position > 0 ) {
+ if( encode ) {
+ out.write( encode3to4( b4, buffer, position, options ) );
+ 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
+ * base64-encoded data in a stream.
+ *
+ * @throws java.io.IOException if there's an error flushing
+ * @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
+ * base64-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/brein-api-library-android/src/main/java/com/brein/util/BreinUtil.java b/brein-api-library-android/src/main/java/com/brein/util/BreinUtil.java
new file mode 100755
index 0000000..9a8420e
--- /dev/null
+++ b/brein-api-library-android/src/main/java/com/brein/util/BreinUtil.java
@@ -0,0 +1,266 @@
+package com.brein.util;
+
+import com.brein.api.BreinBase;
+import com.brein.api.BreinException;
+import com.brein.domain.BreinConfig;
+
+import java.io.IOException;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.nio.charset.Charset;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+import java.util.Random;
+
+import javax.crypto.Mac;
+import javax.crypto.spec.SecretKeySpec;
+
+
+/**
+ * Utility class
+ */
+public class BreinUtil {
+
+ /**
+ * Logger instance
+ */
+ // public static Logger LOG = LoggerFactory.getLogger(BreinUtil.class);
+
+ private static final String AB = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+
+ private static final Mac mac;
+
+ private static final Random RANDOM = new Random();
+
+ static {
+
+ try {
+ mac = Mac.getInstance("HmacSHA256");
+ } catch (NoSuchAlgorithmException var1) {
+ throw new IllegalStateException("Unable to find needed algorithm!", var1);
+ }
+ }
+
+ /**
+ * Verifies if the object contains a value
+ * Return false in case of:
+ * - null
+ * - empty strings
+ *
+ * @param object to check
+ * @return true if object contains data
+ */
+ public static boolean containsValue(final Object object) {
+
+ if (object == null) {
+ return false;
+ }
+
+ if (object.getClass() == String.class) {
+ final String strObj = (String) object;
+ return strObj.length() > 0;
+ }
+
+ return true;
+ }
+
+ /**
+ * Helper method to generate a random string
+ *
+ * @return string
+ */
+ public static String randomString() {
+ final int len = 1 + RANDOM.nextInt(100);
+
+ return randomString(len);
+ }
+
+ /**
+ * Helper methods generates a random string by len
+ *
+ * @param len of the requested string
+ * @return random string
+ */
+ public static String randomString(final int len) {
+ final StringBuilder sb = new StringBuilder(len);
+ for (int i = 0; i < len; i++) {
+ sb.append(AB.charAt(RANDOM.nextInt(AB.length())));
+ }
+ return sb.toString();
+ }
+
+ /**
+ * Creates a secret by given len
+ *
+ * @param length of the secret
+ * @return created secret
+ */
+ public static String generateSecret(final int length) {
+ final SecureRandom random = new SecureRandom();
+
+ final byte[] bytes = new byte[length / 8];
+ random.nextBytes(bytes);
+
+ // return android.util.Base64.encodeToString(bytes, Base64.URL_SAFE);
+
+ return Base64.encodeBytes(bytes);
+ }
+
+ /**
+ * generates the signature
+ *
+ * @param message
+ * @param secret
+ * @return
+ */
+ public static String generateSignature(final String message, final String secret) {
+
+ if (message == null) {
+ throw new BreinException("Illegal value for message in method generateSignature");
+ }
+
+ if (secret == null) {
+ throw new BreinException("Illegal value for secret in method generateSignature");
+ }
+
+ try {
+ byte[] e = secret.getBytes(Charset.forName("UTF-8"));
+ SecretKeySpec secretKey = new SecretKeySpec(e, "HmacSHA256");
+ mac.init(secretKey);
+ byte[] hmacData = mac.doFinal(message.getBytes(Charset.forName("UTF-8")));
+
+ // return Base64.encodeToString(hmacData, Base64.DEFAULT);
+ // return android.util.Base64.encodeToString(hmacData, Base64.URL_SAFE);
+
+ return Base64.encodeBytes(hmacData);
+
+ } catch (InvalidKeyException var5) {
+ throw new IllegalStateException("Unable to create signature!", var5);
+ }
+
+ }
+
+ /**
+ * Validates if the URL is correct.
+ *
+ * @param url to check
+ * @return true if ok otherwise false
+ */
+ public static boolean isUrlValid(final String url) {
+ return true;
+ }
+
+ public static boolean isUrlValidNotWorkingOnAndroid(final String url) {
+
+ try {
+ final URL u = new URL(url);
+ HttpURLConnection huc = (HttpURLConnection) u.openConnection();
+ huc.setRequestMethod("POST");
+ huc.setRequestProperty("User-Agent",
+ "Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US; rv:1.9.1.2) Gecko/20090729 Firefox/3.5.2 (.NET CLR 3.5.30729)");
+
+ // Todo: this has caused issues on Android
+ huc.connect();
+ return true;
+
+ } catch (final IOException e) {
+
+ // this must be an error case!
+ return false;
+ }
+ }
+
+ /**
+ * checks if the url is valid -> if not an exception will be thrown
+ *
+ * @param fullyQualifiedUrl url with endpoint
+ */
+ public static void validateUrl(final String fullyQualifiedUrl) throws BreinException {
+
+ final boolean validUrl = isUrlValid(fullyQualifiedUrl);
+ if (!validUrl) {
+ final String msg = "URL: " + fullyQualifiedUrl + " is not valid!";
+ // Log.d("BreinUtil", msg);
+ throw new BreinException(msg);
+ }
+ }
+
+ /**
+ * validates the activity object
+ *
+ * @param breinBase object to validate
+ */
+ public static void validateBreinBase(final BreinBase breinBase) {
+ if (null == breinBase) {
+ // Log.d("BreinUtil", "Object is null");
+ throw new BreinException(BreinException.BREIN_BASE_VALIDATION_FAILED);
+ }
+ }
+
+ /**
+ * validates the configuration object
+ *
+ * @param breinBase activity or lookup object
+ */
+ public static void validateConfig(final BreinBase breinBase) {
+
+ final BreinConfig breinConfig = breinBase.getConfig();
+ if (null == breinConfig) {
+ // Log.d("BreinUtil", "within doRequestAsynch - breinConfig is null");
+ throw new BreinException(BreinException.CONFIG_VALIDATION_FAILED);
+ }
+ }
+
+ /**
+ * retrieves the fully qualified url (base + endpoint)
+ *
+ * @param breinBase activity or lookup object
+ * @return full url
+ */
+ public static String getFullyQualifiedUrl(final BreinBase breinBase) {
+ final BreinConfig breinConfig = breinBase.getConfig();
+
+ final String url = breinConfig.getUrl();
+ if (null == url) {
+ // Log.d("BreinUtil", "url is null");
+ throw new BreinException(BreinException.URL_IS_NULL);
+ }
+
+ final String endPoint = breinBase.getEndPoint();
+ return url + endPoint;
+ }
+
+ /**
+ * retrieves the request body depending of the object
+ *
+ * @param breinBase object to use
+ * @return request as json string
+ */
+ public static String getRequestBody(final BreinBase breinBase) {
+
+ final String requestBody = breinBase.prepareJsonRequest();
+ if (!BreinUtil.containsValue(requestBody)) {
+ // Log.d("BreinUtil", "url is null");
+ throw new BreinException(BreinException.REQUEST_BODY_FAILED);
+ }
+ return requestBody;
+ }
+
+ /**
+ * Invokes validation of BreinBase object, configuration and url.
+ *
+ * @param breinBase activity or lookup object
+ */
+ public static void validate(final BreinBase breinBase) {
+
+ // validation of activity and config
+ validateBreinBase(breinBase);
+ validateConfig(breinBase);
+
+ // validate URL, might throw an exception...
+ // validateUrl(getFullyQualifiedUrl(breinBase));
+ }
+
+
+}
diff --git a/brein-api-library-android/src/main/res/values/strings.xml b/brein-api-library-android/src/main/res/values/strings.xml
new file mode 100644
index 0000000..087ed5e
--- /dev/null
+++ b/brein-api-library-android/src/main/res/values/strings.xml
@@ -0,0 +1,3 @@
+
+ Brein-Api-Library-Android
+
diff --git a/brein-api-library-android/src/test/java/com/brein/activity/TestActivity.java b/brein-api-library-android/src/test/java/com/brein/activity/TestActivity.java
new file mode 100644
index 0000000..e44b174
--- /dev/null
+++ b/brein-api-library-android/src/test/java/com/brein/activity/TestActivity.java
@@ -0,0 +1,253 @@
+package com.brein.activity;
+
+import com.brein.api.BreinActivity;
+import com.brein.domain.BreinActivityType;
+import com.brein.domain.BreinConfig;
+import com.brein.domain.BreinUser;
+import com.brein.engine.BreinEngineType;
+
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Ignore;
+import org.junit.Test;
+
+import java.util.Properties;
+
+/**
+ * This test cases shows how to use the activity
+ */
+
+public class TestActivity {
+
+ /**
+ * Contains the BASE URL of the Breinify Backend
+ */
+ // private static final String BASE_URL = "http://dev.breinify.com/api";
+ private static final String BASE_URL = "https://api.breinify.com";
+
+ /**
+ * This has to be a valid api key
+ */
+ // private static final String VALID_API_KEY = "A187-B1DF-E3C5-4BDB-93C4-4729-7B54-E5B1";
+ private static final String VALID_API_KEY = "41B2-F48C-156A-409A-B465-317F-A0B4-E0E8";
+
+ private static final String VALID_SIGNATURE_API_KEY = "CA8A-8D28-3408-45A8-8E20-8474-06C0-8548";
+
+ /**
+ * Contains the Breinify User
+ */
+ private final BreinUser breinUser = new BreinUser("Marco.Recchioni@breinify.com");
+
+ /**
+ * Contains the Category
+ */
+ private final String breinCategoryType = "services";
+
+ /**
+ * Sign parameter
+ */
+ private final boolean sign = false;
+
+ /**
+ * The Activity itself
+ */
+ private final BreinActivity breinActivity = new BreinActivity();
+
+ /**
+ * Init part
+ */
+ @BeforeClass
+ public static void init() {
+
+ // set logging on
+ final Properties props = System.getProperties();
+ props.setProperty("org.slf4j.simpleLogger.defaultLogLevel", "DEBUG");
+
+ }
+
+ /**
+ * Preparation of test case
+ */
+ @Before
+ public void setUp() {
+
+ final BreinConfig breinConfig = new BreinConfig(VALID_API_KEY,
+ BASE_URL,
+ BreinEngineType.HTTP_URL_CONNECTION_ENGINE);
+
+ breinActivity.setConfig(breinConfig);
+ }
+
+ /**
+ * Housekeeping...
+ */
+ @AfterClass
+ public static void tearDown() {
+ /*
+ * we have to wait some time in order to allow the asynch rest processing
+ */
+ try {
+ /*
+ * TODO...
+ * Thread.sleep is not the best practice...
+ *
+ */
+ Thread.sleep(4000);
+
+
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ *
+ */
+ @After
+ public void wait4FiveSeconds() {
+ try {
+ Thread.sleep(5000);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+ /**
+ * testcase how to use the activity api
+ */
+ @Test
+ public void testLogin() {
+
+ final String description = "Login-Description";
+
+ /*
+ * additional user information
+ */
+ breinUser.setFirstName("Marco");
+ breinUser.setLastName("Recchioni");
+
+ /*
+ * invoke activity call
+ */
+ breinActivity.activity(breinUser,
+ BreinActivityType.LOGIN,
+ breinCategoryType, description, sign);
+ }
+
+ /**
+ * Invoke a test call with 200 logins
+ */
+ @Test
+ public void testWith20Logins() {
+
+ final int maxLogin = 20;
+
+ for (int index = 0; index < maxLogin; index++) {
+ testLogin();
+ }
+
+ }
+
+ /**
+ * test case how to invoke logout activity
+ */
+ @Test
+ public void testLogout() {
+
+ final String description = "Logout-Description";
+
+ /*
+ * additional user information
+ */
+ breinUser.setDateOfBirth(12, 31, 2008);
+
+ /*
+ * invoke activity call
+ */
+ breinActivity.activity(breinUser,
+ BreinActivityType.LOGOUT,
+ breinCategoryType, description, sign);
+ }
+
+ /**
+ * test case how to invoke search activity
+ */
+ @Test
+ public void testSearch() {
+
+ final String description = "Search-Description";
+
+ /*
+ * invoke activity call
+ */
+ breinActivity.activity(breinUser,
+ BreinActivityType.SEARCH,
+ breinCategoryType, description, sign);
+ }
+
+ /**
+ * test case how to invoke add-to-cart activity
+ */
+ @Test
+ public void testAddToCart() {
+
+ final String description = "AddToCart-Description";
+
+ /*
+ * invoke activity call
+ */
+ breinActivity.activity(breinUser,
+ BreinActivityType.ADD_TO_CART,
+ breinCategoryType, description, sign);
+ }
+
+ /**
+ * test case how to invoke remove-from-cart activity
+ */
+ @Test
+ public void testRemoveFromCart() {
+
+ final String description = "RemoveFromCart-Description";
+
+ /*
+ * invoke activity call
+ */
+ breinActivity.activity(breinUser,
+ BreinActivityType.REMOVE_FROM_CART,
+ breinCategoryType, description, sign);
+ }
+
+ /**
+ * test case how to invoke select product
+ */
+ @Test
+ public void testSelectProduct() {
+
+ final String description = "Select-Product-Description";
+
+ /*
+ * invoke activity call
+ */
+ breinActivity.activity(breinUser,
+ BreinActivityType.SELECT_PRODUCT,
+ breinCategoryType, description, sign);
+ }
+
+ /**
+ * test case how to invoke other
+ */
+ @Test
+ public void testOther() {
+
+ final String description = "Other-Description";
+
+ /*
+ * invoke activity call
+ */
+ breinActivity.activity(breinUser,
+ BreinActivityType.OTHER,
+ breinCategoryType, description, sign);
+ }
+
+}
diff --git a/brein-api-library-android/src/test/java/com/brein/api/TestApi.java b/brein-api-library-android/src/test/java/com/brein/api/TestApi.java
new file mode 100644
index 0000000..ab215dd
--- /dev/null
+++ b/brein-api-library-android/src/test/java/com/brein/api/TestApi.java
@@ -0,0 +1,854 @@
+package com.brein.api;
+
+import com.brein.domain.BreinActivityType;
+import com.brein.domain.BreinCategoryType;
+import com.brein.domain.BreinConfig;
+import com.brein.domain.BreinDimension;
+import com.brein.domain.BreinResult;
+import com.brein.domain.BreinUser;
+import com.brein.engine.BreinEngine;
+import com.brein.engine.BreinEngineType;
+
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Ignore;
+import org.junit.Test;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Properties;
+
+/**
+ * Test of Breinify Java API (static option)
+ */
+
+@Ignore
+public class TestApi {
+
+ /**
+ * Contains the BASE URL of the Breinify Backend
+ */
+ private static final String BASE_URL = "https://api.breinify.com";
+
+ /**
+ * This has to be a valid api key
+ */
+ private static final String VALID_API_KEY = "41B2-F48C-156A-409A-B465-317F-A0B4-E0E8";
+
+ private static final String VALID_SIGNATURE_API_KEY = "CA8A-8D28-3408-45A8-8E20-8474-06C0-8548";
+
+ /**
+ * Contains the Breinify User
+ */
+ private final BreinUser breinUser = new BreinUser("User.Name@email.com");
+
+ /**
+ * Contains the Category
+ */
+ private final String breinCategoryType = BreinCategoryType.HOME;
+
+ /**
+ * Contains the BreinActivityType
+ */
+ private final String breinActivityType = BreinActivityType.LOGIN;
+
+ /**
+ * Correct configuration
+ */
+ final BreinConfig breinConfig = new BreinConfig(VALID_API_KEY,
+ BASE_URL,
+ BreinEngineType.HTTP_URL_CONNECTION_ENGINE);
+
+ /**
+ * Init part
+ */
+ @BeforeClass
+ public static void setUp() {
+
+ // set logging on
+ final Properties props = System.getProperties();
+ props.setProperty("org.slf4j.simpleLogger.defaultLogLevel", "DEBUG");
+ }
+
+ /**
+ * Housekeeping...
+ */
+ @AfterClass
+ public static void tearDown() {
+
+ /*
+ * we have to wait some time in order to allow the asynch rest processing
+ */
+ try {
+ Thread.sleep(5000);
+ Breinify.shutdown();
+ } catch (final InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+
+ @After
+ public void waitSomeSeconds() {
+ try {
+ Thread.sleep(8000);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * testcase how to use the activity api
+ */
+ @Test
+ public void testLogin() {
+
+ /*
+ * set configuration
+ */
+ Breinify.setConfig(breinConfig);
+
+ /*
+ * additional optional user information
+ */
+ breinUser.setFirstName("User");
+ breinUser.setLastName("Name");
+
+ /*
+ * invoke activity call
+ */
+ Breinify.activity(breinUser,
+ breinActivityType,
+ breinCategoryType,
+ "Login-Description",
+ false);
+ }
+
+ /**
+ * testcase without category type
+ */
+ @Test
+ public void testWithoutCategoryType() {
+
+ /*
+ * set configuration
+ */
+ Breinify.setConfig(breinConfig);
+
+ /*
+ * additional optional user information
+ */
+ breinUser.setFirstName("User");
+ breinUser.setLastName("Name");
+
+ /*
+ * invoke activity call
+ */
+ Breinify.activity(breinUser,
+ breinActivityType,
+ null,
+ "Login-Description",
+ false);
+ }
+
+ /**
+ * Testcase with null value as apikey
+ */
+ @Test
+ public void testLoginWithNullApiKey() {
+
+ final String description = "Login-Description";
+ final boolean sign = false;
+
+ final BreinConfig config = new BreinConfig(null,
+ BASE_URL,
+ BreinEngineType.HTTP_URL_CONNECTION_ENGINE);
+
+ Breinify.setConfig(config);
+
+ /*
+ * additional user information
+ */
+ breinUser.setFirstName("User")
+ .setLastName("Name");
+
+ /*
+ * invoke activity call
+ */
+ Breinify.activity(breinUser,
+ breinActivityType,
+ breinCategoryType,
+ description,
+ sign);
+ }
+
+ /**
+ * Testcase with null value as base url
+ */
+ // @Test(expected= BreinInvalidConfigurationException.class)
+ public void testWithNullBaseUrl() {
+
+ final BreinConfig config = new BreinConfig(VALID_API_KEY,
+ null,
+ BreinEngineType.HTTP_URL_CONNECTION_ENGINE);
+
+ Breinify.setConfig(config);
+ }
+
+ /**
+ * Testcase with null rest engine. This will throw an
+ * exception.
+ */
+ @Test(expected= BreinException.class)
+ public void testLoginWithDefaultRestEngine() {
+
+ final String description = "Login-Description";
+ final boolean sign = false;
+
+
+ BreinConfig config = null;
+ try {
+ config = new BreinConfig(VALID_API_KEY,
+ BASE_URL,
+ BreinEngineType.NO_ENGINE);
+ } catch (final Exception e) {
+ e.printStackTrace();
+ }
+
+ Breinify.setConfig(config);
+
+ /*
+ * additional user information
+ */
+ breinUser.setFirstName("User");
+ breinUser.setLastName("Name");
+
+
+ /*
+ * invoke activity call
+ */
+ Breinify.activity(breinUser,
+ breinActivityType,
+ breinCategoryType,
+ description,
+ sign);
+
+ }
+
+ /**
+ * Test case with wrong endpoint configuration
+ */
+ @Test
+ public void testWithWrongEndPoint() {
+
+ final String description = "Login-Description";
+
+ /*
+ * set configuration
+ */
+ final BreinConfig config = new BreinConfig(VALID_API_KEY,
+ BASE_URL,
+ BreinEngineType.HTTP_URL_CONNECTION_ENGINE);
+ config.setActivityEndpoint("/wrongEndPoint");
+
+ Breinify.setConfig(config);
+
+ /*
+ * additional user information
+ */
+ breinUser.setFirstName("User");
+ breinUser.setLastName("Name");
+
+ /*
+ * invoke activity call
+ */
+ Breinify.activity(breinUser,
+ breinActivityType,
+ breinCategoryType,
+ description,
+ false);
+ }
+
+ /**
+ * Invoke a test call with 20 logins
+ */
+ @Test
+ public void testWith20Logins() {
+
+ final int maxLogin = 20;
+ for (int index = 0; index < maxLogin; index++) {
+ testLogin();
+ }
+
+ }
+
+ /**
+ * test case how to invoke logout activity
+ */
+ @Test
+ public void testLogout() {
+
+ final String description = "Logout-Description";
+
+ /*
+ * set configuration
+ */
+ Breinify.setConfig(breinConfig);
+
+ /*
+ * additional user information
+ */
+ breinUser.setDateOfBirth(12, 31, 2008);
+
+ /*
+ * invoke activity call
+ */
+ Breinify.activity(breinUser,
+ breinActivityType,
+ breinCategoryType,
+ description,
+ false);
+ }
+
+ /**
+ * test case how to invoke search activity
+ */
+ @Test
+ public void testSearch() {
+
+ final String description = "Search-Description";
+
+ /*
+ * set configuration
+ */
+ Breinify.setConfig(breinConfig);
+
+ /*
+ * invoke activity call
+ */
+ Breinify.activity(breinUser,
+ BreinActivityType.SEARCH,
+ breinCategoryType,
+ description,
+ false);
+ }
+
+ /**
+ * test case how to invoke addToCart activity
+ */
+ @Test
+ public void testAddToCart() {
+
+ final String description = "AddToCart-Description";
+
+ /*
+ * set configuration
+ */
+ Breinify.setConfig(breinConfig);
+
+ /*
+ * invoke activity call
+ */
+ Breinify.activity(breinUser,
+ BreinActivityType.ADD_TO_CART,
+ breinCategoryType,
+ description,
+ false);
+ }
+
+ /**
+ * test case how to invoke removeFromCart activity
+ */
+ @Test
+ public void testRemoveFromCart() {
+
+ final String description = "RemoveFromCart-Description";
+
+ /*
+ * set configuration
+ */
+ Breinify.setConfig(breinConfig);
+
+ /*
+ * invoke activity call
+ */
+ Breinify.activity(breinUser,
+ BreinActivityType.REMOVE_FROM_CART,
+ breinCategoryType,
+ description,
+ false);
+ }
+
+ /**
+ * test case how to invoke selectProduct activity
+ */
+ @Test
+ public void testSelectProduct() {
+
+ final String description = "Select-Product-Description";
+
+ /*
+ * set configuration
+ */
+ Breinify.setConfig(breinConfig);
+
+ /*
+ * invoke activity call
+ */
+ Breinify.activity(breinUser,
+ BreinActivityType.SELECT_PRODUCT,
+ breinCategoryType,
+ description,
+ false);
+ }
+
+ /**
+ * test case how to invoke other activity
+ */
+ @Test
+ public void testOther() {
+
+ final String description = "Other-Description";
+
+ /*
+ * set configuration
+ */
+ Breinify.setConfig(breinConfig);
+
+ /*
+ * invoke activity call
+ */
+ Breinify.activity(breinUser,
+ BreinActivityType.OTHER,
+ breinCategoryType,
+ description,
+ false);
+ }
+
+ /**
+ * test case how to invoke it with flebile values
+ */
+ @Test
+ public void testFlexibleValues() {
+
+ final String description = "Other-Description";
+
+ /*
+ * set configuration
+ */
+ Breinify.setConfig(breinConfig);
+
+ /*
+ * invoke activity call
+ */
+ Breinify.activity(breinUser,
+ BreinActivityType.OTHER,
+ breinCategoryType,
+ description,
+ false);
+ }
+
+ /**
+ * test case containing additional information
+ */
+ @Test
+ public void testPageVisit() {
+
+ // set configuration
+ final BreinConfig breinConfig = new BreinConfig(VALID_API_KEY,
+ BASE_URL,
+ BreinEngineType.HTTP_URL_CONNECTION_ENGINE);
+ Breinify.setConfig(breinConfig);
+
+ // user data
+ final BreinUser breinUser = new BreinUser("User.Name@email.com")
+ .setFirstName("Marco")
+ .setLastName("Recchioni")
+ .setDateOfBirth(11, 20, 1999)
+ .setDeviceId("DD-EEEEE")
+ .setImei("55544455333")
+ .setSessionId("r3V2kDAvFFL_-RBhuc_-Dg")
+ .setUrl("https://sample.com.au/home")
+ .setReferrer("https://sample.com.au/track")
+ .setIpAddress("10.11.12.130")
+ .setUserAgent("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2486.0 Safari/537.36 Edge/13.10586");
+
+
+ final Map tagMap = new HashMap<>();
+ tagMap.put("t1", 0.0);
+ tagMap.put("t2", 5);
+ tagMap.put("t3", "0.0");
+ tagMap.put("t4", 5.0000);
+ tagMap.put("nr", 3000);
+ tagMap.put("sortid", "1.0");
+
+ final BreinActivity breinActivity = Breinify.getBreinActivity();
+
+ // just in case you want to set the unixTimestamp
+ breinActivity.setUnixTimestamp(System.currentTimeMillis() / 1000L);
+ breinActivity.setBreinUser(breinUser);
+ breinActivity.setBreinCategoryType(BreinCategoryType.APPAREL);
+ breinActivity.setBreinActivityType(BreinActivityType.PAGEVISIT);
+ breinActivity.setDescription("your description");
+ breinActivity.setSign(false);
+ breinActivity.setTagsMap(tagMap);
+
+ Breinify.activity();
+ }
+
+ /**
+ * test case without having set the BreinUser.
+ * This will lead to an Exception.
+ */
+ //@Test(expected= BreinException.class)
+ public void testPageVisitWithException() {
+
+ // set configuration
+ final BreinConfig breinConfig = new BreinConfig(VALID_API_KEY,
+ BASE_URL,
+ BreinEngineType.HTTP_URL_CONNECTION_ENGINE);
+ Breinify.setConfig(breinConfig);
+
+ // user data
+ final BreinUser breinUser = new BreinUser("User.Name@email.com")
+ .setFirstName("User")
+ .setLastName("Name")
+ .setDateOfBirth(11, 20, 1999)
+ .setDeviceId("DD-EEEEE")
+ .setImei("55544455333")
+ .setSessionId("r3V2kDAvFFL_-RBhuc_-Dg")
+ .setUrl("https://sample.com.au/home")
+ .setReferrer("https://sample.com.au/track")
+ .setUserAgent("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2486.0 Safari/537.36 Edge/13.10586");
+
+ final BreinActivity breinActivity = Breinify.getBreinActivity();
+
+ breinActivity.setBreinCategoryType(BreinCategoryType.APPAREL);
+ breinActivity.setBreinActivityType(BreinActivityType.PAGEVISIT);
+ breinActivity.setDescription("your description");
+ breinActivity.setSign(false);
+
+ // user not set -> exception expected
+ Breinify.activity();
+ }
+
+ /**
+ * simply demonstrate the configuration of the engine
+ */
+ @Test
+ public void testConfiguration() {
+
+ final BreinEngine breinEngine = breinConfig.getBreinEngine();
+
+ /*
+ * set connection timeout to 30000 ms
+ */
+ breinConfig.setConnectionTimeout(30000);
+
+ /*
+ * set socket timeoiut to 25000 ms
+ */
+ breinConfig.setSocketTimeout(25000);
+
+ /*
+ * configure the engine
+ */
+ breinEngine.configure(breinConfig);
+ }
+
+ /**
+ * Test activity call with bad url
+ */
+ // @Test(expected= BreinInvalidConfigurationException.class)
+ public void testLoginWithBadUrl() {
+
+ /*
+ * Just to ensure that the right config has been set
+ */
+ final String badUrl = "www.beeeeeiiiniiify.com";
+
+ final BreinConfig breinConfigWithBadUrl = new BreinConfig(VALID_API_KEY,
+ badUrl,
+ BreinEngineType.HTTP_URL_CONNECTION_ENGINE);
+
+ Breinify.setConfig(breinConfigWithBadUrl);
+
+ /*
+ * invoke activity call, will cause an exception
+ */
+ Breinify.activity(breinUser,
+ BreinActivityType.LOGIN,
+ BreinCategoryType.HOME,
+ "Login-Description",
+ false);
+ }
+
+ /**
+ * Tests the lookup functionality
+ */
+ @Test
+ public void testLookup() {
+
+ final String[] dimensions = {"firstname",
+ "gender",
+ "age",
+ "agegroup",
+ "digitalfootprint",
+ "images"};
+
+ final BreinDimension breinDimension = new BreinDimension(dimensions);
+ final boolean sign = false;
+
+ /*
+ * set configuration
+ */
+ Breinify.setConfig(breinConfig);
+
+ /*
+ * invoke lookup
+ */
+ final BreinResult response = Breinify.lookup(breinUser, breinDimension, sign);
+ if (response != null) {
+ final Object dataFirstname = response.get("firstname");
+ final Object dataGender = response.get("gender");
+ final Object dataAge = response.get("age");
+ final Object dataAgeGroup = response.get("agegroup");
+ final Object dataDigitalFootprinting = response.get("digitalfootprint");
+ final Object dataImages = response.get("images");
+ }
+ }
+
+ /**
+ * Test a login activity with sign and correct secret
+ */
+ /**
+ * Test a login activity with sign and correct secret
+ */
+ @Test
+ public void testLoginWithSign() {
+
+ final String secret = "lmcoj4k27hbbszzyiqamhg==";
+ final boolean sign = true;
+
+ final BreinConfig breinConfig = new BreinConfig(VALID_SIGNATURE_API_KEY,
+ BASE_URL,
+ BreinEngineType.HTTP_URL_CONNECTION_ENGINE);
+
+ // set secret
+ breinConfig.setSecret(secret);
+
+ // set config
+ Breinify.setConfig(breinConfig);
+
+ // invoke activity call
+ Breinify.activity(breinUser,
+ "login",
+ "home",
+ "Login-Description",
+ sign);
+ }
+
+ /**
+ * Test a login activity with sign but wrong secret
+ */
+ @Test
+ public void testLoginWithSignButWrongSecret() {
+
+ final String wrongSecret = "ThisIsAWrongSecret";
+ final boolean sign = true;
+
+ final BreinConfig breinConfig = new BreinConfig(VALID_API_KEY,
+ BASE_URL,
+ BreinEngineType.HTTP_URL_CONNECTION_ENGINE);
+
+ // set secret
+ breinConfig.setSecret(wrongSecret);
+
+ // set config
+ Breinify.setConfig(breinConfig);
+
+ // invoke activity call
+ Breinify.activity(breinUser,
+ BreinActivityType.LOGIN,
+ BreinCategoryType.HOME,
+ "Login-Description",
+ sign);
+ }
+
+ /**
+ * Test a lookup with sign and correct secret
+ */
+ @Test
+ public void testLookupWithSign() {
+
+ final String secret = "p3rqlab6m7/172pdgiq6ng==";
+ final String[] dimensions = {"firstname",
+ "gender",
+ "age",
+ "agegroup",
+ "digitalfootprint",
+ "images"};
+
+ final BreinDimension breinDimension = new BreinDimension(dimensions);
+ final boolean sign = true;
+
+ final BreinConfig breinConfig = new BreinConfig(VALID_API_KEY,
+ BASE_URL,
+ BreinEngineType.HTTP_URL_CONNECTION_ENGINE);
+
+ // set secret
+ breinConfig.setSecret(secret);
+
+ /*
+ * set configuration
+ */
+ Breinify.setConfig(breinConfig);
+
+ /*
+ * invoke lookup
+ */
+ final BreinResult response = Breinify.lookup(breinUser, breinDimension, sign);
+ if (response != null) {
+ final Object dataFirstname = response.get("firstname");
+ final Object dataGender = response.get("gender");
+ final Object dataAge = response.get("age");
+ final Object dataAgeGroup = response.get("agegroup");
+ final Object dataDigitalFootprinting = response.get("digitalfootprint");
+ final Object dataImages = response.get("images");
+ }
+ }
+
+ /**
+ * Test a lookup with sign and correct secret
+ */
+ @Test
+ public void testLookupWithSignButWrongSecret() {
+
+ final String thisIsAWrongSecret = "ThisIsAWrongSecret";
+ final String[] dimensions = {"firstname",
+ "gender",
+ "age",
+ "agegroup",
+ "digitalfootprint",
+ "images"};
+
+ final BreinDimension breinDimension = new BreinDimension(dimensions);
+ final boolean sign = true;
+
+ final BreinConfig breinConfig = new BreinConfig(VALID_API_KEY,
+ BASE_URL,
+ BreinEngineType.HTTP_URL_CONNECTION_ENGINE);
+
+ // set secret
+ breinConfig.setSecret(thisIsAWrongSecret);
+
+ /*
+ * set configuration
+ */
+ Breinify.setConfig(breinConfig);
+
+ /*
+ * invoke lookup
+ */
+ final BreinResult response = Breinify.lookup(breinUser, breinDimension, sign);
+ if (response != null) {
+ final Object dataFirstname = response.get("firstname");
+ final Object dataGender = response.get("gender");
+ final Object dataAge = response.get("age");
+ final Object dataAgeGroup = response.get("agegroup");
+ final Object dataDigitalFootprinting = response.get("digitalfootprint");
+ final Object dataImages = response.get("images");
+ }
+ }
+
+ /**
+ * test case where an activity is sent without having set
+ * the category type for this particular activity object.
+ * In this case the default category type has to be used.
+ * If this is not set then the call needs to be rejected.
+ */
+ @Test
+ public void testActivityWithoutCategory() {
+
+ // create configuration
+ final BreinConfig breinConfig = new BreinConfig(VALID_API_KEY,
+ BASE_URL,
+ BreinEngineType.HTTP_URL_CONNECTION_ENGINE)
+ .setDefaultCategory("DEF-CAT-TYPE");
+
+ Breinify.setConfig(breinConfig);
+
+ // create user
+ final BreinUser breinUser = new BreinUser()
+ .setSessionId("SESS-ID-IS-THIS");
+
+ // send activity with all fields sets so far
+ Breinify.activity(breinUser, "ACT-TYPE", "CAT-TYPE", "DESC", false);
+
+ // send activity without CAT-TYPE => use default
+ Breinify.activity(breinUser, "ACT-TYPE", "", "DESC", false);
+
+ Breinify.activity(breinUser, "ACT-TYPE", null, "DESC", false);
+
+ // send activity by setting the breinActivity methods
+ BreinActivity breinActivity = Breinify.getBreinActivity()
+ .setBreinCategoryType(null)
+ .setBreinActivityType("ACTI-TYPE")
+ .setDescription("DESC");
+
+ Breinify.activity();
+
+ }
+
+ /**
+ * test case how to use the activity api
+ */
+ // @Test
+ public void testLoginWithPauseToCheckWifiIssue() {
+
+ // set configuration
+ Breinify.setConfig(breinConfig);
+
+ // additional optional user information
+ breinUser.setFirstName("User");
+ breinUser.setLastName("Name");
+
+ int index = 1;
+
+ while (true) {
+
+ try {
+
+ while (true) {
+
+ // invoke activity call
+ Breinify.activity(breinUser,
+ breinActivityType,
+ breinCategoryType,
+ "Login-Description",
+ false);
+
+ try {
+ System.out.println("Waiting: " + Integer.toString(index++));
+ Thread.sleep(2000);
+
+ } catch (final Exception e) {
+ e.printStackTrace();
+ }
+
+ }
+
+ } catch (final BreinException e) {
+ System.out.println("Exception is: " + e);
+ }
+ }
+ }
+
+
+ @Test
+ public void testForDoc() {
+
+
+ }
+
+}
diff --git a/brein-api-library-android/src/test/java/com/brein/api/TestExecutor.java b/brein-api-library-android/src/test/java/com/brein/api/TestExecutor.java
new file mode 100644
index 0000000..2f79bfe
--- /dev/null
+++ b/brein-api-library-android/src/test/java/com/brein/api/TestExecutor.java
@@ -0,0 +1,263 @@
+package com.brein.api;
+
+import com.brein.domain.BreinActivityType;
+import com.brein.domain.BreinCategoryType;
+import com.brein.domain.BreinConfig;
+import com.brein.domain.BreinDimension;
+import com.brein.domain.BreinResult;
+import com.brein.domain.BreinUser;
+import com.brein.engine.BreinEngineType;
+
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Ignore;
+import org.junit.Test;
+
+import java.util.Properties;
+
+/**
+ * Test of Breinify Java API (static option)
+ */
+@Ignore
+public class TestExecutor {
+
+ /**
+ * some constants for lookup
+ */
+ private static final String FIRSTNAME = "firstname";
+ private static final String GENDER = "gender";
+ private static final String AGE = "age";
+ private static final String AGEGROUP = "agegroup";
+ private static final String DIGITALFOOTPRINT = "digitalfootprint";
+ private static final String IMAGES = "images";
+
+ /**
+ * Contains the BASE URL of the Breinify Backend
+ */
+ private static final String BASE_URL = "https://api.breinify.com";
+
+ /**
+ * This has to be a valid api key
+ */
+ private static final String VALID_API_KEY = "41B2-F48C-156A-409A-B465-317F-A0B4-E0E8";
+
+ private static final String VALID_SIGNATURE_API_KEY = "CA8A-8D28-3408-45A8-8E20-8474-06C0-8548";
+
+ /**
+ * Contains the Breinify User
+ */
+ private final BreinUser breinUser = new BreinUser("User.Name@email.com");
+
+ /**
+ * Init part
+ */
+ @BeforeClass
+ public static void init() {
+
+ // set logging on
+ final Properties props = System.getProperties();
+ props.setProperty("org.slf4j.simpleLogger.defaultLogLevel", "DEBUG");
+
+ }
+
+ /**
+ * Housekeeping...
+ */
+ @AfterClass
+ public static void tearDown() {
+ /*
+ * we have to wait some time in order to allow the asynch rest processing
+ */
+ try {
+ Thread.sleep(1000);
+ } catch (final InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+
+ @After
+ public void wait4Seconds() {
+ try {
+ Thread.sleep(4000);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * testcase how to use the activity api
+ */
+ @Test
+ public void testPageVisit() {
+
+ /*
+ * additional user information
+ */
+ breinUser.setFirstName("User");
+ breinUser.setLastName("Name");
+ breinUser.setDateOfBirth(11, 20, 1999);
+ breinUser.setDeviceId("DD-EEEEE");
+ breinUser.setImei("55544455333");
+ breinUser.setSessionId("r3V2kDAvFFL_-RBhuc_-Dg");
+ breinUser.setUrl("https://sample.com.au/home");
+ breinUser.setReferrer("https://sample.com.au/track");
+ breinUser.setUserAgent("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2486.0 Safari/537.36 Edge/13.10586");
+
+ final BreinifyExecutor breinifyExecutor = new BreinConfig()
+ .setApiKey(VALID_API_KEY)
+ .setBaseUrl(BASE_URL)
+ .setRestEngineType(BreinEngineType.HTTP_URL_CONNECTION_ENGINE)
+ .build();
+
+ final BreinActivity breinActivity = breinifyExecutor.getBreinActivity();
+
+ breinActivity.setBreinUser(breinUser);
+ breinActivity.setBreinCategoryType(BreinCategoryType.APPAREL);
+ breinActivity.setBreinActivityType(BreinActivityType.PAGEVISIT);
+ breinActivity.setDescription("your description");
+ breinActivity.setSign(false);
+
+ /*
+ * invoke activity call
+ */
+ breinifyExecutor.activity();
+ }
+
+ /**
+ * testcase how to use the activity api
+ */
+ @Test
+ public void testLogin() {
+
+ /*
+ * additional user information
+ */
+ breinUser.setFirstName("User");
+ breinUser.setLastName("Name");
+
+ final BreinifyExecutor breinifyExecutor = new BreinConfig()
+ .setApiKey(VALID_API_KEY)
+ .setBaseUrl(BASE_URL)
+ .setRestEngineType(BreinEngineType.HTTP_URL_CONNECTION_ENGINE)
+ .build();
+
+ final String description = "your description";
+ /*
+ * invoke activity call
+ */
+ breinifyExecutor.activity(breinUser,
+ BreinActivityType.LOGIN,
+ BreinCategoryType.FOOD,
+ description,
+ false);
+ }
+
+ /**
+ * testcase how to use the activity api
+ */
+ @Test
+ public void testWitoutCategorySet() {
+
+ /*
+ * additional user information
+ */
+ breinUser.setFirstName("User");
+ breinUser.setLastName("Name");
+
+ final BreinifyExecutor breinifyExecutor = new BreinConfig()
+ .setApiKey(VALID_API_KEY)
+ .setBaseUrl(BASE_URL)
+ .setRestEngineType(BreinEngineType.HTTP_URL_CONNECTION_ENGINE)
+ .build();
+
+ final String description = "your description";
+ /*
+ * invoke activity call
+ */
+ breinifyExecutor.activity(breinUser,
+ BreinActivityType.LOGIN,
+ null,
+ description,
+ false);
+ }
+
+
+ /**
+ * Tests the lookup functionality
+ */
+ @Test
+ public void testLookup() {
+
+ final String[] dimensions = {
+ FIRSTNAME,
+ GENDER,
+ AGE,
+ AGEGROUP,
+ DIGITALFOOTPRINT,
+ IMAGES
+ };
+
+ final BreinDimension breinDimension = new BreinDimension(dimensions);
+ final boolean sign = false;
+
+ final BreinifyExecutor breinifyExecutor = new BreinConfig()
+ .setApiKey(VALID_API_KEY)
+ .setBaseUrl(BASE_URL)
+ .setRestEngineType(BreinEngineType.HTTP_URL_CONNECTION_ENGINE)
+ .build();
+
+ /*
+ * use Philipp, so can get some data
+ */
+ final BreinUser user = new BreinUser("user.name@email.com");
+
+ /*
+ * invoke lookup
+ */
+ final BreinResult result = breinifyExecutor.lookup(user,
+ breinDimension, sign);
+
+ if (result != null) {
+ final Object dataFirstname = result.get(FIRSTNAME);
+ final Object dataGender = result.get(GENDER);
+ final Object dataAge = result.get(AGE);
+ final Object dataAgeGroup = result.get(AGEGROUP);
+ final Object dataDigitalFootprinting = result.get(DIGITALFOOTPRINT);
+ final Object dataImages = result.get(IMAGES);
+ }
+
+ // assertNotEquals(null, dataFirstname);
+ }
+
+ /**
+ * Test activity call with bad url
+ */
+ // @Test(expected= BreinInvalidConfigurationException.class)
+ public void testLoginWithBadUrl() {
+
+ /*
+ * Just to ensure that the right config has been set
+ */
+ final String badUrl = "www.beeeeeiiiniiify.com";
+
+ final BreinifyExecutor breinifyExecutor = new BreinConfig()
+ .setApiKey(VALID_API_KEY)
+ .setBaseUrl(badUrl)
+ .setRestEngineType(BreinEngineType.HTTP_URL_CONNECTION_ENGINE)
+ .build();
+
+ breinifyExecutor.activity(breinUser,
+ BreinActivityType.LOGIN,
+ BreinCategoryType.FOOD,
+ "description",
+ false);
+
+ }
+
+ @Test
+ public void testForQA() {
+
+ }
+
+}
diff --git a/brein-api-library-android/src/test/java/com/brein/api/TestHttpUrlConnectionApi.java b/brein-api-library-android/src/test/java/com/brein/api/TestHttpUrlConnectionApi.java
new file mode 100644
index 0000000..09d78a5
--- /dev/null
+++ b/brein-api-library-android/src/test/java/com/brein/api/TestHttpUrlConnectionApi.java
@@ -0,0 +1,480 @@
+package com.brein.api;
+
+import com.brein.domain.BreinActivityType;
+import com.brein.domain.BreinCategoryType;
+import com.brein.domain.BreinConfig;
+import com.brein.domain.BreinDimension;
+import com.brein.domain.BreinResult;
+import com.brein.domain.BreinUser;
+import com.brein.engine.BreinEngine;
+import com.brein.engine.BreinEngineType;
+
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Ignore;
+import org.junit.Test;
+
+import java.util.Properties;
+
+/**
+ * Test of Breinify Java API (static option)
+ */
+@Ignore
+public class TestHttpUrlConnectionApi {
+
+ /**
+ * Contains the BASE URL of the Breinify Backend
+ */
+ // private static final String BASE_URL = "http://dev.breinify.com/api";
+ private static final String BASE_URL = "https://api.breinify.com";
+
+ /**
+ * This has to be a valid api key
+ */
+ // private static final String VALID_API_KEY = "A187-B1DF-E3C5-4BDB-93C4-4729-7B54-E5B1";
+
+ private static final String VALID_API_KEY = "41B2-F48C-156A-409A-B465-317F-A0B4-E0E8";
+
+ /**
+ * Contains the Breinify User
+ */
+ private final BreinUser breinUser = new BreinUser("philipp@meisen.net");
+
+ /**
+ * Contains the Category
+ */
+ private final String breinCategoryType = "home";
+
+ /**
+ * Correct configuration
+ */
+ final BreinConfig breinConfig = new BreinConfig(VALID_API_KEY,
+ BASE_URL,
+ BreinEngineType.HTTP_URL_CONNECTION_ENGINE);
+
+ /**
+ * Init part
+ */
+ @BeforeClass
+ public static void init() {
+
+ // set logging on
+ final Properties props = System.getProperties();
+ props.setProperty("org.slf4j.simpleLogger.defaultLogLevel", "DEBUG");
+ }
+
+ /**
+ * setup for each test
+ */
+ @Before
+ public void setUp() {
+
+ }
+
+ /**
+ * Housekeeping...
+ */
+ @AfterClass
+ public static void tearDown() {
+ try {
+ Thread.sleep(4000);
+ Breinify.shutdown();
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+
+
+ @After
+ public void wait5Seconds() {
+ try {
+ Thread.sleep(5000);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+ /**
+ * testcase how to use the activity api
+ */
+ @Test
+ public void testLogin() {
+
+ /*
+ * set configuration
+ */
+ Breinify.setConfig(breinConfig);
+
+ /*
+ * additional optional user information
+ */
+ breinUser.setFirstName("Marco");
+ breinUser.setLastName("Recchioni");
+
+ /*
+ * invoke activity call
+ */
+ Breinify.activity(breinUser,
+ BreinActivityType.LOGIN,
+ BreinCategoryType.HOME,
+ "Login-Description",
+ false);
+ }
+
+ /**
+ * Testcase with null value as apikey
+ */
+ @Test
+ public void testLoginWithNullApiKey() {
+
+ final String description = "Login-Description";
+ final boolean sign = false;
+
+ final BreinConfig config = new BreinConfig(null,
+ BASE_URL,
+ BreinEngineType.HTTP_URL_CONNECTION_ENGINE);
+
+ Breinify.setConfig(config);
+
+ /*
+ * additional user information
+ */
+ breinUser.setFirstName("Marco");
+ breinUser.setLastName("Recchioni");
+
+ /*
+ * invoke activity call
+ */
+ Breinify.activity(breinUser,
+ BreinActivityType.LOGIN,
+ breinCategoryType,
+ description,
+ sign);
+ }
+
+ /**
+ * Testcase with null value as base url
+ */
+ // @Test(expected= BreinInvalidConfigurationException.class)
+ public void testLoginWithNullBaseUrl() {
+
+ final String description = "Login-Description";
+ final boolean sign = false;
+
+ final BreinConfig config = new BreinConfig(VALID_API_KEY,
+ null,
+ BreinEngineType.HTTP_URL_CONNECTION_ENGINE);
+
+ Breinify.setConfig(config);
+
+ /*
+ * additional user information
+ */
+ breinUser.setFirstName("Marco");
+ breinUser.setLastName("Recchioni");
+
+ /*
+ * invoke activity call
+ */
+ Breinify.activity(breinUser,
+ BreinActivityType.LOGIN,
+ breinCategoryType,
+ description,
+ sign);
+
+ }
+
+ /**
+ * Testcase with null rest engine. This will throw an
+ * exception.
+ */
+ @Test(expected= BreinException.class)
+ public void testLoginWithNoRestEngine() {
+
+ final String description = "Login-Description";
+ final boolean sign = false;
+
+
+ BreinConfig config = null;
+ try {
+ config = new BreinConfig(VALID_API_KEY,
+ BASE_URL,
+ BreinEngineType.NO_ENGINE);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+
+ Breinify.setConfig(config);
+
+ /*
+ * additional user information
+ */
+ breinUser.setFirstName("Marco");
+ breinUser.setLastName("Recchioni");
+
+
+ /*
+ * invoke activity call
+ */
+ Breinify.activity(breinUser,
+ BreinActivityType.LOGIN,
+ breinCategoryType,
+ description,
+ sign);
+
+ }
+
+ /**
+ * Test case with wrong endpoint configuration
+ */
+ @Test
+ public void testWithWrongEndPoint() {
+
+ final String description = "Login-Description";
+
+ /*
+ * set configuration
+ */
+ BreinConfig config = new BreinConfig(VALID_API_KEY,
+ BASE_URL,
+ BreinEngineType.HTTP_URL_CONNECTION_ENGINE);
+ config.setActivityEndpoint("/wrongEndPoint");
+
+ Breinify.setConfig(config);
+
+ /*
+ * additional user information
+ */
+ breinUser.setFirstName("Marco");
+ breinUser.setLastName("Recchioni");
+
+ /*
+ * invoke activity call
+ */
+ Breinify.activity(breinUser,
+ BreinActivityType.LOGIN,
+ breinCategoryType,
+ description,
+ false);
+
+
+ }
+
+ /**
+ * Invoke a test call with 20 logins
+ */
+ @Test
+ public void testWith20Logins() {
+
+ final int maxLogin = 20;
+
+ for (int index = 0; index < maxLogin; index++) {
+ testLogin();
+ }
+ }
+
+ /**
+ * test case how to invoke logout activity
+ */
+ @Test
+ public void testLogout() {
+
+ final String description = "Logout-Description";
+
+ /*
+ * set configuration
+ */
+ Breinify.setConfig(breinConfig);
+
+ /*
+ * additional user information
+ */
+ breinUser.setDateOfBirth(12, 31, 2008);
+
+ /*
+ * invoke activity call
+ */
+ Breinify.activity(breinUser,
+ BreinActivityType.LOGOUT,
+ breinCategoryType,
+ description,
+ false);
+ }
+
+ /**
+ * TODO
+ */
+ @Test
+ public void testSearch() {
+
+ final String description = "Search-Description";
+
+ /*
+ * set configuration
+ */
+ Breinify.setConfig(breinConfig);
+
+ /*
+ * invoke activity call
+ */
+ Breinify.activity(breinUser,
+ BreinActivityType.SEARCH,
+ breinCategoryType,
+ description,
+ false);
+ }
+
+ /**
+ * TODO
+ */
+ @Test
+ public void testAddToCart() {
+
+ final String description = "AddToCart-Description";
+
+ /*
+ * set configuration
+ */
+ Breinify.setConfig(breinConfig);
+
+ /*
+ * invoke activity call
+ */
+ Breinify.activity(breinUser,
+ BreinActivityType.ADD_TO_CART,
+ breinCategoryType,
+ description,
+ false);
+ }
+
+ /**
+ * TODO
+ */
+ @Test
+ public void testRemoveFromCart() {
+
+ final String description = "RemoveFromCart-Description";
+
+ /*
+ * set configuration
+ */
+ Breinify.setConfig(breinConfig);
+
+ /*
+ * invoke activity call
+ */
+ Breinify.activity(breinUser,
+ BreinActivityType.REMOVE_FROM_CART,
+ breinCategoryType,
+ description,
+ false);
+ }
+
+ /**
+ * TODO
+ */
+ @Test
+ public void testSelectProduct() {
+
+ final String description = "Select-Product-Description";
+
+ /*
+ * set configuration
+ */
+ Breinify.setConfig(breinConfig);
+
+ /*
+ * invoke activity call
+ */
+ Breinify.activity(breinUser,
+ BreinActivityType.SELECT_PRODUCT,
+ breinCategoryType,
+ description,
+ false);
+ }
+
+ /**
+ * TODO
+ */
+ @Test
+ public void testOther() {
+
+ final String description = "Other-Description";
+
+ /*
+ * set configuration
+ */
+ Breinify.setConfig(breinConfig);
+
+ /*
+ * invoke activity call
+ */
+ Breinify.activity(breinUser,
+ BreinActivityType.OTHER,
+ breinCategoryType,
+ description,
+ false);
+ }
+
+ /**
+ * simply demonstrate the configuration of the engine
+ */
+ @Test
+ public void testConfiguration() {
+
+ final BreinEngine breinEngine = breinConfig.getBreinEngine();
+
+ /*
+ * set connection timeout to 30000 ms
+ */
+ breinConfig.setConnectionTimeout(30000);
+
+ /*
+ * set socket timeout to 25000 ms
+ */
+ breinConfig.setSocketTimeout(25000);
+
+ /*
+ * configure the engine
+ */
+ breinEngine.configure(breinConfig);
+ }
+
+ /**
+ * Tests the lookup functionality
+ */
+ // @Test
+ public void testLookup() {
+
+ final String[] dimensions = {"firstname",
+ "gender",
+ "age",
+ "agegroup",
+ "digitalfootprint",
+ "images"};
+
+ final BreinDimension breinDimension = new BreinDimension(dimensions);
+
+ // configuration
+ Breinify.setConfig(breinConfig);
+
+ // invoke lookup
+ final BreinResult response = Breinify.lookup(breinUser, breinDimension, false);
+
+ while(true) {
+ ;
+ }
+
+ /*
+ final Object dataFirstname = response.get("firstname");
+ final Object dataGender = response.get("gender");
+ final Object dataAge = response.get("age");
+ final Object dataAgeGroup = response.get("agegroup");
+ final Object dataDigitalFootprinting = response.get("digitalfootprint");
+ final Object dataImages = response.get("images");
+ */
+
+ }
+
+}
diff --git a/brein-api-library-android/src/test/java/com/brein/config/TestConfig.java b/brein-api-library-android/src/test/java/com/brein/config/TestConfig.java
new file mode 100755
index 0000000..3784428
--- /dev/null
+++ b/brein-api-library-android/src/test/java/com/brein/config/TestConfig.java
@@ -0,0 +1,140 @@
+package com.brein.config;
+
+import com.brein.api.BreinActivity;
+import com.brein.domain.BreinConfig;
+import com.brein.engine.BreinEngineType;
+
+import org.junit.BeforeClass;
+import org.junit.Ignore;
+import org.junit.Test;
+
+import java.util.Properties;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Test of configuration
+ */
+@Ignore
+public class TestConfig {
+ private static final String testApiKey = "TEST-API-KEY";
+
+ /**
+ * Init part
+ */
+ @BeforeClass
+ public static void setUp() {
+
+ // set logging on
+ final Properties props = System.getProperties();
+ props.setProperty("org.slf4j.simpleLogger.defaultLogLevel", "DEBUG");
+
+ }
+
+ /**
+ * Test of Breinify class with empty configuration api-key
+ */
+ @Test
+ public void testEmptyConfig() {
+
+ final String emptyString = "";
+ final BreinConfig breinConfig = new BreinConfig();
+ breinConfig.setApiKey(emptyString);
+
+ final BreinActivity breinActivity = new BreinActivity();
+ breinActivity.setConfig(breinConfig);
+ assertNull(breinActivity.getConfig().getApiKey());
+ }
+
+ /**
+ * Test of Breinify class with null configuration api-key
+ */
+ @Test
+ public void testNullConfig() {
+
+ final BreinConfig breinConfig = new BreinConfig();
+ breinConfig.setApiKey(null);
+ final BreinActivity breinActivity = new BreinActivity();
+ breinActivity.setConfig(breinConfig);
+ assertNull(breinActivity.getConfig().getApiKey());
+ }
+
+ /**
+ * This should be the normal configuration methods
+ */
+ @Test
+ public void testNormalConfigUsage() {
+
+ final BreinConfig breinConfig = new BreinConfig();
+ final String validApiKey = "9D9C-C9E9-BC93-4D1D-9A61-3A0F-9BD9-CF14";
+ breinConfig.setApiKey(validApiKey);
+
+ final BreinActivity breinActivity = new BreinActivity();
+ breinActivity.setConfig(breinConfig);
+
+ // TODO
+ // assertTrue(!Objects.equals(breinActivity.getConfig().getApiKey(), ""));
+ }
+
+ /**
+ * Test a config with a wrong url
+ */
+ //@Test(expected= BreinInvalidConfigurationException.class)
+ public void testConfigWithWrongUrl() {
+
+ final String wrongUrl = "https://breeeeeinify.com";
+
+ final BreinConfig breinConfig = new BreinConfig(testApiKey,
+ wrongUrl,
+ BreinEngineType.HTTP_URL_CONNECTION_ENGINE);
+
+ final boolean isValid = breinConfig.isUrlValid(wrongUrl);
+ assertFalse(isValid);
+ }
+
+ /**
+ * Test a config with a correct url
+ */
+ @Test
+ public void testConfigWithCorrectUrl() {
+
+ final String correctUrl = "http://google.com";
+
+ final BreinConfig breinConfig = new BreinConfig(testApiKey,
+ correctUrl,
+ BreinEngineType.HTTP_URL_CONNECTION_ENGINE);
+
+ final boolean isValid = breinConfig.isUrlValid(correctUrl);
+ assertTrue(isValid);
+ }
+
+ /**
+ * Tests if both Breinify URL's are reachable:
+ * https://api.breinify.com
+ * http://api.breinify.com
+ */
+ @Test
+ public void testBreinifyUrls() {
+
+ final String httpsUrl = "https://api.breinify.com";
+ final String httpUrl = "http://api.breinify.com";
+
+ // HTTPS
+ final BreinConfig breinConfigHttps = new BreinConfig(testApiKey,
+ httpsUrl,
+ BreinEngineType.HTTP_URL_CONNECTION_ENGINE);
+ final boolean isHttpsValid = breinConfigHttps.isUrlValid(httpsUrl);
+ assertTrue(isHttpsValid);
+
+ // HTTP
+ final BreinConfig breinConfigHttp = new BreinConfig(testApiKey,
+ httpUrl,
+ BreinEngineType.HTTP_URL_CONNECTION_ENGINE);
+ final boolean isHttpValid = breinConfigHttps.isUrlValid(httpUrl);
+ assertTrue(isHttpValid);
+ }
+
+}
+
diff --git a/brein-api-library-android/src/test/java/com/brein/domain/TestDomain.java b/brein-api-library-android/src/test/java/com/brein/domain/TestDomain.java
new file mode 100755
index 0000000..ff069e6
--- /dev/null
+++ b/brein-api-library-android/src/test/java/com/brein/domain/TestDomain.java
@@ -0,0 +1,173 @@
+package com.brein.domain;
+
+import com.brein.api.BreinActivity;
+
+import org.junit.BeforeClass;
+import org.junit.Ignore;
+import org.junit.Test;
+
+import java.util.Properties;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Test classes for the domain objects
+ */
+@Ignore
+public class TestDomain {
+
+ /**
+ * Init part
+ */
+ @BeforeClass
+ public static void setUp() {
+
+ // set logging on
+ final Properties props = System.getProperties();
+ props.setProperty("org.slf4j.simpleLogger.defaultLogLevel", "DEBUG");
+ }
+
+ /**
+ * creates a brein request object that will be used within the body
+ * of the request
+ */
+ @Test
+ public void testBreinRequest() {
+ final BreinConfig breinConfig = new BreinConfig();
+ final String validApiKey = "9D9C-C9E9-BC93-4D1D-9A61-3A0F-9BD9-CF14";
+ breinConfig.setApiKey(validApiKey);
+
+ final BreinUser breinUser = new BreinUser("toni.maroni@mail.com")
+ .setFirstName("Toni")
+ .setLastName("Maroni");
+
+ final BreinActivity breinActivity = new BreinActivity();
+ breinActivity.setConfig(breinConfig);
+ breinActivity.setBreinUser(breinUser);
+ breinActivity.setBreinActivityType(BreinActivityType.LOGIN);
+ breinActivity.setDescription("Super-Description");
+ breinActivity.setBreinCategoryType(BreinCategoryType.HOME);
+
+ final String jsonOutput = breinActivity.prepareJsonRequest();
+ assertTrue(jsonOutput.length() > 0);
+ }
+
+ /**
+ * creates a brein request object that will be used within the body
+ * of the request but with less data
+ */
+ @Test
+ public void testBreinRequestWithLessData() {
+ final BreinConfig breinConfig = new BreinConfig();
+ final String validApiKey = "9D9C-C9E9-BC93-4D1D-9A61-3A0F-9BD9-CF14";
+ breinConfig.setApiKey(validApiKey);
+
+ final BreinUser breinUser = new BreinUser();
+
+ final BreinActivity breinActivity = new BreinActivity();
+ breinActivity.setConfig(breinConfig);
+ breinActivity.setBreinUser(breinUser);
+ breinActivity.setBreinActivityType(BreinActivityType.LOGIN);
+ breinActivity.setDescription("Super-Description");
+ breinActivity.setBreinCategoryType(BreinCategoryType.FOOD);
+
+ final String jsonOutput = breinActivity.prepareJsonRequest();
+ assertTrue(jsonOutput.length() > 0);
+ }
+
+ /**
+ * Test the birthday settings
+ */
+ @Test
+ public void testBirthday() {
+
+ final BreinUser breinUser = new BreinUser();
+
+ // set right values
+ breinUser.setDateOfBirth(1, 22, 1966); // this is correct date
+ assertFalse(breinUser.getDateOfBirth().isEmpty());
+
+ // set wrong day
+ breinUser.resetDateOfBirth();
+ breinUser.setDateOfBirth(1, 77, 1966); // this is wrong date
+ assertTrue(breinUser.getDateOfBirth().isEmpty());
+
+ // set wrong month
+ breinUser.resetDateOfBirth();
+ breinUser.setDateOfBirth(13, 22, 1966); // this is correct date
+ assertTrue(breinUser.getDateOfBirth().isEmpty());
+
+ // set wrong year
+ breinUser.resetDateOfBirth();
+ breinUser.setDateOfBirth(1, 22, 1700);
+ assertTrue(breinUser.getDateOfBirth().isEmpty());
+ }
+
+ /**
+ * Tests all BreinUser Methods
+ */
+ @Test
+ public void testBreinUserMethods() {
+
+ final BreinUser breinUser = new BreinUser()
+ .setFirstName("User")
+ .setLastName("Anyhere")
+ .setImei("356938035643809")
+ .setDateOfBirth(6, 20, 1985)
+ .setDeviceId("AAAAAAAAA-BBBB-CCCC-1111-222222220000");
+
+ assertFalse(breinUser.toString().isEmpty());
+ }
+
+ /**
+ * Tests all BreinUser Methods
+ */
+ @Test
+ public void testBreinUserWithNoMethods() {
+
+ final BreinUser breinUser = new BreinUser();
+ assertFalse(breinUser.toString().isEmpty());
+ }
+
+ /**
+ * Test of breinActivityType options
+ */
+ @Test
+ public void testBreinActivityTypeSetToPredefinedString() {
+
+ final String breinActivityType = BreinActivityType.CHECKOUT;
+ assertTrue(breinActivityType.equals(BreinActivityType.CHECKOUT));
+ }
+
+ /**
+ * Test of breinActivityType options
+ */
+ @Test
+ public void testBreinActivityTypeSetToAnyString() {
+
+ final String breinActivityType = "whatYouWant";
+ assertTrue(breinActivityType.equals("whatYouWant"));
+ }
+
+ /**
+ * Test of breinCategory options to predefined string
+ */
+ @Test
+ public void testBreinCategoryTypeSetToPredefinedString() {
+
+ final String breinCategoryType = BreinCategoryType.APPAREL;
+ assertTrue(breinCategoryType.equals(BreinCategoryType.APPAREL));
+ }
+
+ /**
+ * Test of breinCategory options to flexible string
+ */
+ @Test
+ public void testBreinCategoryTypeSetToFlexibleString() {
+
+ final String breinCategoryType = "flexibleString";
+ assertTrue(breinCategoryType.equals("flexibleString"));
+ }
+
+}
diff --git a/brein-api-library-android/src/test/java/com/brein/engine/TestEngine.java b/brein-api-library-android/src/test/java/com/brein/engine/TestEngine.java
new file mode 100644
index 0000000..1425e14
--- /dev/null
+++ b/brein-api-library-android/src/test/java/com/brein/engine/TestEngine.java
@@ -0,0 +1,49 @@
+package com.brein.engine;
+
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+
+
+/**
+ * Run some tests for the UNIREST API
+ */
+
+@Ignore
+public class TestEngine {
+
+ /**
+ * this is my test api key
+ */
+ private static final String API_KEY = "A187-B1DF-E3C5-4BDB-93C4-4729-7B54-E5B1";
+
+
+
+ /**
+ * Preparation for the test cases
+ */
+ @Before
+ public void setUp() {
+ }
+
+ /**
+ * Housekeeping...
+ */
+ @AfterClass
+ public static void tearDown() {
+
+
+ }
+
+ /**
+ * This should run some tests for the jersey client api...
+ */
+ @Test
+ public void testJerseyRestEngine() {
+
+ }
+
+
+
+}
diff --git a/brein-api-library-android/src/test/java/com/brein/lookup/TestLookup.java b/brein-api-library-android/src/test/java/com/brein/lookup/TestLookup.java
new file mode 100644
index 0000000..369ad4c
--- /dev/null
+++ b/brein-api-library-android/src/test/java/com/brein/lookup/TestLookup.java
@@ -0,0 +1,104 @@
+package com.brein.lookup;
+
+import com.brein.api.BreinLookup;
+import com.brein.domain.BreinConfig;
+import com.brein.domain.BreinDimension;
+import com.brein.domain.BreinResult;
+import com.brein.domain.BreinUser;
+import com.brein.engine.BreinEngineType;
+
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+
+import static junit.framework.TestCase.fail;
+
+/**
+ * Test cases for lookup functionality
+ */
+@Ignore
+public class TestLookup {
+
+ /**
+ * Contains the BASE URL of the Breinify Backend
+ */
+ // private static final String BASE_URL = "http://dev.breinify.com/api";
+ private static final String BASE_URL = "https://api.breinify.com";
+
+ /**
+ * This has to be a valid api key
+ */
+ // private static final String VALID_API_KEY = "A187-B1DF-E3C5-4BDB-93C4-4729-7B54-E5B1";
+
+ private static final String VALID_API_KEY = "41B2-F48C-156A-409A-B465-317F-A0B4-E0E8";
+
+ /**
+ * Contains the Breinify User
+ */
+ private final BreinUser breinUser = new BreinUser("philipp@meisen.net");
+
+ /**
+ * The Lookup itself
+ */
+ private final BreinLookup breinLookup = new BreinLookup();
+
+ /**
+ * Preparation of test case
+ */
+ @Before
+ public void setUp() {
+
+ final BreinConfig breinConfig = new BreinConfig(VALID_API_KEY,
+ BASE_URL,
+ BreinEngineType.HTTP_URL_CONNECTION_ENGINE);
+
+ breinLookup.setConfig(breinConfig);
+ }
+
+ /**
+ * Housekeeping...
+ */
+ @AfterClass
+ public static void tearDown() {
+ /**
+ * we have to wait some time in order to allow the asynch rest processing
+ */
+ try {
+ /**
+ * TODO...
+ * Thread.sleep is not the best practice...
+ *
+ */
+ Thread.sleep(4000);
+ } catch (InterruptedException e) {
+ fail();
+ }
+ }
+
+ /**
+ * Tests the lookup functionality
+ *
+ */
+ @Test
+ public void testLookup() {
+
+ final String[] dimensions = {"firstname", "gender",
+ "age", "agegroup", "digitalfootprint", "images"};
+
+ final BreinDimension breinDimension = new BreinDimension(dimensions);
+
+ /**
+ * invoke lookup
+ */
+ final BreinResult breinResult = breinLookup.lookUp(breinUser, breinDimension, false);
+ if (breinResult != null) {
+ final Object dataFirstname = breinResult.get("firstname");
+ final Object dataGender = breinResult.get("gender");
+ final Object dataAge = breinResult.get("age");
+ final Object dataAgeGroup = breinResult.get("agegroup");
+ final Object dataDigitalFootprinting = breinResult.get("digitalfootprint");
+ final Object dataImages = breinResult.get("digitalfootprint");
+ }
+ }
+}
diff --git a/brein-api-library-android/src/test/java/com/brein/util/TestUtil.java b/brein-api-library-android/src/test/java/com/brein/util/TestUtil.java
new file mode 100644
index 0000000..84aa878
--- /dev/null
+++ b/brein-api-library-android/src/test/java/com/brein/util/TestUtil.java
@@ -0,0 +1,31 @@
+package com.brein.util;
+
+import org.junit.Ignore;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Created by marcorecchioni
+ */
+@Ignore
+public class TestUtil {
+
+
+ @Test
+ public void testSignature() {
+
+ assertEquals("h5HRhGRwWlRs9pscyHhQWNc7pxnDOwDZBIAnnhEQbrU=",
+ BreinUtil.generateSignature("apiKey", "secretkey"));
+
+ final String secret = BreinUtil.generateSecret(128);
+ for (int i = 0; i < 100; i++) {
+
+ // this is our test case, for the message 'apiKey' and secret 'secretKey'
+ // it must return the used signature, the message should vary on each
+ // request
+ BreinUtil.generateSignature(BreinUtil.randomString(), secret);
+ }
+ }
+
+}
diff --git a/build.gradle b/build.gradle
new file mode 100644
index 0000000..5f50a0d
--- /dev/null
+++ b/build.gradle
@@ -0,0 +1,34 @@
+// Top-level build file where you can add configuration options common to all sub-projects/modules.
+
+buildscript {
+ repositories {
+ jcenter()
+
+ maven {
+ url "https://plugins.gradle.org/m2/"
+ }
+ }
+ dependencies {
+ classpath 'com.android.tools.build:gradle:2.2.0'
+ classpath "com.github.jengelman.gradle.plugins:shadow:1.2.3"
+
+ // NOTE: Do not place your application dependencies here; they belong
+ // in the individual module build.gradle files
+ }
+}
+
+apply plugin: "com.github.johnrengelman.shadow"
+
+allprojects {
+ repositories {
+ jcenter()
+
+ maven {
+ url "https://plugins.gradle.org/m2/"
+ }
+ }
+}
+
+task clean(type: Delete) {
+ delete rootProject.buildDir
+}
diff --git a/build/generated/mockable-android-24.jar b/build/generated/mockable-android-24.jar
new file mode 100644
index 0000000..2808dc5
Binary files /dev/null and b/build/generated/mockable-android-24.jar differ
diff --git a/build/intermediates/dex-cache/cache.xml b/build/intermediates/dex-cache/cache.xml
new file mode 100644
index 0000000..386435e
--- /dev/null
+++ b/build/intermediates/dex-cache/cache.xml
@@ -0,0 +1,167 @@
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
diff --git a/build/intermediates/lint-cache/api-versions-8-24.0.2.bin b/build/intermediates/lint-cache/api-versions-8-24.0.2.bin
new file mode 100644
index 0000000..feeae89
Binary files /dev/null and b/build/intermediates/lint-cache/api-versions-8-24.0.2.bin differ
diff --git a/documentation/img/logo.png b/documentation/img/logo.png
new file mode 100644
index 0000000..94105f3
Binary files /dev/null and b/documentation/img/logo.png differ
diff --git a/gradle.properties b/gradle.properties
new file mode 100644
index 0000000..1d3591c
--- /dev/null
+++ b/gradle.properties
@@ -0,0 +1,18 @@
+# Project-wide Gradle settings.
+
+# IDE (e.g. Android Studio) users:
+# Gradle settings configured through the IDE *will override*
+# any settings specified in this file.
+
+# For more details on how to configure your build environment visit
+# http://www.gradle.org/docs/current/userguide/build_environment.html
+
+# Specifies the JVM arguments used for the daemon process.
+# The setting is particularly useful for tweaking memory settings.
+# Default value: -Xmx10248m -XX:MaxPermSize=256m
+# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
+
+# When configured, Gradle will run in incubating parallel mode.
+# This option should only be used with decoupled projects. More details, visit
+# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
+# org.gradle.parallel=true
\ No newline at end of file
diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..13372ae
Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..97c8d5d
--- /dev/null
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Sun Sep 18 02:25:04 CEST 2016
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip
diff --git a/gradlew b/gradlew
new file mode 100755
index 0000000..9d82f78
--- /dev/null
+++ b/gradlew
@@ -0,0 +1,160 @@
+#!/usr/bin/env bash
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn ( ) {
+ echo "$*"
+}
+
+die ( ) {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+esac
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=$((i+1))
+ done
+ case $i in
+ (0) set -- ;;
+ (1) set -- "$args0" ;;
+ (2) set -- "$args0" "$args1" ;;
+ (3) set -- "$args0" "$args1" "$args2" ;;
+ (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
+function splitJvmOpts() {
+ JVM_OPTS=("$@")
+}
+eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
+JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
+
+exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
diff --git a/gradlew.bat b/gradlew.bat
new file mode 100644
index 0000000..8a0b282
--- /dev/null
+++ b/gradlew.bat
@@ -0,0 +1,90 @@
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windowz variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+if "%@eval[2+2]" == "4" goto 4NT_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+goto execute
+
+:4NT_args
+@rem Get arguments from the 4NT Shell from JP Software
+set CMD_LINE_ARGS=%$
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/local.properties b/local.properties
new file mode 100644
index 0000000..8c0cd90
--- /dev/null
+++ b/local.properties
@@ -0,0 +1,11 @@
+## This file is automatically generated by Android Studio.
+# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
+#
+# This file must *NOT* be checked into Version Control Systems,
+# as it contains information specific to your local configuration.
+#
+# Location of the SDK. This is only used by Gradle.
+# For customization when using a Version Control System, please read the
+# header note.
+#Tue Sep 20 16:47:43 CEST 2016
+sdk.dir=/Users/marco/Library/Android/sdk
diff --git a/settings.gradle b/settings.gradle
new file mode 100644
index 0000000..ff45590
--- /dev/null
+++ b/settings.gradle
@@ -0,0 +1 @@
+include ':app', ':brein-api-library-android'