--- /dev/null
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 15
+VisualStudioVersion = 15.0.27703.2042
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Calculator", "Calculator\Calculator.csproj", "{BEFE0E16-9184-4AEE-9793-AF374BFDA5E7}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {BEFE0E16-9184-4AEE-9793-AF374BFDA5E7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {BEFE0E16-9184-4AEE-9793-AF374BFDA5E7}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {BEFE0E16-9184-4AEE-9793-AF374BFDA5E7}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {BEFE0E16-9184-4AEE-9793-AF374BFDA5E7}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {332903D4-E06B-475C-ACDB-C5F7AF481E20}
+ EndGlobalSection
+EndGlobal
--- /dev/null
+
+//Copyright 2018 Samsung Electronics Co., Ltd
+//
+//Licensed under the Apache License, Version 2.0 (the "License");
+//you may not use this file except in compliance with the License.
+//You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+//Unless required by applicable law or agreed to in writing, software
+//distributed under the License is distributed on an "AS IS" BASIS,
+//WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+//See the License for the specific language governing permissions and
+//limitations under the License.
+using System;
+using System.Collections.Generic;
+using Tizen.Wearable.CircularUI.Forms;
+using Tizen.Wearable.CircularUI.Forms.Renderer;
+using Xamarin.Forms;
+using Xamarin.Forms.Platform.Tizen;
+
+namespace Calculator
+{
+ /// <summary>
+ /// Calculator application entry point.
+ /// </summary>
+ class Program : global::Xamarin.Forms.Platform.Tizen.ApplicationLifecycle
+ {
+ /// <summary>
+ /// An AppResourcePath which is used to get full path of the app resources.
+ /// </summary>
+ public static string AppResourcePath
+ {
+ get;
+ private set;
+ }
+
+ /// <summary>
+ /// This function is called when the application is created
+ /// </summary>
+ protected override void OnCreate()
+ {
+ base.OnCreate();
+
+ AppResourcePath = FormsApplication.DirectoryInfo.Resource;
+ FormsApplication.LoadApplication(new CalculatorApp());
+ }
+
+ /// <summary>
+ /// The entry point of the application
+ /// </summary>
+ /// <param name="args">Arguments</param>
+ static void Main(string[] args)
+ {
+ var app = new Program();
+ // define your custom handlers
+ var customRenderers = new Dictionary<Type, Func<IRegisterable>>()
+ {
+ { typeof(CirclePage), ()=> new CirclePageRenderer() },
+ { typeof(global:: Tizen.Wearable.CircularUI.Forms.CircleListView), () => new CircleListViewRenderer() },
+ { typeof(Calculator.Controls.ImageButton), () => new Calculator.Renderers.ImageButtonRenderer() }
+ };
+
+ var option = new InitializationOptions(app);
+ option.UseStaticRegistrar(StaticRegistrarStrategy.StaticRegistrarOnly, customRenderers, true);
+ option.UseMessagingCenter = false;
+ option.UseStyle = false;
+ option.UseVisual = false;
+ option.UseShell = false;
+ Forms.Init(option);
+
+ global::Tizen.Wearable.CircularUI.Forms.Renderer.FormsCircularUI.Init();
+ app.FormsApplication.Run(args);
+ }
+ }
+}
--- /dev/null
+<Project Sdk="Tizen.NET.Sdk/1.0.8">
+
+ <PropertyGroup>
+ <OutputType>Exe</OutputType>
+ <TargetFramework>tizen40</TargetFramework>
+ </PropertyGroup>
+
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+ <DebugType>portable</DebugType>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+ <DebugType>None</DebugType>
+ </PropertyGroup>
+
+ <ItemGroup>
+ <ProjectReference Include="..\..\..\Tizen.CircularUI\Tizen.Wearable.CircularUI.Forms.Renderer\XSF.CircularUI.Forms.Renderer.csproj" />
+ <ProjectReference Include="..\..\..\Tizen.CircularUI\Tizen.Wearable.CircularUI.Forms\XSF.CircularUI.Forms.csproj" />
+ <ProjectReference Include="..\..\..\Xamarin.Forms\Xamarin.Forms.Core\XSF.Core.csproj" />
+ <ProjectReference Include="..\..\..\Xamarin.Forms\Xamarin.Forms.Platform.Tizen\XSF.Platform.Tizen.csproj" />
+ <ProjectReference Include="..\..\..\Xamarin.Forms\Xamarin.Forms.Platform\XSF.Platform.csproj" />
+ <ProjectReference Include="..\..\..\Xamarin.Forms\Xamarin.Forms.Xaml\XSF.Xaml.csproj" />
+ </ItemGroup>
+
+ <ItemGroup>
+ <Compile Update="Views\CalculatorMainPage.xaml.cs">
+ <DependentUpon>CalculatorMainPage.xaml</DependentUpon>
+ </Compile>
+ </ItemGroup>
+
+ <ItemGroup>
+ <EmbeddedResource Update="Controls\ImageButton.xaml">
+ <Generator>MSBuild:UpdateDesignTimeXaml</Generator>
+ </EmbeddedResource>
+ <EmbeddedResource Update="Views\CalculatorMainPage.xaml">
+ <Generator>MSBuild:UpdateDesignTimeXaml</Generator>
+ </EmbeddedResource>
+ </ItemGroup>
+
+ <ItemGroup>
+ <Folder Include="Impl\" />
+ </ItemGroup>
+
+</Project>
--- /dev/null
+
+//Copyright 2018 Samsung Electronics Co., Ltd
+//
+//Licensed under the Apache License, Version 2.0 (the "License");
+//you may not use this file except in compliance with the License.
+//You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+//Unless required by applicable law or agreed to in writing, software
+//distributed under the License is distributed on an "AS IS" BASIS,
+//WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+//See the License for the specific language governing permissions and
+//limitations under the License.
+
+using System;
+
+using Xamarin.Forms;
+using Calculator.Views;
+using Calculator.Impl;
+
+namespace Calculator
+{
+ public class CalculatorApp : Application
+ {
+ /// <summary>
+ /// InputParser class instance.
+ /// The InputParser checks user input and validates calculated expression.
+ /// </summary>
+ /// <seealso cref="global::Calculator.Impl.InputParser">
+ private static readonly Lazy<InputParser> inputParser = new Lazy<InputParser>(() => new InputParser());
+
+ static public InputParser InputParserInstance
+ {
+ get
+ {
+ return inputParser.Value;
+ }
+ }
+
+ /// <summary>
+ /// A Formatter class instance.
+ /// The Formatter class provides formatting for displaying text from validated expression.
+ /// </summary>
+ /// <seealso cref="global::Calculator.Impl.Formatter">
+ private static readonly Lazy<Formatter> formatter = new Lazy<Formatter>(() => new Formatter());
+ static public Formatter FormatterInstance
+ {
+ get
+ {
+ return formatter.Value;
+ }
+ }
+
+ /// <summary>
+ /// A CalculatorImpl class instance.
+ /// The CalculatorImpl class calculates validated expression and provides result value.
+ /// </summary>
+ /// <seealso cref="global::Calculator.Impl.CalculatorImpl">
+ private static readonly Lazy<CalculatorImpl> calculator = new Lazy<CalculatorImpl>(() => new CalculatorImpl());
+ static public CalculatorImpl CalculatorInstance
+ {
+ get
+ {
+ return calculator.Value;
+ }
+ }
+
+ public CalculatorApp()
+ {
+ // The root page of the application
+ MainPage = new CalculatorMainPage();
+ }
+ }
+}
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<Image xmlns="http://xamarin.com/schemas/2014/forms"
+ xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
+ x:Class="Calculator.Controls.ImageButton">
+</Image>
\ No newline at end of file
--- /dev/null
+
+//Copyright 2018 Samsung Electronics Co., Ltd
+//
+//Licensed under the Apache License, Version 2.0 (the "License");
+//you may not use this file except in compliance with the License.
+//You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+//Unless required by applicable law or agreed to in writing, software
+//distributed under the License is distributed on an "AS IS" BASIS,
+//WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+//See the License for the specific language governing permissions and
+//limitations under the License.
+
+using System;
+
+using System.Windows.Input;
+using Xamarin.Forms;
+
+namespace Calculator.Controls
+{
+ /// <summary>
+ /// A command button which is implemented with a custom renderer based on a native image.
+ /// </summary>
+ public partial class ImageButton : Image
+ {
+ public static readonly BindableProperty CommandProperty =
+ BindableProperty.Create(nameof(Command), typeof(ICommand), typeof(ImageButton), null, BindingMode.TwoWay);
+
+ /// <summary>
+ /// A command that will be executed if the button is touched.
+ /// </summary>
+ public ICommand Command
+ {
+ get { return (ICommand)GetValue(CommandProperty); }
+ set { SetValue(CommandProperty, value); }
+ }
+
+ /// <summary>
+ /// A command parameter that will be passed when the Command is executed.
+ /// </summary>
+ /// <see cref="ImageButton.Command"/>
+ public String CommandParameter
+ {
+ get;
+ set;
+ }
+
+ public ImageButton()
+ {
+ CommandParameter = "";
+ InitializeComponent();
+ }
+ }
+}
--- /dev/null
+
+//Copyright 2018 Samsung Electronics Co., Ltd
+//
+//Licensed under the Apache License, Version 2.0 (the "License");
+//you may not use this file except in compliance with the License.
+//You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+//Unless required by applicable law or agreed to in writing, software
+//distributed under the License is distributed on an "AS IS" BASIS,
+//WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+//See the License for the specific language governing permissions and
+//limitations under the License.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+using Calculator.Models;
+using Tizen;
+
+namespace Calculator.Impl
+{
+ /// <summary>
+ /// A Calculator Implementation class that calculates result for given expression.
+ /// </summary>
+ /// <remarks>
+ /// The CalculatorImpl requires InputElement class based infix notation expression list.
+ /// </remarks>
+ public sealed class CalculatorImpl
+ {
+ /// <summary>
+ /// An enum value representing Calculator working status.
+ /// </summary>
+ public enum WorkingStatus
+ {
+ WORKING,
+ ERROR,
+ }
+
+ /// <summary>
+ /// Calculator working status.
+ /// Status is set to ERROR if last calculation failed.
+ /// </summary>
+ public WorkingStatus Status
+ {
+ get;
+ private set;
+ }
+
+ /// <summary>
+ /// All operators that can be used in calculation.
+ /// </summary>
+ private IReadOnlyDictionary<String, IOperator> OperatorDescriptions;
+
+ /// <summary>
+ /// Last used operand.
+ /// This operand is used in repeated calculation.
+ /// </summary>
+ private Literal LastOperand;
+
+ /// <summary>
+ /// Last used operator.
+ /// This operator is used in repeated calculation.
+ /// </summary>
+ private IOperator LastOperator;
+
+ /// <summary>
+ /// A flag value shows if the last pressed button is equal key.
+ /// </summary>
+ private bool IsEqualUsed;
+
+ /// <summary>
+ /// Last calculated result value.
+ /// </summary>
+ public double Result
+ {
+ get;
+ private set;
+ }
+
+ /// <summary>
+ /// The CalculatorImpl's constructor which initializes calculator
+ /// and sets default calculation operators.
+ /// </summary>
+ public CalculatorImpl()
+ {
+ Status = WorkingStatus.WORKING;
+ OperatorDescriptions = Operators.OperatorMap;
+ }
+
+ /// <summary>
+ /// The method which calculates given expression.
+ /// </summary>
+ /// <param name="separatedInfixNotation"> An expression list in infix notation </param>
+ /// <returns> Calculation result status </returns>
+ public CalculationResult SetExpression(IEnumerable<InputElement> separatedInfixNotation)
+ {
+ IsEqualUsed = false;
+ return Calculate(separatedInfixNotation);
+ }
+
+ /// <summary>
+ /// This method is handling equal key press.
+ /// If the equal key is pressed once, just provides a result,
+ /// but if the equal key is pressed repeatedly, calculator calculates with lastly used operand and operator
+ /// </summary>
+ /// <param name="separated"> Expression list in infix notation </param>
+ /// <returns> Calculation result status </returns>
+ public CalculationResult Equal(IEnumerable<InputElement> separated)
+ {
+ CalculationResult res;
+
+ if (IsEqualUsed == false)
+ {
+ res = Calculate(separated);
+ if (res is CalculationSuccessful)
+ {
+ IsEqualUsed = true;
+ return res;
+ }
+
+ LastOperand = null;
+ LastOperator = null;
+ return res;
+ }
+
+ if (separated.Count() < 1 ||
+ LastOperator == null ||
+ LastOperand == null)
+ {
+ return new CalculationFailed();
+ }
+
+ List<InputElement> tempList = new List<InputElement>(separated);
+ tempList.Add(LastOperator as InputElement);
+ tempList.Add(LastOperand);
+
+ return Calculate(tempList);
+ }
+
+ /// <summary>
+ /// The method calculates result for given expression.
+ /// </summary>
+ /// <param name="separated"> InputElement list in infix notation</param>
+ /// <returns> Calculation result status </returns>
+ private CalculationResult Calculate(IEnumerable<InputElement> separated)
+ {
+ Status = WorkingStatus.ERROR;
+
+ if (!separated.Any())
+ {
+ Result = 0;
+ Status = WorkingStatus.WORKING;
+ return new CalculationSuccessful();
+ }
+
+ if (separated.Last() is Point)
+ {
+ return new CalculationFailed();
+ }
+
+ IList<InputElement> temp = new List<InputElement>(separated);
+ // This section adds missing closing brackets
+ {
+ int depth = 0;
+ foreach (var item in temp)
+ {
+ if (item.CompareTo("(") == 0)
+ {
+ depth += 1;
+ }
+ else if (item.CompareTo(")") == 0)
+ {
+ depth -= 1;
+ }
+ }
+
+ for (int i = 0; i < depth; i++)
+ {
+ temp.Add(new CloseBracket());
+ }
+ }
+
+
+ Queue<InputElement> postfix;
+ if (!ChangeToPostfix(temp, out postfix))
+ {
+ return new CantCalculateInvalidFormat();
+ }
+
+ Stack<string> operand = new Stack<string>();
+ try
+ {
+ foreach(var token in postfix)
+ {
+ if (token is Literal)
+ {
+ operand.Push(token.GetElement);
+ }
+ else
+ {
+ IOperator op = OperatorDescriptions[token];
+ string op1;
+ string op2;
+ double _op1 = 0;
+ double _op2 = 0;
+
+ // This section retrieves arguments needed by operator
+ if (op is IBinaryOperator)
+ {
+ op2 = operand.Pop();
+ op1 = operand.Pop();
+
+ if (!GetDoubleValue(op2, out _op2) || !GetDoubleValue(op1, out _op1))
+ {
+ return new CantCalculateInvalidFormat();
+ }
+
+ if ((op is Models.Point) == false)
+ {
+ LastOperand = new Literal(op2);
+ LastOperator = op;
+ }
+ }
+ else if (op is IUnaryOperator)
+ {
+ op1 = operand.Pop();
+
+ if (!GetDoubleValue(op1, out _op1))
+ {
+ return new CantCalculateInvalidFormat();
+ }
+ }
+
+ op.GetResult(_op1, _op2, out double result);
+
+ if (Double.IsInfinity(result) || Double.IsNaN(result))
+ {
+ return new CantCalculateTooBigNumber();
+ }
+
+ operand.Push(result.ToString());
+ }
+ }
+ }
+ catch (DivideByZeroException)
+ {
+ return new CantDivideByZero();
+ }
+ catch (InvalidOperationException)
+ {
+ return new CantCalculateInvalidFormat();
+ }
+ catch (Exception e)
+ {
+ System.Diagnostics.Debug.WriteLine(e.Message);
+ return new CalculationFailed();
+ }
+
+ double ret;
+ if (operand.Count == 0 || Double.TryParse(operand.Peek(), out ret) == false)
+ {
+ return new CalculationFailed();
+ }
+
+ Result = ret;
+ Status = WorkingStatus.WORKING;
+ return new CalculationSuccessful();
+ }
+
+ /// <summary>
+ /// The method changes expression format from infix to postfix.
+ /// </summary>
+ /// <param name="separated"> InputElement list in infix notation </param>
+ /// <param name="ret"> InputElement queue in postfix notation </param>
+ /// <returns> Result status of expression's reorder </returns>
+ private bool ChangeToPostfix(IEnumerable<InputElement> separated, out Queue<InputElement> ret)
+ {
+ Queue<InputElement> postfix = new Queue<InputElement>();
+ Stack<InputElement> operators = new Stack<InputElement>();
+
+ try
+ {
+ foreach (InputElement token in separated)
+ {
+ if (token is Literal)
+ {
+ postfix.Enqueue(token);
+ }
+ else if (token.CompareTo("(") == 0)
+ {
+ operators.Push(token);
+ }
+ else if (token.CompareTo(")") == 0)
+ {
+ InputElement topToken = operators.Pop();
+ while (topToken.CompareTo("(") != 0)
+ {
+ postfix.Enqueue(topToken);
+ topToken = operators.Pop();
+ }
+ }
+ else
+ {
+ while ((operators.Count > 0) &&
+ (OperatorDescriptions[operators.Peek()].Priority >= OperatorDescriptions[token].Priority))
+ {
+ postfix.Enqueue(operators.Pop());
+ }
+
+ operators.Push(token);
+ }
+ }
+
+ while (operators.Count > 0)
+ {
+ postfix.Enqueue(operators.Pop());
+ }
+ }
+ catch (Exception)
+ {
+ postfix.Clear();
+ ret = postfix;
+ return false;
+ }
+
+ ret = postfix;
+ return true;
+ }
+
+ private bool GetDoubleValue(string literal, out double value)
+ {
+ string strValue = literal;
+
+ if (literal.StartsWith("0") && literal.Contains(".") == false)
+ {
+ strValue = ("0." + literal);
+ }
+
+ return Double.TryParse(strValue, out value);
+ }
+ }
+}
--- /dev/null
+
+//Copyright 2018 Samsung Electronics Co., Ltd
+//
+//Licensed under the Apache License, Version 2.0 (the "License");
+//you may not use this file except in compliance with the License.
+//You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+//Unless required by applicable law or agreed to in writing, software
+//distributed under the License is distributed on an "AS IS" BASIS,
+//WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+//See the License for the specific language governing permissions and
+//limitations under the License.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Xamarin.Forms;
+
+using Calculator.Models;
+
+namespace Calculator.Impl
+{
+ /// <summary>
+ /// A Formatter class that provides formatted displayable text.</summary>
+ public sealed class Formatter
+ {
+ private const double displayMax = 9999999999D;
+ private static readonly Color literalColor = Color.White;
+ private static readonly Color operatorColor = Color.Pink;
+
+ /// <summary>
+ /// The method provides formatted text for a number.
+ /// </summary>
+ /// <param name="value"> A number value </param>
+ /// <returns> A formatted number text </returns>
+ private String GetNumberText(Double value)
+ {
+ if (Math.Abs(value) > displayMax)
+ {
+ return String.Format("{0:0.########E+000}", value);
+ }
+
+ return String.Format("{0:#,##0.##########}", value);
+ }
+
+ /// <summary>
+ /// The method provides formatted expression text for given expression.
+ /// </summary>
+ /// <param name="separatedExpression"> An expression consist of InputElement list </param>
+ /// <returns> A formatted expression text </returns>
+ public FormattedString GetFormattedExpressionText(IEnumerable<InputElement> separatedExpression)
+ {
+ var result = new FormattedString();
+ int oneLineLengthCount = 0;
+
+ for (int i = 0; i < separatedExpression.Count(); i++)
+ {
+ InputElement s = separatedExpression.ElementAt(i);
+ string element = s.GetDisplayElement;
+
+ if (s is Literal)
+ {
+ double value;
+ string displayNumberText = element;
+ if (Double.TryParse(element, out value))
+ {
+ displayNumberText = GetNumberText(value);
+ }
+
+ result.Spans.Add(new Span
+ {
+ Text = displayNumberText,
+ ForegroundColor = literalColor,
+ });
+ }
+ else if (s is Reverse)
+ {
+ result.Spans.Add(new Span
+ {
+ Text = element,
+ ForegroundColor = literalColor,
+ });
+ }
+ else if (s is Models.Point && i < (separatedExpression.Count() - 1))
+ {
+ if (separatedExpression.ElementAt(i + 1) is Literal)
+ {
+ string last = result.Spans.Last().Text;
+ string stringValue = last;
+ string next = separatedExpression.ElementAt(i + 1);
+ stringValue = last + "." + next;
+ double value;
+
+ if (Double.TryParse(stringValue, out value))
+ {
+ result.Spans.RemoveAt(result.Spans.Count - 1);
+ result.Spans.Add(new Span
+ {
+ Text = stringValue,
+ ForegroundColor = literalColor,
+ });
+ i += 1;
+ }
+ }
+ }
+ else
+ {
+ result.Spans.Add(new Span
+ {
+ Text = element,
+ ForegroundColor = operatorColor,
+ });
+
+ }
+
+
+ oneLineLengthCount += result.Spans.Last().Text.Length;
+ }
+
+ return result;
+ }
+
+ /// <summary>
+ /// The method provides formatted text for the calculated result. </summary>
+ /// <param name="outputText"> A result text </param>
+ /// <returns> A formatted result text </returns>
+ public FormattedString GetFormattedOutputText(String outputText)
+ {
+ string displayNumber;
+
+ double value;
+ if (Double.TryParse(outputText, out value) == false)
+ {
+ return new FormattedString();
+ }
+
+ // Exceptional Case : 0.00000 => 0.00000 (0), 0.0 (X)
+ if (value == 0 &&
+ outputText.Length > 1)
+ {
+ displayNumber = outputText;
+ }
+
+ else
+ {
+ displayNumber = "=" + GetNumberText(value);
+ }
+
+ return new FormattedString()
+ {
+ Spans =
+ {
+ new Span
+ {
+ Text = displayNumber,
+ },
+ }
+ };
+ }
+ }
+}
--- /dev/null
+
+//Copyright 2018 Samsung Electronics Co., Ltd
+//
+//Licensed under the Apache License, Version 2.0 (the "License");
+//you may not use this file except in compliance with the License.
+//You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+//Unless required by applicable law or agreed to in writing, software
+//distributed under the License is distributed on an "AS IS" BASIS,
+//WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+//See the License for the specific language governing permissions and
+//limitations under the License.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+using Calculator.Models;
+
+namespace Calculator.Impl
+{
+ /// <summary>
+ /// InputParser class which validates inputted elements(Operator, Operand)
+ /// and provides separated elements list for the Formatter and the CalculatorImpl.
+ /// This class keeps the inputted expression further use for validate purpose.
+ /// </summary>
+ public sealed class InputParser
+ {
+ private List<InputElement> expressionElements = new List<InputElement>();
+
+ /// <summary>
+ /// InputElement list contains lastly validated expression. </summary>
+ public IEnumerable<InputElement> ExpressionElements
+ {
+ get
+ {
+ return expressionElements;
+ }
+ }
+
+ /// <summary>
+ /// A flag represents whether the input elements is empty or not. </summary>
+ public bool IsEmptyInputElements
+ {
+ get
+ {
+ return (expressionElements.Count == 0);
+ }
+ }
+
+ /// <summary>
+ /// A flag represents whether the last inputted element is the Literal or not. </summary>
+ private bool IsLastElementLiteral
+ {
+ get
+ {
+ if (expressionElements.Count > 0 &&
+ expressionElements.Last() is Literal)
+ {
+ return true;
+ }
+
+ return false;
+ }
+ }
+
+ /// <summary>
+ /// A flag represents whether the last inputted element is the Operator or not. </summary>
+ private bool IsLastElementOperator
+ {
+ get
+ {
+ if (expressionElements.Count > 0 &&
+ expressionElements.Last() is IOperator)
+ {
+ return true;
+ }
+
+ return false;
+ }
+ }
+
+ /// <summary>
+ /// A flag indicates last validation status. </summary>
+ private bool HasLastValidationSucceeded;
+ /// <summary>
+ /// A flag represents whether equal key is pressed or not last time. </summary>
+ private bool IsEqualUsed;
+
+ public InputParser()
+ {
+
+ }
+
+ /// <summary>
+ /// The method adds inputted element to existing expression and validates it. </summary>
+ /// <param name="input"> An inputted element </param>
+ /// <param name="separated"> An infix notation expression InputElement list </param>
+ /// <returns> An element adding result status </returns>
+ public AddingElementResult GetSeparatedPlainText(string input, out IEnumerable<InputElement> separated)
+ {
+ double inputNumber;
+ separated = expressionElements;
+
+ if (input.Length < 1)
+ {
+ HasLastValidationSucceeded = false;
+ return new InvalidFormatUsed();
+ }
+
+ if (Double.TryParse(input, out inputNumber))
+ {
+ Literal inputLiteral = new Literal(input);
+
+ if (IsEmptyInputElements)
+ {
+ expressionElements.Add(inputLiteral);
+ IsEqualUsed = false;
+ HasLastValidationSucceeded = true;
+ return new AddingPossible();
+ }
+ else
+ {
+ AddingElementResult res = expressionElements.Last().CheckPossibilityAddingElement(ref expressionElements,
+ ref IsEqualUsed, ref HasLastValidationSucceeded, inputLiteral);
+ IsEqualUsed = false;
+ if (res is AddingPossible)
+ {
+ HasLastValidationSucceeded = true;
+ }
+ else
+ {
+ HasLastValidationSucceeded = false;
+ }
+
+ return res;
+ }
+ }
+
+ InputElement inputElem = Operators.GetOperatorAsInputElement(input);
+ IOperator inputOper = inputElem as IOperator;
+ if (inputOper == null)
+ {
+ HasLastValidationSucceeded = false;
+ return new InvalidFormatUsed();
+ }
+
+ if (IsEmptyInputElements)
+ {
+ if (OperandType.LEFT == inputOper.OperandType ||
+ OperandType.BOTH == inputOper.OperandType)
+ {
+ // Exceptional Case : '.' => '0.'
+ if (inputOper is Point)
+ {
+ expressionElements.Add(new Literal("0"));
+ }
+
+ else
+ {
+ return new InvalidFormatUsed();
+ }
+ }
+
+ expressionElements.Add(inputElem);
+ Operators.GetOperatorAsInputElement(input)?.PostAddingWork(ref expressionElements);
+ IsEqualUsed = false;
+
+ HasLastValidationSucceeded = true;
+ return new AddingPossible();
+ }
+
+ if (inputElem.AlternativeWork(ref expressionElements, ref IsEqualUsed, ref HasLastValidationSucceeded))
+ {
+ IsEqualUsed = false;
+ HasLastValidationSucceeded = true;
+ return new AddingPossible();
+ }
+
+ // Exceptional case : Only one '.'
+ if (input.CompareTo(".") == 0)
+ {
+ if (CheckDotExist())
+ {
+ separated = expressionElements;
+ HasLastValidationSucceeded = true;
+ return new AddingImpossible();
+ }
+ }
+ else if (input.CompareTo("R") == 0)
+ {
+ if (ReverseSign(out separated))
+ {
+ return new AddingPossible();
+ }
+
+ return new InvalidFormatUsed();
+ }
+
+ expressionElements.Last().CheckPossibilityAddingElement(ref expressionElements, ref IsEqualUsed, ref HasLastValidationSucceeded, inputOper as InputElement);
+ IsEqualUsed = false;
+
+ HasLastValidationSucceeded = true;
+ return new AddingPossible();
+ }
+
+ /// <summary>
+ /// The method provides status indicates that there is '.' exists or not </summary>
+ /// <returns> A existence of '.' </returns>
+ private bool CheckDotExist()
+ {
+ for (int i = expressionElements.Count - 1; i >= 0; i--)
+ {
+ if (expressionElements[i].CompareTo(".") == 0)
+ {
+ return true;
+ }
+
+ if (expressionElements[i] is IOperator)
+ {
+ break;
+ }
+
+ }
+
+ return false;
+ }
+
+ /// <summary>
+ /// The method resets the InputParser to the initial status. </summary>
+ public void Clear()
+ {
+ expressionElements.Clear();
+ HasLastValidationSucceeded = false;
+ IsEqualUsed = false;
+ }
+
+ /// <summary>
+ /// The method removes last inputted element from current expression. </summary>
+ /// <param name="separated"> Current expression InputElement list </param>
+ /// <returns> Flag informing whether last element has been successfully removed. </returns>
+ public bool DeleteLast(out IEnumerable<InputElement> separated)
+ {
+ separated = new List<InputElement>();
+
+ if (expressionElements.Count == 0)
+ {
+ return false;
+ }
+
+ if (expressionElements.Last() is Literal &&
+ expressionElements.Last().GetElement.Length > 1)
+ {
+ Literal literal = expressionElements.Last() as Literal;
+
+ expressionElements[expressionElements.Count - 1] =
+ new Literal(literal.GetElement.Substring(0, literal.GetElement.Length - 1));
+ }
+ else
+ {
+ expressionElements.RemoveAt(expressionElements.Count - 1);
+ }
+
+ separated = expressionElements;
+ return true;
+ }
+
+ /// <summary>
+ /// The method adds a reverse sign or replace last inputted reverse sign. </summary>
+ /// <param name="separated"> A current expression InputElement list </param>
+ /// <returns> An element adding reverse sign </returns>
+ public bool ReverseSign(out IEnumerable<InputElement> separated)
+ {
+ double lastNumber;
+
+ separated = expressionElements;
+ if (IsEmptyInputElements)
+ {
+ expressionElements.Add(new OpenBracket());
+ expressionElements.Add(new Reverse());
+ return true;
+ }
+
+ if (expressionElements.Last() is Point)
+ {
+ expressionElements.RemoveAt(expressionElements.Count - 1);
+ }
+
+ if (IsEmptyInputElements)
+ {
+ expressionElements.Add(new OpenBracket());
+ expressionElements.Add(new Reverse());
+ return true;
+ }
+
+ // (3 + R => ((-
+ // ((-3 + R => (3
+ // 1+-3 + R => 1+3
+ // 1.1 + R => (-1.1
+
+ if (Double.TryParse(expressionElements.Last(), out lastNumber))
+ {
+ if (lastNumber < 0)
+ {
+ if (expressionElements.Count >= 2 &&
+ expressionElements[expressionElements.Count - 2] is OpenBracket)
+ {
+ expressionElements.RemoveAt(expressionElements.Count - 1);
+ expressionElements.RemoveAt(expressionElements.Count - 1);
+ expressionElements.Add(new Literal(lastNumber * -1));
+ }
+ else
+ {
+ expressionElements.RemoveAt(expressionElements.Count - 1);
+ expressionElements.Add(new Literal(lastNumber * -1));
+ }
+ }
+ else
+ {
+ if (expressionElements.Count > 2)
+ {
+ if (expressionElements[expressionElements.Count - 2] is IOperator)
+ {
+ if (expressionElements[expressionElements.Count - 2] is Point)
+ {
+ Double postPoint;
+ if (Double.TryParse(expressionElements[expressionElements.Count - 3], out postPoint) == false)
+ {
+ return false;
+ }
+
+ expressionElements.RemoveAt(expressionElements.Count - 1);
+ expressionElements.RemoveAt(expressionElements.Count - 1);
+ expressionElements.RemoveAt(expressionElements.Count - 1);
+
+ while (expressionElements.Count > 0 &&
+ expressionElements.Last() is IOperator)
+ {
+ if (expressionElements.Last() is OpenBracket)
+ {
+ if (postPoint < 0)
+ {
+ expressionElements.RemoveAt(expressionElements.Count - 1);
+ }
+
+ break;
+ }
+ else if (expressionElements.Last() is Reverse)
+ {
+ expressionElements.RemoveAt(expressionElements.Count - 1);
+ postPoint = postPoint * -1;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ if ((postPoint * -1) < 0)
+ {
+ expressionElements.Add(new OpenBracket());
+ }
+
+ expressionElements.Add(new Literal(postPoint * -1));
+ expressionElements.Add(new Point());
+ expressionElements.Add(new Literal(lastNumber));
+ return true;
+ }
+ else if (expressionElements[expressionElements.Count - 2] is OpenBracket)
+ {
+ expressionElements.RemoveAt(expressionElements.Count - 1);
+ expressionElements.Add(new OpenBracket());
+ expressionElements.Add(new Literal(lastNumber * -1));
+ }
+ else
+ {
+ expressionElements.RemoveAt(expressionElements.Count - 1);
+ Operators.GetOperatorAsInputElement(expressionElements.Last())?.CheckPossibilityAddingElement(
+ ref expressionElements, ref IsEqualUsed, ref HasLastValidationSucceeded, new OpenBracket());
+ expressionElements.Add(new Literal(lastNumber * -1));
+ }
+
+ }
+ else
+ {
+ return false;
+ }
+
+ }
+ else
+ {
+ expressionElements.RemoveAt(expressionElements.Count - 1);
+ expressionElements.Add(new OpenBracket());
+ expressionElements.Add(new Literal(lastNumber * -1));
+ }
+ }
+
+ return true;
+ }
+
+ // ( + R => ((-
+ // ((R + R => (
+
+ if (expressionElements.Count > 0 &&
+ expressionElements[expressionElements.Count - 1].CompareTo("R") == 0)
+ {
+ if (expressionElements.Count > 1 &&
+ expressionElements[expressionElements.Count - 2].CompareTo("(") == 0)
+ {
+ expressionElements.RemoveAt(expressionElements.Count - 1);
+ expressionElements.RemoveAt(expressionElements.Count - 1);
+ }
+ else
+ {
+ expressionElements.RemoveAt(expressionElements.Count - 1);
+ }
+ }
+ else
+ {
+ if ((expressionElements.Last().CheckPossibilityAddingElement(ref expressionElements,
+ ref IsEqualUsed,
+ ref HasLastValidationSucceeded,
+ new OpenBracket())
+ is AddingPossible) == false)
+ {
+ return false;
+ }
+
+ expressionElements.Add(new Reverse());
+ }
+
+ return true;
+ }
+
+ /// <summary>
+ /// The method manages equal key operation. </summary>
+ /// <param name="separated"> A current expression InputElement list </param>
+ /// <returns> A status of equal key operation </returns>
+ public bool Equal(out IEnumerable<InputElement> separated)
+ {
+ List<InputElement> tempList = new List<InputElement>(expressionElements);
+ separated = tempList;
+
+ if (IsEqualUsed == false)
+ {
+ IsEqualUsed = true;
+ return true;
+ }
+
+ if (HasLastValidationSucceeded == false)
+ {
+ return false;
+ }
+
+ return true;
+ }
+
+ /// <summary>
+ /// The method sets Calculated result as expression.
+ /// Usually this is used after equal key pressed situation. </summary>
+ /// <param name="result"> A calculated result value </param>
+ public void Set(string result)
+ {
+ expressionElements.Clear();
+ expressionElements.Add(new Literal(result));
+ }
+ }
+}
--- /dev/null
+
+//Copyright 2018 Samsung Electronics Co., Ltd
+//
+//Licensed under the Apache License, Version 2.0 (the "License");
+//you may not use this file except in compliance with the License.
+//You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+//Unless required by applicable law or agreed to in writing, software
+//distributed under the License is distributed on an "AS IS" BASIS,
+//WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+//See the License for the specific language governing permissions and
+//limitations under the License.
+
+using System;
+
+namespace Calculator.Models
+{
+ /// <summary>
+ /// AddingElementResult base class to notify of element submit.
+ /// </summary>
+ public class AddingElementResult
+ {
+ /// <summary>
+ /// Message to be displayed in error pop up.
+ /// </summary>
+ public virtual String Message
+ {
+ get { return string.Empty; }
+ }
+ }
+
+ /// <summary>
+ /// Class used to inform that adding element is possible.
+ /// </summary>
+ public class AddingPossible : AddingElementResult
+ {
+ }
+
+ /// <summary>
+ /// Class used to inform that adding element is impossible.
+ /// </summary>
+ public class AddingImpossible : AddingElementResult
+ {
+ }
+
+ /// <summary>
+ /// Class used to inform that adding element is impossible due to invalid expression format.
+ /// </summary>
+ public class InvalidFormatUsed : AddingElementResult
+ {
+ /// <summary>
+ /// Message to be displayed in error pop up.
+ /// </summary>
+ public override String Message
+ {
+ get { return "Invalid format used."; }
+ }
+ }
+
+ /// <summary>
+ /// Class used to inform that adding element is impossible due to a 15 digits exceeding number.
+ /// </summary>
+ public class CantMoreThan15Digit : AddingElementResult
+ {
+ /// <summary>
+ /// Message to be displayed in error pop up.
+ /// </summary>
+ public override String Message
+ {
+ get { return "Can't enter more than 15 digits."; }
+ }
+ }
+
+ /// <summary>
+ /// Class used to inform that adding element is impossible because number contains more than 10 digits after decimal point.
+ /// </summary>
+ public class CantMoreThan10Decimal : AddingElementResult
+ {
+ /// <summary>
+ /// Message to be displayed in error pop up.
+ /// </summary>
+ public override String Message
+ {
+ get { return "Can't enter more than 10 decimal places."; }
+ }
+ }
+}
--- /dev/null
+
+//Copyright 2018 Samsung Electronics Co., Ltd
+//
+//Licensed under the Apache License, Version 2.0 (the "License");
+//you may not use this file except in compliance with the License.
+//You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+//Unless required by applicable law or agreed to in writing, software
+//distributed under the License is distributed on an "AS IS" BASIS,
+//WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+//See the License for the specific language governing permissions and
+//limitations under the License.
+
+using System;
+
+namespace Calculator.Models
+{
+ /// <summary>
+ /// CalculateResult base class to notify the calculate result. </summary>
+ public class CalculationResult
+ {
+ /// <summary>
+ /// A message will be displayed in error pop up . </summary>
+ public virtual String Message
+ {
+ get { return string.Empty; }
+ }
+ }
+
+ /// <summary>
+ /// A CalculateResult class describes calculating is completed without error. </summary>
+ public class CalculationSuccessful : CalculationResult
+ {
+ }
+
+ /// <summary>
+ /// A CalculateFailed class describes calculating is failed. </summary>
+ public class CalculationFailed : CalculationResult
+ {
+ }
+
+ /// <summary>
+ /// A CalculateResult class describes calculating is failed
+ /// due to invalid expression format. </summary>
+ public class CantCalculateInvalidFormat : CalculationResult
+ {
+ /// <summary>
+ /// A message will be displayed in error pop up . </summary>
+ public override String Message
+ {
+ get { return "Invalid format used."; }
+ }
+ }
+
+ /// <summary>
+ /// A CalculateResult class describes calculating is failed
+ /// due to big number. </summary>
+ public class CantCalculateTooBigNumber : CalculationResult
+ {
+ /// <summary>
+ /// A message will be displayed in error pop up . </summary>
+ public override String Message
+ {
+ get { return "Couldn't display entire result. Result too long."; }
+ }
+ }
+
+ /// <summary>
+ /// A CalculateResult class describes calculating is failed
+ /// due to dividing by zero. </summary>
+ public class CantDivideByZero : CalculationResult
+ {
+ /// <summary>
+ /// A message will be displayed in error pop up . </summary>
+ public override String Message
+ {
+ get { return "Can't divide by zero."; }
+ }
+ }
+}
--- /dev/null
+
+//Copyright 2018 Samsung Electronics Co., Ltd
+//
+//Licensed under the Apache License, Version 2.0 (the "License");
+//you may not use this file except in compliance with the License.
+//You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+//Unless required by applicable law or agreed to in writing, software
+//distributed under the License is distributed on an "AS IS" BASIS,
+//WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+//See the License for the specific language governing permissions and
+//limitations under the License.
+
+namespace Calculator.Models
+{
+ /// <summary>
+ /// Interface for the BinaryOperator.
+ /// </summary>
+ public interface IBinaryOperator : IOperator
+ {
+ }
+}
--- /dev/null
+
+//Copyright 2018 Samsung Electronics Co., Ltd
+//
+//Licensed under the Apache License, Version 2.0 (the "License");
+//you may not use this file except in compliance with the License.
+//You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+//Unless required by applicable law or agreed to in writing, software
+//distributed under the License is distributed on an "AS IS" BASIS,
+//WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+//See the License for the specific language governing permissions and
+//limitations under the License.
+
+namespace Calculator.Models
+{
+ /// <summary>
+ /// An interface for the Nullary.
+ /// </summary>
+ public interface INullary : IOperator
+ {
+ }
+}
--- /dev/null
+
+//Copyright 2018 Samsung Electronics Co., Ltd
+//
+//Licensed under the Apache License, Version 2.0 (the "License");
+//you may not use this file except in compliance with the License.
+//You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+//Unless required by applicable law or agreed to in writing, software
+//distributed under the License is distributed on an "AS IS" BASIS,
+//WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+//See the License for the specific language governing permissions and
+//limitations under the License.
+
+namespace Calculator.Models
+{
+ /// <summary>
+ /// A operand placing type of the operator
+ /// LEFT : Operand is placed on Left side only
+ /// RIGHT : Operand is placed on Right side only
+ /// BOTH : Operand is placed on Both side
+ /// NONE : Operand is placed on Both side
+ /// </summary>
+ public enum OperandType
+ {
+ LEFT, // Operand is placed on Left side only
+ RIGHT, // Operand is placed on Right side only
+ BOTH, // Operand is placed on Both side
+ NONE, // Operand is placed on Both side
+ };
+
+ /// <summary>
+ /// An interface for the Operator</summary>
+ public interface IOperator
+ {
+ /// <summary>
+ /// Operator priority
+ /// </summary>
+ int Priority { get; }
+
+ /// <summary>
+ /// An operator's operand type
+ /// </summary>
+ /// <seealso cref="OperandType">
+ /// Operand type.
+ /// </seealso>
+ OperandType OperandType { get; }
+
+ /// <summary>
+ /// The method provides result of operator
+ /// </summary>
+ /// <param name="left"> Left side element. </param>
+ /// <param name="right"> Light side element. </param>
+ /// <param name="result"> Operator's calculation result. </param>
+ void GetResult(double left, double right, out double result);
+ }
+}
--- /dev/null
+
+//Copyright 2018 Samsung Electronics Co., Ltd
+//
+//Licensed under the Apache License, Version 2.0 (the "License");
+//you may not use this file except in compliance with the License.
+//You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+//Unless required by applicable law or agreed to in writing, software
+//distributed under the License is distributed on an "AS IS" BASIS,
+//WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+//See the License for the specific language governing permissions and
+//limitations under the License.
+
+namespace Calculator.Models
+{
+ /// <summary>
+ /// An interface for the UnaryOperator. </summary>
+ public interface IUnaryOperator : IOperator
+ {
+ }
+}
--- /dev/null
+
+//Copyright 2018 Samsung Electronics Co., Ltd
+//
+//Licensed under the Apache License, Version 2.0 (the "License");
+//you may not use this file except in compliance with the License.
+//You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+//Unless required by applicable law or agreed to in writing, software
+//distributed under the License is distributed on an "AS IS" BASIS,
+//WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+//See the License for the specific language governing permissions and
+//limitations under the License.
+
+using System;
+using System.Collections.Generic;
+
+namespace Calculator.Models
+{
+ /// <summary>
+ /// A base class for the calculating elements(Number, Operators).
+ /// The InputElemet can be a Literal and any other operators that inherit the InputElement and IOperator. </summary>
+ public abstract class InputElement : IComparable<string>
+ {
+ /// <summary>
+ /// A property provides element string.
+ /// This property is used for validation and calculation </summary>
+ public abstract string GetElement { get; }
+
+ /// <summary>
+ /// A property provides displaying element string.
+ /// This property is used for building formatted string. </summary>
+ public abstract string GetDisplayElement { get; }
+
+ /// <summary>
+ /// The method provides whether it is possible to add followingElement after this InputElement(is placed at the end of expressionElements).
+ /// This method can modify current expression by adding new element or removing last element. </summary>
+ /// <param name="expressionElements"> Current expression, an InputElement list </param>
+ /// <param name="isEqualUsed"> A flag value whether equal key is pressed or not </param>
+ /// <param name="isLastValidationSucceed"> A flag value whether last calculation is succeed or not </param>
+ /// <param name="followingElement"> An input element will be added after this element. </param>
+ /// <returns> A status of adding element to exist expression </returns>
+ /// <seealso cref="AlternativeWork(ref List{InputElement}, ref bool, ref bool)"/>
+ public abstract AddingElementResult CheckPossibilityAddingElement(ref List<InputElement> expressionElements,
+ ref bool isEqualUsed,
+ ref bool isLastValidationSucceed,
+ InputElement followingElement);
+
+ /// <summary>
+ /// The method doing some exceptional or an independent work instead of adding this InputElement.
+ /// If this methods return true, calculator do not call the CheckPossibilityAddingElement method.</summary>
+ /// <param name="expressionElements"> Current expression, an InputElement list </param>
+ /// <param name="IsEqualUsed"> A flag value whether equal key is pressed or not </param>
+ /// <param name="IsLastValidationSucceed"> A flag value whether last calculation is succeed or not </param>
+ /// <returns> A status of operator's alternative work </returns>
+ /// <seealso cref="AddingElementResult"/>
+ public virtual bool AlternativeWork(ref List<InputElement> expressionElements,
+ ref bool IsEqualUsed,
+ ref bool IsLastValidationSucceed)
+ {
+ return false;
+ }
+
+ /// <summary>
+ /// The method doing additional work after added as followingElement.</summary>
+ /// <param name="expressionElements"> Current expression, an InputElement list </param>
+ public virtual void PostAddingWork(ref List<InputElement> expressionElements)
+ {
+ }
+
+ public static implicit operator string(InputElement ie)
+ {
+ return ie.GetElement;
+ }
+
+ public override string ToString()
+ {
+ return GetElement;
+ }
+
+ public int CompareTo(string other)
+ {
+ return other.CompareTo(GetElement);
+ }
+ }
+}
--- /dev/null
+
+//Copyright 2018 Samsung Electronics Co., Ltd
+//
+//Licensed under the Apache License, Version 2.0 (the "License");
+//you may not use this file except in compliance with the License.
+//You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+//Unless required by applicable law or agreed to in writing, software
+//distributed under the License is distributed on an "AS IS" BASIS,
+//WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+//See the License for the specific language governing permissions and
+//limitations under the License.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace Calculator.Models
+{
+ /// <summary>
+ /// This class represents numbers.
+ /// </summary>
+ public class Literal : InputElement
+ {
+ /// <summary>
+ /// Number value
+ /// </summary>
+ private string literal;
+
+ /// <summary>
+ /// Literal constructor for a text formatted number.
+ /// </summary>
+ /// <param name="value"> A number value string </param>
+ public Literal(string value)
+ {
+ literal = value ?? string.Empty;
+ }
+
+ /// <summary>
+ /// Literal constructor for a number.
+ /// </summary>
+ /// <param name="value"> A number value </param>
+ public Literal(double value)
+ {
+ literal = value.ToString();
+ }
+
+ /// <summary>
+ /// A property provides element string.
+ /// This element string is using for validation and calculation.
+ /// </summary>
+ public override string GetElement
+ {
+ get
+ {
+ return literal;
+ }
+ }
+
+ /// <summary>
+ /// A property provides displaying element string.
+ /// This displaying element is used for making formatted string.
+ /// </summary>
+ public override string GetDisplayElement
+ {
+ get
+ {
+ return literal;
+ }
+ }
+
+ /// <summary>
+ /// The method provides possibility of adding followingElement after this Literal.
+ /// This method can modify current expression by adding additional element or removing last element. </summary>
+ /// <param name="expressionElements"> Current expression, an InputElement list </param>
+ /// <param name="IsEqualUsed"> A flag value whether equal key is pressed or not </param>
+ /// <param name="IsLastValidationSucceed"> A flag value whether last calculation is succeed or not </param>
+ /// <param name="right"> An input element will be added after this element. </param>
+ /// <returns> A status of adding element to current expression. </returns>
+ public override AddingElementResult CheckPossibilityAddingElement(ref List<InputElement> expressionElements,
+ ref bool IsEqualUsed,
+ ref bool IsLastValidationSucceed,
+ InputElement right)
+ {
+ if (right is Literal)
+ {
+ Literal lastLiteral = expressionElements.Last() as Literal;
+
+ if (IsEqualUsed &&
+ IsLastValidationSucceed)
+ {
+ expressionElements.Clear();
+ expressionElements.Add(right);
+ }
+ else
+ {
+ // Exceptional case, prohibit 15 digits
+ if (lastLiteral.GetElement.Length > 14)
+ {
+ return new CantMoreThan15Digit();
+ }
+
+ // Exceptional case, prohibit 10 decimal places,
+ // OK - 0.0000000000 (10)
+ // NO - 0.00000000000 (11)
+ if (expressionElements.Count > 2 &&
+ lastLiteral.GetElement.Length == 10 &&
+ expressionElements[expressionElements.Count - 2] is Point)
+ {
+ return new CantMoreThan10Decimal();
+ }
+
+ if (lastLiteral.Append(right))
+ {
+ return new AddingPossible();
+ }
+
+ return new InvalidFormatUsed();
+ }
+ }
+ else
+ {
+ IOperator rightOper = right as IOperator;
+
+ if (right is INullary ||
+ OperandType.RIGHT == rightOper.OperandType)
+ {
+ expressionElements.Add(new Multiplication());
+ }
+
+ expressionElements.Add(right);
+
+ right.PostAddingWork(ref expressionElements);
+ }
+
+ return new AddingPossible();
+ }
+
+ /// <summary>
+ /// The methods appends a given literal.</summary>
+ /// <param name="value"> A InputElement(Literal) </param>
+ /// <returns> Result status of appending </returns>
+ public bool Append(InputElement value)
+ {
+ double number;
+ if (Double.TryParse(value.GetElement, out number) == false)
+ {
+ return false;
+ }
+
+ literal += value.GetElement;
+ return true;
+ }
+ }
+}
--- /dev/null
+
+//Copyright 2018 Samsung Electronics Co., Ltd
+//
+//Licensed under the Apache License, Version 2.0 (the "License");
+//you may not use this file except in compliance with the License.
+//You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+//Unless required by applicable law or agreed to in writing, software
+//distributed under the License is distributed on an "AS IS" BASIS,
+//WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+//See the License for the specific language governing permissions and
+//limitations under the License.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Tizen;
+
+namespace Calculator.Models
+{
+ /// <summary>
+ /// This class includes all possible operators for the InputParser and the CalculatorImpl.
+ /// Also this class includes helper methods for the input elements </summary>
+ public static class Operators
+ {
+ /// <summary>
+ /// Dictionary of the all possible operators
+ /// </summary>
+ private static IReadOnlyDictionary<String, IOperator> operators = new Dictionary<string, IOperator>()
+ {
+ { Plus.Operator, new Plus() },
+ { Minus.Operator, new Minus() },
+ { Multiplication.Operator, new Multiplication() },
+ { Division.Operator, new Division() },
+ { OpenBracket.Operator, new OpenBracket() },
+ { CloseBracket.Operator, new CloseBracket() },
+ { Point.Operator, new Point() },
+ { Reverse.Operator, new Reverse() },
+ };
+
+ /// <summary>
+ /// The property returns operator dictionary.
+ /// </summary>
+ public static IReadOnlyDictionary<String, IOperator> OperatorMap
+ {
+ get => operators;
+ }
+
+ /// <summary>
+ /// The method provides matched Operator's instance </summary>
+ /// <param name="oper"> A operator's string. </param>
+ /// <returns> Matched operator's instance.
+ /// But NULL will be returned if there is no matched operator. </returns>
+ public static IOperator GetOperator(string oper)
+ {
+ if (OperatorMap.ContainsKey(oper))
+ {
+ return OperatorMap[oper];
+ }
+
+ return null;
+ }
+
+ /// <summary>
+ /// The method provides matched Operator's instance as InputElement type </summary>
+ /// <param name="oper"> A operator's string. </param>
+ /// <returns> Matched operator's InputElement type instance.
+ /// But NULL will be returned if there is no matched operator. </returns>
+ public static InputElement GetOperatorAsInputElement(string oper)
+ {
+ if (OperatorMap.ContainsKey(oper))
+ {
+ return OperatorMap[oper] as InputElement;
+ }
+
+ return null;
+ }
+
+ /// <summary>
+ /// The method checks addingElement can be following of left argument receiving operator </summary>
+ /// <param name="addingElement"> A adding InputElementthat can be a Literal or a Operator </param>
+ /// /// <param name="isMultiplyOperatorRequired"> A flag indicates '*' is required to following addingElement if following is possible </param>
+ /// <returns> A possibility of following </returns>
+ internal static bool IsPossibleCombineWithOperatorLeft(InputElement addingElement, out bool isMultiplyOperatorRequired)
+ {
+ IOperator addingElementOper = null;
+
+ isMultiplyOperatorRequired = false;
+ if ((addingElementOper = addingElement as IOperator) == null ||
+ addingElement is INullary)
+ {
+ isMultiplyOperatorRequired = true;
+ return true;
+ }
+
+ if (OperandType.LEFT == addingElementOper.OperandType || OperandType.BOTH == addingElementOper.OperandType)
+ {
+ return false;
+ }
+
+ else if (OperandType.RIGHT == addingElementOper.OperandType)
+ {
+ isMultiplyOperatorRequired = true;
+ return true;
+ }
+
+ return false;
+ }
+
+ /// <summary>
+ /// The method checks addingElement can be following of both side argument receiving operator </summary>
+ /// <param name="addingElement"> A adding InputElementthat can be a Literal or a Operator </param>
+ /// <param name="isMultiplyOperatorRequired"> A flag indicates '*' is required to following addingElement if following is possible </param>
+ /// <returns> A possibility of following </returns>
+ internal static bool IsPossibleCombineWithOperatorRightBoth(InputElement addingElement, out bool isMultiplyOperatorRequired)
+ {
+ IOperator addingElementOper = null;
+
+ isMultiplyOperatorRequired = false;
+
+ if ((addingElementOper = addingElement as IOperator) == null ||
+ addingElement is INullary)
+ {
+ return true;
+ }
+
+ if (addingElementOper is IUnaryOperator &&
+ OperandType.RIGHT == addingElementOper.OperandType)
+ {
+ return true;
+ }
+
+ return false;
+ }
+
+ /// <summary>
+ /// The method checks addingElement can be following of left argument receiving operand type operator </summary>
+ /// <param name="addingElement"> A adding InputElementthat can be a Literal or a Operator </param>
+ /// <param name="isMultiplyOperatorRequired"> A flag indicates '*' is required to following addingElement if following is possible </param>
+ /// <returns> A possibility of following </returns>
+ internal static bool IsPossibleCombineWithOperandOperatorLeft(InputElement addingElement, out bool isMultiplyOperatorRequired)
+ {
+ IOperator addingElementOper = null;
+
+ isMultiplyOperatorRequired = false;
+
+ if ((addingElementOper = addingElement as IOperator) == null ||
+ addingElement is INullary)
+ {
+ isMultiplyOperatorRequired = true;
+ return true;
+ }
+
+ if (OperandType.LEFT == addingElementOper.OperandType || OperandType.BOTH == addingElementOper.OperandType)
+ {
+ return true; // ex ))
+ }
+
+ else if (OperandType.RIGHT == addingElementOper.OperandType)
+ {
+ isMultiplyOperatorRequired = true;
+ return true;
+ }
+
+ return false;
+ }
+
+ /// <summary>
+ /// The method checks addingElement can be following of both side argument receiving operand type operator </summary>
+ /// <param name="addingElement"> A adding InputElementthat can be a Literal or a Operator </param>
+ /// <param name="isMultiplyOperatorRequired"> A flag indicates '*' is required to following addingElement if following is possible </param>
+ /// <returns> A possibility of following </returns>
+ internal static bool IsPossibleCombineWithOperandOperatorRightBoth(InputElement addingElement, out bool isMultiplyOperatorRequired)
+ {
+ IOperator addingElementOper = null;
+
+ isMultiplyOperatorRequired = false;
+
+ if ((addingElementOper = addingElement as IOperator) == null ||
+ addingElement is INullary)
+ {
+ return true;
+ }
+
+ if (addingElementOper is IUnaryOperator &&
+ OperandType.RIGHT == addingElementOper.OperandType)
+ {
+ return true;
+ }
+
+ return false;
+ }
+
+ /// <summary>
+ /// The method checks addingElement can be following of no argument receiving operand type operator </summary>
+ /// <param name="addingElement"> A adding InputElementthat can be a Literal or a Operator </param>
+ /// <param name="isMultiplyOperatorRequired"> A flag indicates '*' is required to following addingElement if following is possible </param>
+ /// <returns> A possibility of following </returns>
+ internal static bool IsPossibleCombineOperandOperatorWithNone(InputElement addingElement, out bool isMultiplyOperatorRequired)
+ {
+ IOperator addingElementOper = null;
+
+ isMultiplyOperatorRequired = false;
+ if ((addingElementOper = addingElement as IOperator) == null ||
+ addingElement is INullary ||
+ OperandType.RIGHT == addingElementOper.OperandType)
+ {
+ isMultiplyOperatorRequired = true;
+ return true;
+ }
+
+ return true;
+ }
+
+ /// <summary>
+ /// The method checks whether addingElement can be following to current expression or
+ /// add '*' if the addingElement is required</summary>
+ /// <param name="expressionElements"> Current expression, an InputElement list </param>
+ /// <param name="isEqualUsed"> A flag value whether equal key is pressed or not </param>
+ /// <param name="isLastValidationSucceed"> A flag value whether last calculation is succeed or not </param>
+ /// <param name="addingElement"> A InputElement will be added after this element. </param>
+ /// <returns> A status of adding element to exist expression </returns>
+ private static bool PreInsertingWork(ref List<InputElement> expressionElements,
+ ref bool isEqualUsed,
+ ref bool isLastValidationSucceed,
+ InputElement addingElement)
+ {
+ if (expressionElements.Count == 0)
+ {
+ throw new Exception("This function should not be called if there is no inputed element");
+ }
+
+ if (expressionElements.Last() is Literal &&
+ addingElement is IOperator)
+ {
+ IOperator inputOper = Operators.GetOperator(addingElement);
+ if (inputOper is INullary ||
+ OperandType.RIGHT == inputOper?.OperandType)
+ {
+ expressionElements.Add(new Multiplication());
+ }
+
+ return true;
+ }
+
+ IOperator lastOper = expressionElements.Last() as IOperator;
+ bool isMultiplyRequird = false;
+ bool res = false;
+
+ if (lastOper is Point &&
+ addingElement is IOperator)
+ {
+ IOperator inputOper = Operators.GetOperator(addingElement);
+ expressionElements.RemoveAt(expressionElements.Count - 1);
+ if (OperandType.RIGHT == inputOper.OperandType ||
+ OperandType.NONE == inputOper.OperandType)
+ {
+ expressionElements.Add(new Multiplication());
+ }
+
+ return true;
+ }
+
+ if (OperandType.RIGHT == lastOper.OperandType ||
+ OperandType.BOTH == lastOper.OperandType)
+ {
+ if (lastOper is IUnaryOperator)
+ {
+ res = Operators.IsPossibleCombineWithOperandOperatorRightBoth(addingElement, out isMultiplyRequird);
+ }
+
+ else
+ {
+ res = Operators.IsPossibleCombineWithOperatorRightBoth(addingElement, out isMultiplyRequird);
+ }
+ }
+ else if (OperandType.LEFT == lastOper.OperandType)
+ {
+ if (lastOper is IUnaryOperator)
+ {
+ res = Operators.IsPossibleCombineWithOperandOperatorLeft(addingElement, out isMultiplyRequird);
+ }
+
+ else
+ {
+ res = Operators.IsPossibleCombineWithOperatorLeft(addingElement, out isMultiplyRequird);
+ }
+ }
+ else if (OperandType.NONE == lastOper.OperandType)
+ {
+ res = Operators.IsPossibleCombineOperandOperatorWithNone(addingElement, out isMultiplyRequird);
+ }
+
+ if (isMultiplyRequird)
+ {
+ expressionElements.Add(new Multiplication());
+ }
+
+ return res;
+ }
+
+ /// <summary>
+ /// The methods insert addingElement to current expression(expressionElements)</summary>
+ /// <param name="expressionElements"> Current expression, an InputElement list </param>
+ /// <param name="isEqualUsed"> A flag value whether equal key is pressed or not </param>
+ /// <param name="isLastValidationSucceed"> A flag value whether last calculation is succeed or not </param>
+ /// <param name="addingElement"> A InputElement will be added after this element. </param>
+ /// <returns> A status of adding element to exist expression </returns>
+ /// <seealso cref="PreInsertingWork(ref List{InputElement}, ref bool, ref bool, InputElement)"/>
+ internal static bool InsertingWork(ref List<InputElement> expressionElements,
+ ref bool isEqualUsed,
+ ref bool isLastValidationSucceed,
+ InputElement addingElement)
+ {
+ IOperator inputOper = Operators.GetOperator(addingElement);
+
+
+ if (PreInsertingWork(ref expressionElements, ref isEqualUsed, ref isLastValidationSucceed, addingElement) == false)
+ {
+ if (addingElement is IOperator &&
+ inputOper == null)
+ {
+ return isLastValidationSucceed = false;
+ }
+
+ IOperator lastOper = expressionElements.Last() as IOperator;
+ if (lastOper == null)
+ {
+ return isLastValidationSucceed = false;
+ }
+
+ if (OperandType.BOTH == lastOper.OperandType &&
+ OperandType.BOTH == inputOper?.OperandType)
+ {
+ expressionElements.RemoveAt(expressionElements.Count - 1);
+ expressionElements.Add(addingElement);
+ return true;
+ }
+
+ return isLastValidationSucceed = false;
+ }
+
+ expressionElements.Add(addingElement);
+
+ addingElement.PostAddingWork(ref expressionElements);
+ return true;
+ }
+ }
+
+ /// <summary>
+ /// A plus operator. </summary>
+ public class Plus : InputElement, IBinaryOperator
+ {
+ public static string Operator = "+";
+ /// <summary>
+ /// A property provides element string.
+ /// This element string is using for validation and calculation. </summary>
+ public override string GetElement
+ {
+ get => Operator;
+ }
+
+ /// <summary>
+ /// A property provides displaying element string.
+ /// This displaying element is using for making formatted string. </summary>
+ public override string GetDisplayElement
+ {
+ get => "+";
+ }
+
+ /// <summary>
+ /// A operator priority </summary>
+ public int Priority
+ {
+ get => 2;
+ }
+
+ /// <summary>
+ /// A operator's operand type </summary>
+ /// <seealso cref="OperandType">
+ /// A operand type. </seealso>
+ public OperandType OperandType
+ {
+ get => OperandType.BOTH;
+ }
+
+ /// <summary>
+ /// The method provides possibility of that is it possible adding addingElement after this InputElement.
+ /// This method can modify current expression by adding additional element or removing last element. </summary>
+ /// <param name="expressionElements"> Current expression, an InputElement list </param>
+ /// <param name="isEqualUsed"> A flag value whether equal key is pressed or not </param>
+ /// <param name="isLastValidationSucceed"> A flag value whether last calculation is succeed or not </param>
+ /// <param name="addingElement"> A InputElement will be added after this element. </param>
+ /// <returns> A status of adding element to exist expression </returns>
+ public override AddingElementResult CheckPossibilityAddingElement(ref List<InputElement> expressionElements,
+ ref bool isEqualUsed,
+ ref bool isLastValidationSucceed,
+ InputElement addingElement)
+ {
+ if (Operators.InsertingWork(ref expressionElements, ref isEqualUsed, ref isLastValidationSucceed, addingElement))
+ {
+ return new AddingPossible();
+ }
+
+ return new InvalidFormatUsed();
+ }
+
+ /// <summary>
+ /// The method provides result of operator </summary>
+ /// <param name="left"> A left side element. </param>
+ /// <param name="right"> A rightside element. </param>
+ /// <param name="result"> Calculation result. </param>
+ public void GetResult(double left, double right, out double result)
+ {
+ result = left + right;
+ }
+ }
+
+ /// <summary>
+ /// A minus operator </summary>
+ public class Minus : InputElement, IBinaryOperator
+ {
+ public static string Operator = "-";
+ /// <summary>
+ /// A property provides element string.
+ /// This element string is using for validation and calculation. </summary>
+ public override string GetElement
+ {
+ get => Operator;
+ }
+
+ /// <summary>
+ /// A property provides displaying element string.
+ /// This displaying element is using for making formatted string. </summary>
+ public override string GetDisplayElement
+ {
+ get => "-";
+ }
+
+ /// <summary>
+ /// A operator priority </summary>
+ public int Priority
+ {
+ get => 2;
+ }
+
+ /// <summary>
+ /// A operator's operand type </summary>
+ /// <seealso cref="OperandType">
+ /// A operand type. </seealso>
+ public OperandType OperandType
+ {
+ get => OperandType.BOTH;
+ }
+
+ /// <summary>
+ /// The method provides possibility of that is it possible adding addingElement after this InputElement.
+ /// This method can modify current expression by adding additional element or removing last element. </summary>
+ /// <param name="expressionElements"> Current expression, an InputElement list </param>
+ /// <param name="isEqualUsed"> A flag value whether equal key is pressed or not </param>
+ /// <param name="isLastValidationSucceed"> A flag value whether last calculation is succeed or not </param>
+ /// <param name="addingElement"> A InputElement will be added after this element. </param>
+ /// <returns> A status of adding element to exist expression </returns>
+ public override AddingElementResult CheckPossibilityAddingElement(ref List<InputElement> expressionElements,
+ ref bool isEqualUsed,
+ ref bool isLastValidationSucceed,
+ InputElement addingElement)
+ {
+ if (Operators.InsertingWork(ref expressionElements, ref isEqualUsed, ref isLastValidationSucceed, addingElement))
+ {
+ return new AddingPossible();
+ }
+
+ return new InvalidFormatUsed();
+ }
+
+ /// <summary>
+ /// The method provides result of operator </summary>
+ /// <param name="left"> A left side element. </param>
+ /// <param name="right"> A rightside element. </param>
+ /// <param name="result"> Calculation result. </param>
+ public void GetResult(double left, double addingElement, out double result)
+ {
+ result = left - addingElement;
+ }
+ }
+
+ /// <summary>
+ /// A multiplication operator </summary>
+ public class Multiplication : InputElement, IBinaryOperator
+ {
+ public static string Operator = "*";
+ /// <summary>
+ /// A property provides element string.
+ /// This element string is using for validation and calculation. </summary>
+ public override string GetElement
+ {
+ get => Operator;
+ }
+
+ /// <summary>
+ /// A property provides displaying element string.
+ /// This displaying element is using for making formatted string. </summary>
+ public override string GetDisplayElement
+ {
+ get =>"×";
+ }
+
+ /// <summary>
+ /// A operator priority </summary>
+ public int Priority
+ {
+ get => 3;
+ }
+
+ /// <summary>
+ /// A operator's operand type </summary>
+ /// <seealso cref="OperandType">
+ /// A operand type. </seealso>
+ public OperandType OperandType
+ {
+ get => OperandType.BOTH;
+ }
+
+ /// <summary>
+ /// The method provides possibility of that is it possible adding addingElement after this InputElement.
+ /// This method can modify current expression by adding additional element or removing last element. </summary>
+ /// <param name="expressionElements"> Current expression, an InputElement list </param>
+ /// <param name="isEqualUsed"> A flag value whether equal key is pressed or not </param>
+ /// <param name="isLastValidationSucceed"> A flag value whether last calculation is succeed or not </param>
+ /// <param name="addingElement"> A InputElement will be added after this element. </param>
+ /// <returns> A status of adding element to exist expression </returns>
+ public override AddingElementResult CheckPossibilityAddingElement(ref List<InputElement> expressionElements,
+ ref bool isEqualUsed,
+ ref bool isLastValidationSucceed,
+ InputElement addingElement)
+ {
+ if (Operators.InsertingWork(ref expressionElements, ref isEqualUsed, ref isLastValidationSucceed, addingElement))
+ {
+ return new AddingPossible();
+ }
+
+ return new InvalidFormatUsed();
+ }
+
+ /// <summary>
+ /// The method provides result of operator </summary>
+ /// <param name="left"> A left side element. </param>
+ /// <param name="right"> A rightside element. </param>
+ /// <param name="result"> Calculation result. </param>
+ public void GetResult(double left, double right, out double result)
+ {
+ result = left * right;
+ }
+ }
+
+ /// <summary>
+ /// A division operator </summary>
+ public class Division : InputElement, IBinaryOperator
+ {
+ public static string Operator = "/";
+ /// <summary>
+ /// A property provides element string.
+ /// This element string is using for validation and calculation. </summary>
+ public override string GetElement
+ {
+ get => Operator;
+ }
+
+ /// <summary>
+ /// A property provides displaying element string.
+ /// This displaying element is using for making formatted string. </summary>
+ public override string GetDisplayElement
+ {
+ get => "÷";
+ }
+
+ /// <summary>
+ /// A operator priority </summary>
+ public int Priority
+ {
+ get => 3;
+ }
+
+ /// <summary>
+ /// A operator's operand type </summary>
+ /// <seealso cref="OperandType">
+ /// A operand type. </seealso>
+ public OperandType OperandType
+ {
+ get => OperandType.BOTH;
+ }
+
+ /// <summary>
+ /// The method provides possibility of that is it possible adding addingElement after this InputElement.
+ /// This method can modify current expression by adding additional element or removing last element. </summary>
+ /// <param name="expressionElements"> Current expression, an InputElement list </param>
+ /// <param name="isEqualUsed"> A flag value whether equal key is pressed or not </param>
+ /// <param name="isLastValidationSucceed"> A flag value whether last calculation is succeed or not </param>
+ /// <param name="addingElement"> A InputElement will be added after this element. </param>
+ /// <returns> A status of adding element to exist expression </returns>
+ public override AddingElementResult CheckPossibilityAddingElement(ref List<InputElement> expressionElements,
+ ref bool isEqualUsed,
+ ref bool isLastValidationSucceed,
+ InputElement addingElement)
+ {
+ if (Operators.InsertingWork(ref expressionElements, ref isEqualUsed, ref isLastValidationSucceed, addingElement))
+ {
+ return new AddingPossible();
+ }
+
+ return new InvalidFormatUsed();
+ }
+
+ /// <summary>
+ /// The method provides result of operator </summary>
+ /// <param name="left"> A left side element. </param>
+ /// <param name="right"> A rightside element. </param>
+ /// <param name="result"> Calculation result. </param>
+ public void GetResult(double left, double right, out double result)
+ {
+ result = 0;
+ if (right == 0)
+ {
+ throw new DivideByZeroException();
+ }
+
+ result = left / right;
+ }
+ }
+
+ /// <summary>
+ /// A opening parenthesis operator </summary>
+ public class OpenBracket : InputElement, IUnaryOperator
+ {
+ public static string Operator = "(";
+ /// <summary>
+ /// A property provides element string.
+ /// This element string is using for validation and calculation. </summary>
+ public override string GetElement
+ {
+ get => Operator;
+ }
+
+ /// <summary>
+ /// A property provides displaying element string.
+ /// This displaying element is using for making formatted string. </summary>
+ public override string GetDisplayElement
+ {
+ get => "(";
+ }
+
+ /// <summary>
+ /// A operator priority </summary>
+ public int Priority
+ {
+ get => 1;
+ }
+
+ /// <summary>
+ /// A operator's operand type </summary>
+ /// <seealso cref="OperandType">
+ /// A operand type. </seealso>
+ public OperandType OperandType
+ {
+ get => OperandType.RIGHT;
+ }
+
+ /// <summary>
+ /// The method provides possibility of that is it possible adding addingElement after this InputElement.
+ /// This method can modify current expression by adding additional element or removing last element. </summary>
+ /// <param name="expressionElements"> Current expression, an InputElement list </param>
+ /// <param name="isEqualUsed"> A flag value whether equal key is pressed or not </param>
+ /// <param name="isLastValidationSucceed"> A flag value whether last calculation is succeed or not </param>
+ /// <param name="addingElement"> A InputElement will be added after this element. </param>
+ /// <returns> A status of adding element to exist expression </returns>
+ public override AddingElementResult CheckPossibilityAddingElement(ref List<InputElement> expressionElements,
+ ref bool isEqualUsed,
+ ref bool isLastValidationSucceed,
+ InputElement addingElement)
+ {
+ if (Operators.InsertingWork(ref expressionElements, ref isEqualUsed, ref isLastValidationSucceed, addingElement))
+ {
+ return new AddingPossible();
+ }
+
+ return new InvalidFormatUsed();
+ }
+
+ public override bool AlternativeWork(ref List<InputElement> expressionElements,
+ ref bool isEqualUsed,
+ ref bool isLastValidationSucceed)
+ {
+ if (GetNumberOfOpenedBracket(expressionElements) > 0)
+ {
+ return Operators.InsertingWork(ref expressionElements, ref isEqualUsed, ref isLastValidationSucceed, new CloseBracket());
+ }
+
+ return Operators.InsertingWork(ref expressionElements, ref isEqualUsed, ref isLastValidationSucceed, new OpenBracket());
+ }
+
+ /// <summary>
+ /// The method provides result of operator </summary>
+ /// <param name="left"> A left side element. </param>
+ /// <param name="right"> A right side element. </param>
+ /// <param name="result"> Calculation result. </param>
+ public void GetResult(double left, double right, out double result)
+ {
+ throw new Exception();
+ }
+
+ /// <summary>
+ /// Return a number of unclosed parenthesis </summary>
+ /// <param name="expressionElements"> An expression. </param>
+ /// <returns> A number of unclosed parenthesizes </returns>
+ private int GetNumberOfOpenedBracket(List<InputElement> expressionElements)
+ {
+ int res = 0;
+ foreach (var item in expressionElements)
+ {
+ if (item.CompareTo("(") == 0)
+ {
+ res += 1;
+ }
+
+ else if (item.CompareTo(")") == 0)
+ {
+ res -= 1;
+ }
+ }
+
+ return res;
+ }
+ }
+
+
+ /// <summary>
+ /// A closing parenthesis operator </summary>
+ public class CloseBracket : InputElement, IUnaryOperator
+ {
+ public static string Operator = ")";
+ /// <summary>
+ /// A property provides element string.
+ /// This element string is using for validation and calculation. </summary>
+ public override string GetElement
+ {
+ get => Operator;
+ }
+
+ /// <summary>
+ /// A property provides displaying element string.
+ /// This displaying element is using for making formatted string. </summary>
+ public override string GetDisplayElement
+ {
+ get => ")";
+ }
+
+ /// <summary>
+ /// A operator priority </summary>
+ public int Priority
+ {
+ get => 1;
+ }
+
+ /// <summary>
+ /// A operator's operand type </summary>
+ /// <seealso cref="OperandType">
+ /// A operand type. </seealso>
+ public OperandType OperandType
+ {
+ get => OperandType.LEFT;
+ }
+
+ /// <summary>
+ /// The method provides possibility of that is it possible adding addingElement after this InputElement.
+ /// This method can modify current expression by adding additional element or removing last element. </summary>
+ /// <param name="expressionElements"> Current expression, an InputElement list </param>
+ /// <param name="isEqualUsed"> A flag value whether equal key is pressed or not </param>
+ /// <param name="isLastValidationSucceed"> A flag value whether last calculation is succeed or not </param>
+ /// <param name="addingElement"> A InputElement will be added after this element. </param>
+ /// <returns> A status of adding element to exist expression </returns>
+ public override AddingElementResult CheckPossibilityAddingElement(ref List<InputElement> expressionElements,
+ ref bool isEqualUsed,
+ ref bool isLastValidationSucceed,
+ InputElement addingElement)
+ {
+ if (Operators.InsertingWork(ref expressionElements, ref isEqualUsed, ref isLastValidationSucceed, addingElement))
+ {
+ return new AddingPossible();
+ }
+
+ return new InvalidFormatUsed();
+ }
+
+ /// <summary>
+ /// The method provides result of operator </summary>
+ /// <param name="left"> A left side element. </param>
+ /// <param name="right"> A right side element. </param>
+ /// <param name="result"> Calculation result. </param>
+ public void GetResult(double left, double right, out double result)
+ {
+ throw new Exception();
+ }
+ }
+
+ /// <summary>
+ /// A point adding operator </summary>
+ public class Point : InputElement, IBinaryOperator
+ {
+ public static string Operator = ".";
+ /// <summary>
+ /// A property provides element string.
+ /// This element string is using for validation and calculation. </summary>
+ public override string GetElement
+ {
+ get => Operator;
+ }
+
+ /// <summary>
+ /// A property provides displaying element string.
+ /// This displaying element is using for making formatted string. </summary>
+ public override string GetDisplayElement
+ {
+ get => ".";
+ }
+
+ /// <summary>
+ /// A operator priority </summary>
+ public int Priority
+ {
+ get => 4;
+ }
+
+ /// <summary>
+ /// A operator's operand type </summary>
+ /// <seealso cref="OperandType">
+ /// A operand type. </seealso>
+ public OperandType OperandType
+ {
+ get => OperandType.BOTH;
+ }
+
+ /// <summary>
+ /// The method provides possibility of that is it possible adding addingElement after this InputElement.
+ /// This method can modify current expression by adding additional element or removing last element. </summary>
+ /// <param name="expressionElements"> Current expression, an InputElement list </param>
+ /// <param name="isEqualUsed"> A flag value whether equal key is pressed or not </param>
+ /// <param name="isLastValidationSucceed"> A flag value whether last calculation is succeed or not </param>
+ /// <param name="addingElement"> A InputElement will be added after this element. </param>
+ /// <returns> A status of adding element to exist expression </returns>
+ public override AddingElementResult CheckPossibilityAddingElement(ref List<InputElement> expressionElements,
+ ref bool isEqualUsed,
+ ref bool isLastValidationSucceed,
+ InputElement addingElement)
+ {
+ if (Operators.InsertingWork(ref expressionElements, ref isEqualUsed, ref isLastValidationSucceed, addingElement))
+ {
+ return new AddingPossible();
+ }
+
+ return new InvalidFormatUsed();
+ }
+
+ /// <summary>
+ /// The method provides result of operator </summary>
+ /// <param name="left"> A left side element. </param>
+ /// <param name="right"> A rightside element. </param>
+ /// <param name="result"> Calculation result. </param>
+ public void GetResult(double left, double right, out double result)
+ {
+ double decimalPlace = right;
+ while (decimalPlace >= 1)
+ {
+ decimalPlace = decimalPlace / 10;
+ }
+
+ if (left < 0)
+ result = left - decimalPlace;
+ else
+ result = left + decimalPlace;
+ }
+ }
+
+ /// <summary>
+ /// A reverse sign operator </summary>
+ public class Reverse : InputElement, IUnaryOperator
+ {
+ public static string Operator = "R";
+ /// <summary>
+ /// A property provides element string.
+ /// This element string is using for validation and calculation. </summary>
+ public override string GetElement
+ {
+ get => Operator;
+ }
+
+ /// <summary>
+ /// A property provides displaying element string.
+ /// This displaying element is using for making formatted string. </summary>
+ public override string GetDisplayElement
+ {
+ get => "-";
+ }
+
+ /// <summary>
+ /// A operator priority </summary>
+ public int Priority
+ {
+ get => 4;
+ }
+
+ /// <summary>
+ /// A operator's operand type </summary>
+ /// <seealso cref="OperandType">
+ /// A operand type. </seealso>
+ public OperandType OperandType
+ {
+ get => OperandType.RIGHT;
+ }
+
+ /// <summary>
+ /// The method provides possibility of that is it possible adding addingElement after this InputElement.
+ /// This method can modify current expression by adding additional element or removing last element. </summary>
+ /// <param name="expressionElements"> Current expression, an InputElement list </param>
+ /// <param name="isEqualUsed"> A flag value whether equal key is pressed or not </param>
+ /// <param name="isLastValidationSucceed"> A flag value whether last calculation is succeed or not </param>
+ /// <param name="addingElement"> A InputElement will be added after this element. </param>
+ /// <returns> A status of adding element to exist expression </returns>
+ public override AddingElementResult CheckPossibilityAddingElement(ref List<InputElement> expressionElements,
+ ref bool isEqualUsed,
+ ref bool isLastValidationSucceed,
+ InputElement addingElement)
+ {
+ if (Operators.InsertingWork(ref expressionElements, ref isEqualUsed, ref isLastValidationSucceed, addingElement))
+ {
+ return new AddingPossible();
+ }
+
+ return new InvalidFormatUsed();
+
+ }
+
+ /// <summary>
+ /// The method provides result of operator </summary>
+ /// <param name="left"> A left side element. </param>
+ /// <param name="right"> A rightside element. </param>
+ /// <param name="result"> Calculation result. </param>
+ public void GetResult(double left, double right, out double result)
+ {
+ result = left * -1;
+ }
+ }
+
+
+}
+
--- /dev/null
+
+//Copyright 2018 Samsung Electronics Co., Ltd
+//
+//Licensed under the Apache License, Version 2.0 (the "License");
+//you may not use this file except in compliance with the License.
+//You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+//Unless required by applicable law or agreed to in writing, software
+//distributed under the License is distributed on an "AS IS" BASIS,
+//WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+//See the License for the specific language governing permissions and
+//limitations under the License.
+
+using ElmSharp;
+using System;
+using Xamarin.Forms;
+using Xamarin.Forms.Platform.Tizen;
+using TizenColor = ElmSharp.Color;
+using ImageButtonRenderer = Calculator.Renderers.ImageButtonRenderer;
+
+[assembly: ExportRenderer(typeof(Calculator.Controls.ImageButton), typeof(ImageButtonRenderer))]
+namespace Calculator.Renderers
+{
+ /// <summary>
+ /// Calculator command button custom renderer
+ /// Actually to implement command button, A image is used instead a button to display as a calculator button.
+ /// </summary>
+ /// <remarks>
+ /// Please refer to Xamarin.Forms Custom Renderer
+ /// https://developer.xamarin.com/guides/xamarin-forms/custom-renderer/
+ /// </remarks>
+ class ImageButtonRenderer : ImageRenderer
+ {
+
+ /// <summary>
+ /// Tizen's gesture recognizer for Tap gesture, Long Tap gesture, Line gesture and so on.
+ /// </summary>
+ private ElmSharp.GestureLayer GestureRecognizer;
+
+ /// <summary>
+ /// Resource directory path
+ /// </summary>
+ private readonly String ResourceDirectory = Program.AppResourcePath;
+
+ /// <summary>
+ /// Command button's color
+ /// </summary>
+ private static readonly TizenColor RegularColor = ElmSharp.Color.White;
+
+ /// <summary>
+ /// Command button's color if it is touched.
+ /// </summary>
+ private static readonly TizenColor PressedColor = new TizenColor(200, 200, 200);
+
+ /// <summary>
+ /// Register touch event callback for the Tap, the Long Tap and the Line behavior.
+ /// </summary>
+ /// <param name="args"> A Image element changed event's argument </param>
+ protected override void OnElementChanged(ElementChangedEventArgs<Xamarin.Forms.Image> args)
+ {
+ base.OnElementChanged(args);
+
+ if (Control == null || Element == null)
+ {
+ return;
+ }
+
+ if (GestureRecognizer == null)
+ {
+ GestureRecognizer = new ElmSharp.GestureLayer(Control);
+ GestureRecognizer.Attach(Control);
+ }
+
+ if (args.NewElement == null)
+ {
+ GestureRecognizer.ClearCallbacks();
+ return;
+ }
+
+ Control.Color = RegularColor;
+
+ GestureRecognizer.SetTapCallback(GestureLayer.GestureType.Tap, GestureLayer.GestureState.Start, x => KeyDown() );
+ GestureRecognizer.SetTapCallback(GestureLayer.GestureType.Tap, GestureLayer.GestureState.End, x => ExecuteTapCommand() );
+ GestureRecognizer.SetTapCallback(GestureLayer.GestureType.LongTap, GestureLayer.GestureState.End, x => KeyUp() );
+ GestureRecognizer.SetTapCallback(GestureLayer.GestureType.LongTap, GestureLayer.GestureState.Abort, x => KeyUp() );
+ GestureRecognizer.SetLineCallback(GestureLayer.GestureState.Move, x => KeyUp() );
+ }
+
+ /// <summary>
+ /// Set button image's blending color.
+ /// It's right time after updating the button image source.
+ /// </summary>
+ protected override void UpdateAfterLoading(bool initialize)
+ {
+ base.UpdateAfterLoading(initialize);
+ Control.Color = RegularColor;
+ }
+
+ /// <summary>
+ /// Revert the button's color
+ /// </summary>
+ private void KeyUp()
+ {
+ Control.Color = RegularColor;
+ }
+
+ /// <summary>
+ /// A Action delegate which is restore button image as default
+ /// and execute button's Command with CommandParameter. </summary>
+ private void ExecuteTapCommand()
+ {
+ Calculator.Controls.ImageButton BtnCommand = Element as Calculator.Controls.ImageButton;
+ BtnCommand?.Command?.Execute(BtnCommand.CommandParameter);
+ KeyUp();
+ }
+
+ /// <summary>
+ /// A Action delegate which is restore button image as pressed situation. </summary>
+ private void KeyDown()
+ {
+ Control.Color = PressedColor;
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+
+//Copyright 2018 Samsung Electronics Co., Ltd
+//
+//Licensed under the Apache License, Version 2.0 (the "License");
+//you may not use this file except in compliance with the License.
+//You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+//Unless required by applicable law or agreed to in writing, software
+//distributed under the License is distributed on an "AS IS" BASIS,
+//WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+//See the License for the specific language governing permissions and
+//limitations under the License.
+
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Windows.Input;
+using Xamarin.Forms;
+
+using Calculator.Impl;
+using Calculator.Models;
+using System.Globalization;
+using System.Runtime.CompilerServices;
+
+namespace Calculator.ViewModels
+{
+ /// <summary>
+ /// A String to Color converter class.
+ /// </summary>
+ /// <remarks>
+ /// Please refer to Xamarin Custom Renderer
+ /// https://developer.xamarin.com/guides/xamarin-forms/custom-renderer/
+ /// </remarks>
+ public class StringToColorConverter : IValueConverter
+ {
+ public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ return Color.FromHex(value.ToString());
+ }
+
+ public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ return null;
+ }
+ }
+
+ /// <summary>
+ /// A Calculator ViewModel class which manages bindings and sending notifications.
+ /// </summary>
+ /// <seealso cref="Views.CalculatorMainPage">
+ public class MainPageViewModel : INotifyPropertyChanged
+ {
+ /// <summary>
+ /// Instance of MainPageViewModel which is wrapped in lazy<T> type.
+ /// </summary>
+ private static readonly Lazy<MainPageViewModel> lazy =
+ new Lazy<MainPageViewModel>(() => new MainPageViewModel());
+
+ /// <summary>
+ /// This property provides MainPageViewModel instance.
+ /// </summary>
+ public static MainPageViewModel Instance => lazy.Value;
+
+ public event PropertyChangedEventHandler PropertyChanged;
+
+ /// <summary>
+ /// This property provides formatted expression text.
+ /// </summary>
+ public FormattedString ExpressionText
+ {
+ get;
+ private set;
+ }
+
+ /// <summary>
+ /// A calculating result text.
+ /// </summary>
+ public FormattedString ResultText
+ {
+ get;
+ private set;
+ }
+
+ private static readonly String ResultRegularColor = "#7F000000";
+ private static readonly String ResultSoloColor = "#59C03A";
+
+ /// <summary>
+ /// Calculation result text color.
+ /// </summary>
+ public String ResultColor
+ {
+ get;
+ private set;
+ }
+
+ /// <summary>
+ /// An element button's command
+ /// </summary>
+ public ICommand PressButton { protected set; get; }
+ /// <summary>
+ /// A clear command button's command
+ /// </summary>
+ public ICommand Clear { protected set; get; }
+ /// <summary>
+ /// A remove last button's command
+ /// </summary>
+ public ICommand RemoveLast { protected set; get; }
+ /// <summary>
+ /// A = command button's command
+ /// </summary>
+ public ICommand Calculate { protected set; get; }
+ /// <summary>
+ /// A +/- operator button's command
+ /// </summary>
+ public ICommand Reverse { protected set; get; }
+
+ /// <summary>
+ /// MainPageViewModel constructor.
+ /// In this constructor, view initialization and commands bindings are completed.
+ /// </summary>
+ private MainPageViewModel()
+ {
+ SetDisplayEmpty();
+
+ if (CalculatorApp.InputParserInstance.IsEmptyInputElements == false)
+ {
+ IEnumerable<InputElement> plainTexts = CalculatorApp.InputParserInstance.ExpressionElements;
+ FormattedString result;
+ FormattedString expression;
+ GetCalculatedResult(plainTexts, false, out expression, out result);
+ ResultText = result;
+
+ ExpressionText = expression;
+ UpdateDisplay();
+ }
+
+ this.PressButton = new Command((value) =>
+ {
+ string input = value.ToString();
+
+ IEnumerable<InputElement> plainTexts;
+ AddingElementResult res = CalculatorApp.InputParserInstance.GetSeparatedPlainText(input, out plainTexts);
+ if ((res is AddingPossible) == false)
+ {
+ DisplayError(res.Message);
+ return;
+ }
+
+ FormattedString result;
+ FormattedString expression;
+ GetCalculatedResult(plainTexts, false, out expression, out result);
+
+ ExpressionText = expression;
+ ResultText = result;
+ UpdateDisplay();
+ });
+
+ this.Clear = new Command(() =>
+ {
+ SetDisplayEmpty();
+ CalculatorApp.InputParserInstance.Clear();
+ });
+
+ this.RemoveLast = new Command(() =>
+ {
+ IEnumerable<InputElement> plainTexts;
+ if (CalculatorApp.InputParserInstance.DeleteLast(out plainTexts) == false)
+ {
+ return;
+ }
+
+ FormattedString result;
+ FormattedString expression;
+ GetCalculatedResult(plainTexts, false, out expression, out result);
+
+ ExpressionText = expression;
+ ResultText = result;
+ UpdateDisplay();
+ });
+
+ this.Calculate = new Command(() =>
+ {
+ IEnumerable<InputElement> plainTexts;
+ if (CalculatorApp.InputParserInstance.Equal(out plainTexts) == false)
+ {
+ return;
+ }
+
+ FormattedString result;
+ FormattedString expression;
+ if (GetEqualResult(plainTexts, out expression, out result) == false)
+ {
+ return;
+ }
+
+ CalculatorApp.InputParserInstance.Set(CalculatorApp.CalculatorInstance.Result.ToString());
+ ResultText = result;
+
+ UpdateDisplay(true);
+ });
+
+ this.Reverse = new Command(() =>
+ {
+ IEnumerable<InputElement> plainTexts;
+ if (CalculatorApp.InputParserInstance.ReverseSign(out plainTexts) == false)
+ {
+ DisplayError("Invalid format used.");
+ return;
+ }
+
+ FormattedString result;
+ FormattedString expression;
+ GetCalculatedResult(plainTexts, false, out expression, out result);
+
+ ExpressionText = expression;
+ ResultText = result;
+ UpdateDisplay();
+ });
+ }
+
+ /// <summary>
+ /// The method provides calculated result and formatted expression for given expression. </summary>
+ /// <param name="inputExpression"> An expression which consist of InputElements with IEnumerable interface. </param>
+ /// <param name="isNeedCheckException"> Value indicates whether error displaying is needed. </param>
+ /// <param name="expression"> A formatted expression. </param>
+ /// <param name="result"> A formatted calculation result. </param>
+ /// <returns> A status of calculation </returns>
+ private bool GetCalculatedResult(IEnumerable<InputElement> inputExpression,
+ bool isNeedCheckException,
+ out FormattedString expression,
+ out FormattedString result)
+ {
+ expression = CalculatorApp.FormatterInstance.GetFormattedExpressionText(inputExpression);
+ result = string.Empty;
+
+ CalculationResult resCal = CalculatorApp.CalculatorInstance.SetExpression(inputExpression);
+ if ((resCal is CalculationSuccessful) == false)
+ {
+ if (isNeedCheckException)
+ {
+ DisplayError(resCal.Message);
+ return false;
+ }
+
+ return true;
+ }
+
+ result = CalculatorApp.FormatterInstance.GetFormattedOutputText(CalculatorApp.CalculatorInstance.Result.ToString());
+ return true;
+ }
+
+ /// <summary>
+ /// The method provides calculated result and formatted expression for given inputExpression. </summary>
+ /// <param name="inputExpression"> An expression which consist of InputElements with IEnumerable interface. </param>
+ /// <param name="expression"> A formatted expression. </param>
+ /// <param name="result"> A formatted calculation result. </param>
+ /// <returns> A result status of equal key execution </returns>
+ private bool GetEqualResult(IEnumerable<InputElement> inputExpression,
+ out FormattedString expression,
+ out FormattedString result)
+ {
+ expression = CalculatorApp.FormatterInstance.GetFormattedExpressionText(inputExpression);
+ result = string.Empty;
+
+ CalculationResult resCal = CalculatorApp.CalculatorInstance.Equal(inputExpression);
+ if ((resCal is CalculationSuccessful) == false)
+ {
+ DisplayError(resCal.Message);
+ return false;
+ }
+
+ result = CalculatorApp.FormatterInstance.GetFormattedOutputText(CalculatorApp.CalculatorInstance.Result.ToString());
+ return true;
+ }
+
+ /// <summary>
+ /// This method updates expression and result texts assuming calculation has not completed.
+ /// </summary>
+ private void UpdateDisplay()
+ {
+ UpdateDisplay(false);
+ }
+
+ /// <summary>
+ /// This method updates expression and result texts.
+ /// </summary>
+ /// <param name="isCalculationCompleted"> Flag value indicating whether calculation has completed or not. </param>
+ private void UpdateDisplay(bool isCalculationCompleted)
+ {
+ UpdateResultTextColor(isCalculationCompleted);
+
+ OnPropertyChanged("ExpressionText");
+ OnPropertyChanged("ResultText");
+ }
+
+ /// <summary>
+ /// This method updates result text color.
+ /// </summary>
+ /// <param name="isCalculationCompleted"> Flag value indicating whether calculation has completed or not. </param>
+ private void UpdateResultTextColor(bool isCalculationCompleted)
+ {
+ if (isCalculationCompleted)
+ {
+ if (ResultColor != ResultSoloColor)
+ {
+ ResultColor = ResultSoloColor;
+ OnPropertyChanged("ResultColor");
+ }
+ }
+ else
+ {
+ if (ResultColor != ResultRegularColor)
+ {
+ ResultColor = ResultRegularColor;
+ OnPropertyChanged("ResultColor");
+ }
+ }
+ }
+
+
+ /// <summary>
+ /// This method clears Expression and result texts.
+ /// </summary>
+ private void SetDisplayEmpty()
+ {
+ ExpressionText = string.Empty;
+ ResultText = string.Empty;
+ UpdateDisplay();
+ }
+
+ /// <summary>
+ /// This method displaying error message on the screen.
+ /// </summary>
+ /// <param name="message"> Message to be displayed </param>
+ private void DisplayError(String message)
+ {
+ if (message.Length > 0)
+ {
+ MessagingCenter.Send(this, "alert", message);
+ }
+ }
+
+ /// <summary>
+ /// The method is used to notify that property has changed. </summary>
+ /// <param name="propertyName"> A property name. </param>
+ protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
+ {
+ PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
+ }
+ }
+}
--- /dev/null
+<?xml version="1.0" encoding="utf-8" ?>
+<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
+ xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
+ xmlns:local="clr-namespace:Calculator.ViewModels"
+ xmlns:control="clr-namespace:Calculator.Controls"
+ x:Class="Calculator.Views.CalculatorMainPage">
+ <ContentPage.Resources>
+ <ResourceDictionary>
+ <local:StringToColorConverter x:Key="StringToColorConverter"/>
+
+ <Style x:Key="font1" TargetType="Label" >
+ <Setter Property="FontFamily" Value="Samsung sans" />
+ <Setter Property="FontSize" Value="7" />
+ <Setter Property="HorizontalTextAlignment" Value="End" />
+ <Setter Property="VerticalTextAlignment" Value="Center" />
+ </Style>
+
+ <Style x:Key="inputString" TargetType="Label" BasedOn="{StaticResource font1}">
+ <Setter Property="LineBreakMode" Value="HeadTruncation" />
+ </Style>
+
+ <Style x:Key="sumString" TargetType="Label" BasedOn="{StaticResource font1}">
+ <Setter Property="LineBreakMode" Value="NoWrap" />
+ </Style>
+
+ <Style x:Key="alertString" TargetType="Label" >
+ <Setter Property="FontFamily" Value="Samsung sans" />
+ <Setter Property="FontSize" Value="6" />
+ <Setter Property="VerticalTextAlignment" Value="Center" />
+ <Setter Property="LineBreakMode" Value="WordWrap" />
+ <Setter Property="TextColor" Value="Black"/>
+ <Setter Property="HorizontalTextAlignment" Value="Center" />
+ <Setter Property="BackgroundColor" Value="#DDFFFFFF" />
+ </Style>
+
+ </ResourceDictionary>
+ </ContentPage.Resources>
+
+ <ContentPage.Content>
+
+ <RelativeLayout>
+ <Grid RelativeLayout.XConstraint="{ConstraintExpression Type=RelativeToParent, Property=Width, Factor=0, Constant=0}"
+ RelativeLayout.YConstraint="{ConstraintExpression Type=RelativeToParent, Property=Height, Factor=0, Constant=0}"
+ RelativeLayout.WidthConstraint="{ConstraintExpression Type=RelativeToParent,Property=Width, Factor=1,Constant=0}"
+ RelativeLayout.HeightConstraint="{ConstraintExpression Type=RelativeToParent,Property=Height, Factor=1,Constant=0}"
+ HorizontalOptions="Fill"
+ VerticalOptions="Fill"
+ RowSpacing="1"
+ ColumnSpacing="1">
+
+ <Grid.RowDefinitions>
+ <!-- First row is there to leave empty space on the top of the screen -->
+ <RowDefinition Height="2*" />
+ <RowDefinition Height="3*" />
+ <RowDefinition Height="3*" />
+ <RowDefinition Height="4*" />
+ <RowDefinition Height="4*" />
+ <RowDefinition Height="4*" />
+ <RowDefinition Height="4*" />
+ <RowDefinition Height="4*" />
+ <RowDefinition Height="4*" />
+ </Grid.RowDefinitions>
+
+ <Grid.ColumnDefinitions>
+ <ColumnDefinition Width="100*"/>
+ <ColumnDefinition Width="100*"/>
+ <ColumnDefinition Width="100*"/>
+ <ColumnDefinition Width="100*"/>
+ <ColumnDefinition Width="100*"/>
+ <ColumnDefinition Width="100*"/>
+ <ColumnDefinition Width="100*"/>
+ <!-- Last column should be a little wider to avoid uneven spacing between columns -->
+ <ColumnDefinition Width="101*"/>
+ </Grid.ColumnDefinitions>
+
+
+ <Label x:Name="ExpressionLabel" Grid.Row="1" Grid.Column="2" Grid.ColumnSpan="4"
+ Style="{StaticResource inputString}"
+ FormattedText="{Binding ExpressionText}"/>
+
+ <Label Grid.Row="2" Grid.Column="2" Grid.ColumnSpan="4" Style="{StaticResource sumString}"
+ TextColor="{Binding ResultColor, Converter={StaticResource StringToColorConverter}}"
+ FormattedText="{Binding ResultText}" />
+
+ <control:ImageButton Grid.Row="3" Grid.Column="0" Grid.ColumnSpan="2"
+ Source="calculator_button_l_01.png"
+ Command="{Binding Clear}"
+ BackgroundColor="HotPink"/>
+ <control:ImageButton Grid.Row="3" Grid.Column="2" Grid.ColumnSpan="2"
+ Source="calculator_button_l_05.png"
+ Command="{Binding PressButton}"
+ CommandParameter="*"
+ BackgroundColor="HotPink"/>
+ <control:ImageButton Grid.Row="3" Grid.Column="4" Grid.ColumnSpan="2"
+ Source="calculator_button_l_04.png"
+ Command="{Binding PressButton}"
+ CommandParameter="/"
+ BackgroundColor="HotPink"/>
+ <control:ImageButton Grid.Row="3" Grid.Column="6" Grid.ColumnSpan="2"
+ Source="btn_delete.png"
+ Command="{Binding RemoveLast}"
+ BackgroundColor="HotPink"/>
+
+ <control:ImageButton Grid.Row="4" Grid.Column="0" Grid.ColumnSpan="2"
+ Source="calculator_button_p_number_7.png"
+ Command="{Binding PressButton}"
+ CommandParameter="7"
+ BackgroundColor="Gray" />
+ <control:ImageButton Grid.Row="4" Grid.Column="2" Grid.ColumnSpan="2"
+ Source="calculator_button_p_number_8.png"
+ Command="{Binding PressButton}"
+ CommandParameter="8"
+ BackgroundColor="Gray"/>
+ <control:ImageButton Grid.Row="4" Grid.Column="4" Grid.ColumnSpan="2"
+ Source="calculator_button_p_number_9.png"
+ Command="{Binding PressButton}"
+ CommandParameter="9"
+ BackgroundColor="Gray"/>
+ <control:ImageButton Grid.Row="4" Grid.Column="6" Grid.ColumnSpan="2"
+ Source="calculator_button_l_07.png"
+ Command="{Binding PressButton}"
+ CommandParameter="+"
+ BackgroundColor="HotPink"/>
+
+ <control:ImageButton Grid.Row="5" Grid.Column="0" Grid.ColumnSpan="2"
+ Source="calculator_button_p_number_4.png"
+ Command="{Binding PressButton}"
+ CommandParameter="4"
+ BackgroundColor="Gray"/>
+ <control:ImageButton Grid.Row="5" Grid.Column="2" Grid.ColumnSpan="2"
+ Source="calculator_button_p_number_5.png"
+ Command="{Binding PressButton}"
+ CommandParameter="5"
+ BackgroundColor="Gray"/>
+ <control:ImageButton Grid.Row="5" Grid.Column="4" Grid.ColumnSpan="2"
+ Source="calculator_button_p_number_6.png"
+ Command="{Binding PressButton}"
+ CommandParameter="6"
+ BackgroundColor="Gray"/>
+ <control:ImageButton Grid.Row="5" Grid.Column="6" Grid.ColumnSpan="2"
+ Source="calculator_button_l_06.png"
+ Command="{Binding PressButton}"
+ CommandParameter="-"
+ BackgroundColor="HotPink"/>
+
+ <control:ImageButton Grid.Row="6" Grid.Column="0" Grid.ColumnSpan="2"
+ Source="calculator_button_p_number_1.png"
+ Command="{Binding PressButton}"
+ CommandParameter="1"
+ BackgroundColor="Gray"/>
+ <control:ImageButton Grid.Row="6" Grid.Column="2" Grid.ColumnSpan="2"
+ Source="calculator_button_p_number_2.png"
+ Command="{Binding PressButton}"
+ CommandParameter="2"
+ BackgroundColor="Gray"/>
+ <control:ImageButton Grid.Row="6" Grid.Column="4" Grid.ColumnSpan="2"
+ Source="calculator_button_p_number_3.png"
+ Command="{Binding PressButton}"
+ CommandParameter="3"
+ BackgroundColor="Gray"/>
+ <control:ImageButton Grid.Row="6" Grid.Column="6" Grid.ColumnSpan="2"
+ Source="calculator_button_l_02.png"
+ Command="{Binding PressButton}"
+ CommandParameter="("
+ BackgroundColor="HotPink" />
+
+ <control:ImageButton Grid.Row="7" Grid.Column="0" Grid.ColumnSpan="3"
+ Source="calculator_button_p_number_10.png"
+ Command="{Binding PressButton}"
+ CommandParameter="."
+ BackgroundColor="Gray"/>
+ <control:ImageButton Grid.Row="7" Grid.Column="3" Grid.ColumnSpan="2"
+ Source="calculator_button_p_number_0.png"
+ Command="{Binding PressButton}"
+ CommandParameter="0"
+ BackgroundColor="Gray" />
+ <control:ImageButton Grid.Row="7" Grid.Column="5" Grid.ColumnSpan="3"
+ Source="calculator_button_p_number_11.png"
+ Command="{Binding Reverse}"
+ BackgroundColor="HotPink"/>
+
+ <control:ImageButton Grid.Row="8" Grid.Column="0" Grid.ColumnSpan="8"
+ Source="calculator_button_l_08.png"
+ Command="{Binding Calculate}"
+ BackgroundColor="DeepPink"/>
+ </Grid>
+
+ <Label RelativeLayout.XConstraint="{ConstraintExpression Type=RelativeToParent, Property=Width, Factor=0, Constant=0}"
+ RelativeLayout.YConstraint="{ConstraintExpression Type=RelativeToParent, Property=Height, Factor=0.16, Constant=0}"
+ RelativeLayout.WidthConstraint="{ConstraintExpression Type=RelativeToParent,Property=Width, Factor=1,Constant=0}"
+ RelativeLayout.HeightConstraint="{ConstraintExpression Type=RelativeToParent,Property=Height, Factor=0.1,Constant=0}"
+ HorizontalOptions="CenterAndExpand"
+ VerticalOptions="CenterAndExpand"
+ x:Name="AlertToast"
+ Style="{StaticResource alertString}"
+ IsVisible="false"/>
+
+ </RelativeLayout>
+
+
+ </ContentPage.Content>
+</ContentPage>
\ No newline at end of file
--- /dev/null
+
+//Copyright 2018 Samsung Electronics Co., Ltd
+//
+//Licensed under the Apache License, Version 2.0 (the "License");
+//you may not use this file except in compliance with the License.
+//You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+//Unless required by applicable law or agreed to in writing, software
+//distributed under the License is distributed on an "AS IS" BASIS,
+//WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+//See the License for the specific language governing permissions and
+//limitations under the License.
+
+using System.Threading;
+using System.Threading.Tasks;
+
+using Calculator.ViewModels;
+using Xamarin.Forms;
+using Xamarin.Forms.Xaml;
+
+namespace Calculator.Views
+{
+ [XamlCompilation(XamlCompilationOptions.Compile)]
+ public partial class CalculatorMainPage : ContentPage
+ {
+ private Mutex CounterMutex = new Mutex(false, "counter_mutex");
+ private int counter;
+
+ public CalculatorMainPage()
+ {
+ NavigationPage.SetHasNavigationBar(this, false);
+ InitializeComponent();
+ BindingContext = MainPageViewModel.Instance;
+
+ MessagingCenter.Subscribe<MainPageViewModel, string>(this, "alert", (sender, arg) =>
+ {
+ CounterMutex.WaitOne();
+ counter++;
+ CounterMutex.ReleaseMutex();
+
+ AlertToast.IsVisible = true;
+ AlertToast.Text = arg.ToString();
+ CloseAlertToast();
+ });
+ }
+
+ /// <summary>
+ /// This method closes alert toast after 1.5 seconds.
+ /// </summary>
+ async void CloseAlertToast()
+ {
+ await Task.Delay(1500);
+ CounterMutex.WaitOne();
+ if (--counter <= 0)
+ {
+ counter = 0;
+ AlertToast.IsVisible = false;
+ }
+
+ CounterMutex.ReleaseMutex();
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns="http://tizen.org/ns/packages" api-version="4" package="org.tizen.example.Calculator" version="1.0.0">
+ <profile name="wearable" />
+ <ui-application appid="org.tizen.example.Calculator"
+ exec="Calculator.dll"
+ type="dotnet"
+ multiple="false"
+ taskmanage="true"
+ nodisplay="false"
+ launch_mode="single">
+ <label>Calculator</label>
+ <icon>Calculator.png</icon>
+ <metadata key="http://tizen.org/metadata/prefer_dotnet_aot" value="true" />
+ </ui-application>
+</manifest>
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Weather", "Test\Weather\Weather\Weather.csproj", "{076A3B6E-64FE-422C-9D54-845BA6036DF7}"
EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Calculator", "Calculator", "{43929EEC-22EC-47D3-A4BC-3CD3C3A725A0}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Calculator", "Test\Calculator\Calculator\Calculator.csproj", "{54404384-2239-4646-9C58-49AB9DDC89FE}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
{6DADDF43-87F3-43C6-822D-12DE155CCF86}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6DADDF43-87F3-43C6-822D-12DE155CCF86}.Release|Any CPU.ActiveCfg = Release|Any CPU
{6DADDF43-87F3-43C6-822D-12DE155CCF86}.Release|Any CPU.Build.0 = Release|Any CPU
+ {076A3B6E-64FE-422C-9D54-845BA6036DF7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {076A3B6E-64FE-422C-9D54-845BA6036DF7}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {076A3B6E-64FE-422C-9D54-845BA6036DF7}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {076A3B6E-64FE-422C-9D54-845BA6036DF7}.Release|Any CPU.Build.0 = Release|Any CPU
+ {54404384-2239-4646-9C58-49AB9DDC89FE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {54404384-2239-4646-9C58-49AB9DDC89FE}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {54404384-2239-4646-9C58-49AB9DDC89FE}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {54404384-2239-4646-9C58-49AB9DDC89FE}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
{4653BFED-31B5-412F-8076-A609A4E5D851} = {232D191B-486D-4EC3-BCB2-F776124AF705}
{E1BF76E2-0454-490C-964A-587E7926CA13} = {087187F9-A361-4269-88EB-47A44185FA7F}
{6DADDF43-87F3-43C6-822D-12DE155CCF86} = {E1BF76E2-0454-490C-964A-587E7926CA13}
+ {F167380D-C156-45D1-A0BA-DB3A5AE34E98} = {087187F9-A361-4269-88EB-47A44185FA7F}
+ {076A3B6E-64FE-422C-9D54-845BA6036DF7} = {F167380D-C156-45D1-A0BA-DB3A5AE34E98}
+ {43929EEC-22EC-47D3-A4BC-3CD3C3A725A0} = {087187F9-A361-4269-88EB-47A44185FA7F}
+ {54404384-2239-4646-9C58-49AB9DDC89FE} = {43929EEC-22EC-47D3-A4BC-3CD3C3A725A0}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {81158F7F-D003-4C6D-9937-5002C54D57EA}