/* * Copyright 2010-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. * A copy of the License is located at * * http://aws.amazon.com/apache2.0 * * or in the "license" file accompanying this file. This file is distributed * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either * express or implied. See the License for the specific language governing * permissions and limitations under the License. */ package com.amazonaws.mobileconnectors.amazonmobileanalytics.internal.event; import android.util.Log; import com.amazonaws.mobileconnectors.amazonmobileanalytics.AnalyticsEvent; import com.amazonaws.mobileconnectors.amazonmobileanalytics.internal.core.idresolver.Id; import com.amazonaws.mobileconnectors.amazonmobileanalytics.internal.core.util.StringUtil; import org.json.JSONObject; import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; public class EventConstraintDecorator implements InternalEvent { private static class Pair<T, U> { private T key; private U value; private T getKey() { return key; } private U getValue() { return value; } public Pair(T key, U value) { this.key = key; this.value = value; } } private static final String TAG = "EventConstraintDecorator"; static final int MAX_EVENT_ATTRIBUTE_METRIC_KEY_LENGTH = 40; static final int MAX_EVENT_ATTRIBUTE_VALUE_LENGTH = 200; static final int MAX_NUM_OF_METRICS_AND_ATTRIBUTES = 50; private final InternalEvent decoratedEvent; private final int maxAttributesAndMetrics; private final AtomicInteger currentNumOfAttributesAndMetrics = new AtomicInteger(0); public static EventConstraintDecorator newInstance(InternalEvent eventToDecorate) { return new EventConstraintDecorator(eventToDecorate, MAX_NUM_OF_METRICS_AND_ATTRIBUTES); } public EventConstraintDecorator(InternalEvent eventToDecorate, int maxAttributesAndMetrics) { this.decoratedEvent = eventToDecorate; this.maxAttributesAndMetrics = maxAttributesAndMetrics; } @Override public synchronized void addAttribute(String name, String value) { if (name != null && value != null) { if (currentNumOfAttributesAndMetrics.get() < maxAttributesAndMetrics && !decoratedEvent.hasAttribute(name)) { Pair<String, String> trimmedResult = processAttribute(name, value); decoratedEvent.addAttribute(trimmedResult.getKey(), trimmedResult.getValue()); currentNumOfAttributesAndMetrics.incrementAndGet(); } else if (decoratedEvent.hasAttribute(name)) { Pair<String, String> trimmedResult = processAttribute(name, value); decoratedEvent.addAttribute(trimmedResult.getKey(), trimmedResult.getValue()); } } } @Override public boolean hasAttribute(String attributeName) { boolean hasAttribute = false; hasAttribute = decoratedEvent.hasAttribute(attributeName); return hasAttribute; } @Override public synchronized void addMetric(String name, Double value) { if (name != null && value != null) { if (currentNumOfAttributesAndMetrics.get() < maxAttributesAndMetrics && !decoratedEvent.hasMetric(name)) { Pair<String, Double> trimmedResult = processMetric(name, value); decoratedEvent.addMetric(trimmedResult.getKey(), trimmedResult.getValue()); currentNumOfAttributesAndMetrics.incrementAndGet(); } else if (decoratedEvent.hasMetric(name)) { Pair<String, Double> trimmedResult = processMetric(name, value); decoratedEvent.addMetric(trimmedResult.getKey(), trimmedResult.getValue()); } } } @Override public boolean hasMetric(String metricName) { boolean hasMetric = false; hasMetric = decoratedEvent.hasMetric(metricName); return hasMetric; } @Override public String getEventType() { return decoratedEvent.getEventType(); } @Override public String getAttribute(String name) { return decoratedEvent.getAttribute(name); } @Override public Double getMetric(String name) { return decoratedEvent.getMetric(name); } @Override public AnalyticsEvent withAttribute(String name, String value) { addAttribute(name, value); return decoratedEvent; } @Override public AnalyticsEvent withMetric(String name, Double value) { addMetric(name, value); return decoratedEvent; } @Override public Map<String, String> getAllAttributes() { return decoratedEvent.getAllAttributes(); } @Override public Map<String, Double> getAllMetrics() { return decoratedEvent.getAllMetrics(); } private static Pair<String, String> processAttribute(String key, String value) { String trimmedKey = StringUtil .clipString(key, MAX_EVENT_ATTRIBUTE_METRIC_KEY_LENGTH, false); if (trimmedKey.length() < key.length()) { Log.w(TAG, "The attribute key has been trimmed to a length of " + MAX_EVENT_ATTRIBUTE_METRIC_KEY_LENGTH + " characters"); } String trimmedValue = StringUtil.clipString(value, MAX_EVENT_ATTRIBUTE_VALUE_LENGTH, false); if (trimmedValue.length() < value.length()) { Log.w(TAG, "The attribute value has been trimmed to a length of " + MAX_EVENT_ATTRIBUTE_VALUE_LENGTH + " characters"); } return new Pair<String, String>(trimmedKey, trimmedValue); } private static Pair<String, Double> processMetric(String key, Double value) { String trimmedKey = StringUtil .clipString(key, MAX_EVENT_ATTRIBUTE_METRIC_KEY_LENGTH, false); if (trimmedKey.length() < key.length()) { Log.w(TAG, "The metric key has been trimmed to a length of " + MAX_EVENT_ATTRIBUTE_METRIC_KEY_LENGTH + " characters"); } return new Pair<String, Double>(trimmedKey, value); } @Override public JSONObject toJSONObject() { return decoratedEvent.toJSONObject(); } @Override public Long getEventTimestamp() { return decoratedEvent.getEventTimestamp(); } @Override public Id getUniqueId() { return decoratedEvent.getUniqueId(); } @Override public String getSessionId() { return decoratedEvent.getSessionId(); } @Override public long getSessionStart() { return decoratedEvent.getSessionStart(); } @Override public Long getSessionStop() { return decoratedEvent.getSessionStop(); } @Override public Long getSessionDuration() { return decoratedEvent.getSessionDuration(); } @Override public String getSdkName() { return decoratedEvent.getSdkName(); } @Override public String getSdkVersion() { return decoratedEvent.getSdkVersion(); } @Override public ClientContext createClientContext(String networkType) { return decoratedEvent.createClientContext(networkType); } }