<commit_before>/*
 * Air Native Extension for Google Analytics on iOS and Android
 * 
 * Author: <NAME>
 * http://alessandrobianco.eu
 *
 * Copyright (c) 2012 <NAME>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
package eu.alebianco.air.extensions.analytics
{
	import eu.alebianco.air.extensions.analytics.enum.StatusLevel;
	import eu.alebianco.air.extensions.analytics.enum.VariableScope;
	import eu.alebianco.air.extensions.analytics.enum.VariableSlot;
	import eu.alebianco.air.extensions.analytics.ns.logStatusLevel;
	import eu.alebianco.core.IDisposable;
	
	import flash.events.EventDispatcher;
	import flash.events.StatusEvent;
	import flash.external.ExtensionContext;
	import flash.utils.getQualifiedClassName;
	
	import mx.logging.ILogger;
	import mx.logging.Log;
	
	public final class GATracker implements IDisposable
	{
		private static const VERSION:String = "1.1";
		
		private static const EXTENSION_ID:String = "eu.alebianco.air.extensions.analytics.NativeGATracker";
		
		private static var instance:GATracker;
		private static var canBuild:Boolean;
		
		private var logger:ILogger;
		
		private var context:ExtensionContext;
		
		public static function getInstance():GATracker
		{
			if (!instance)
			{
				canBuild = true;
				instance = new GATracker();
				canBuild = false;
			}
			return instance;
		}
		
		public static function isSupported():Boolean
		{
			var supported:Boolean;
			
			try
			{
				getInstance();
				supported = instance.context.call("isSupported") == true;
			}
			catch(error:Error)
			{
				supported = false;
			}
			
			return supported;
		}
		
		public function GATracker()
		{
			if (!canBuild)
			{
				throw new Error("Can't instantiate a Singleton class, use getInstance() to get a reference.");
			}
			
			if (!context) 
			{
				context = ExtensionContext.createExtensionContext(EXTENSION_ID, null);
				context.addEventListener(StatusEvent.STATUS, statusHandler);
			}
			
			var className:String = getQualifiedClassName(this).replace("::", ".");
			logger = Log.getLogger(className);
		}
		
		public function dispose():void {
			
			dispatch();
			stopSession();
			context.removeEventListener(StatusEvent.STATUS, statusHandler);
			context = null;
			instance = null;
		}
		
		private function statusHandler(event:StatusEvent):void {
			
			var level:StatusLevel = StatusLevel.parseConstant(event.level);
			if (level)
			{
				var ns:Namespace = level.ns;
				ns::processStatusEvent(level.name, event.code);
			}
		}
		
		logStatusLevel function processStatusEvent(level:String, code:String):void
		{
			switch(level.toUpperCase())
			{
				case "INFO":
					logger.info(code);
					break;
				case "DEBUG":
					logger.debug(code);
					break;
				case "WARN":
					logger.warn(code);
					break;
				case "ERROR":
					logger.error(code);
					break;
				case "FATAL":
					logger.fatal(code);
					break;
				default:
					logger.debug("Level ("+level+") not found.");
					break;
			}
		}
		
		private function handleResult(result:Object, expected:Class = null):*
		{
			if (result is Error)
			{
				throw result as Error;
			}

			if (result && expected && !(result is expected))
			{
				throw new ArgumentError("Method call returned an unexpected value type.");
			}

			return expected ? expected(result) : result;
		}
		
		public function get version():String {
			
			var nativeVersion:String;
			
			try 
			{
				nativeVersion = handleResult(context.call("getVersion"), String) as String;
			}
			catch(error:Error)
			{
				nativeVersion = "<unknown>";
			}
			
			return "GATracker v" + VERSION + "\n using '" + nativeVersion + "'";
		}
		
		public function startNewSession(accountID:String, interval:int = 20):void 
		{
			handleResult(context.call("startNewSession", accountID, interval));
		}
		
		public function stopSession():void
		{<commit_msg>added validation for provided account ID<commit_after>/*
 * Air Native Extension for Google Analytics on iOS and Android
 * 
 * Author: <NAME>
 * http://alessandrobianco.eu
 *
 * Copyright (c) 2012 <NAME>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
package eu.alebianco.air.extensions.analytics
{
	import eu.alebianco.air.extensions.analytics.enum.StatusLevel;
	import eu.alebianco.air.extensions.analytics.enum.VariableScope;
	import eu.alebianco.air.extensions.analytics.enum.VariableSlot;
	import eu.alebianco.air.extensions.analytics.ns.logStatusLevel;
	import eu.alebianco.core.IDisposable;
	
	import flash.events.EventDispatcher;
	import flash.events.StatusEvent;
	import flash.external.ExtensionContext;
	import flash.utils.getQualifiedClassName;
	
	import mx.logging.ILogger;
	import mx.logging.Log;
	
	public final class GATracker implements IDisposable
	{
		private static const VERSION:String = "1.1";

        private static const GA_ID_VALIDATOR:RegExp = /^UA-\d{4,10}-\d{1,4}$/i
		private static const EXTENSION_ID:String = "eu.alebianco.air.extensions.analytics.NativeGATracker";
		
		private static var instance:GATracker;
		private static var canBuild:Boolean;
		
		private var logger:ILogger;
		
		private var context:ExtensionContext;
		
		public static function getInstance():GATracker
		{
			if (!instance)
			{
				canBuild = true;
				instance = new GATracker();
				canBuild = false;
			}
			return instance;
		}
		
		public static function isSupported():Boolean
		{
			var supported:Boolean;
			
			try
			{
				getInstance();
				supported = instance.context.call("isSupported") == true;
			}
			catch(error:Error)
			{
				supported = false;
			}
			
			return supported;
		}
		
		public function GATracker()
		{
			if (!canBuild)
			{
				throw new Error("Can't instantiate a Singleton class, use getInstance() to get a reference.");
			}
			
			if (!context) 
			{
				context = ExtensionContext.createExtensionContext(EXTENSION_ID, null);
				context.addEventListener(StatusEvent.STATUS, statusHandler);
			}
			
			var className:String = getQualifiedClassName(this).replace("::", ".");
			logger = Log.getLogger(className);
		}
		
		public function dispose():void {
			
			dispatch();
			stopSession();
			context.removeEventListener(StatusEvent.STATUS, statusHandler);
			context = null;
			instance = null;
		}
		
		private function statusHandler(event:StatusEvent):void {
			
			var level:StatusLevel = StatusLevel.parseConstant(event.level);
			if (level)
			{
				var ns:Namespace = level.ns;
				ns::processStatusEvent(level.name, event.code);
			}
		}
		
		logStatusLevel function processStatusEvent(level:String, code:String):void
		{
			switch(level.toUpperCase())
			{
				case "INFO":
					logger.info(code);
					break;
				case "DEBUG":
					logger.debug(code);
					break;
				case "WARN":
					logger.warn(code);
					break;
				case "ERROR":
					logger.error(code);
					break;
				case "FATAL":
					logger.fatal(code);
					break;
				default:
					logger.debug("Level ("+level+") not found.");
					break;
			}
		}
		
		private function handleResult(result:Object, expected:Class = null):*
		{
			if (result is Error)
			{
				throw result as Error;
			}

			if (result && expected && !(result is expected))
			{
				throw new ArgumentError("Method call returned an unexpected value type.");
			}

			return expected ? expected(result) : result;
		}
		
		public function get version():String {
			
			var nativeVersion:String;
			
			try 
			{
				nativeVersion = handleResult(context.call("getVersion"), String) as String;
			}
			catch(error:Error)
			{
				nativeVersion = "<unknown>";
			}
			
			return "GATracker v" + VERSION + "\n using '" + nativeVersion + "'";
		}
		
		public function startNewSession(accountID:String, interval:int = 20):void 
		{
            if (GA_ID_VALIDATOR.test(accountID)) {
			    handleResult(context.call("startNewSession", accountID.toUpperCase(), interval));
            } else {
                throw new ArgumentError("The provided account ID is invalid, it should follow the format UA-00000-0.")
            }
		}
		
		public function stopSession():void
		{
{'fraction_non_alphanumeric': 0.10016927212984168, 'fraction_numerical': 0.002688439709250224, 'mean_word_length': 3.440318302387268, 'pattern_counts': {'":': 12, '<': 9, '<?xml version=': 0, '>': 9, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6443828', 'n_tokens_mistral': 3533, 'n_tokens_neox': 2831, 'n_words': 857}