Revert "Calculator display changes"

This reverts commit b3cc081f47.
This commit is contained in:
serso 2012-09-21 10:26:45 +04:00
parent b3cc081f47
commit cca775c2ac
12 changed files with 1485 additions and 1528 deletions

View File

@ -1,30 +1,25 @@
package org.solovyev.android.calculator; package org.solovyev.android.calculator;
import jscl.NumeralBase; import org.jetbrains.annotations.NotNull;
import jscl.math.Generic; import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.NotNull; import org.solovyev.android.calculator.jscl.JsclOperation;
import org.jetbrains.annotations.Nullable; import org.solovyev.common.msg.MessageRegistry;
import org.solovyev.android.calculator.jscl.JsclOperation;
import org.solovyev.common.msg.MessageRegistry; /**
* User: Solovyev_S
/** * Date: 20.09.12
* User: Solovyev_S * Time: 16:38
* Date: 20.09.12 */
* Time: 16:38 public interface Calculator extends CalculatorEventContainer {
*/
public interface Calculator extends CalculatorEventContainer { @NotNull
CalculatorEventDataId createFirstEventDataId();
@NotNull
CalculatorEventDataId createFirstEventDataId(); void evaluate(@NotNull JsclOperation operation,
@NotNull String expression);
void evaluate(@NotNull JsclOperation operation,
@NotNull String expression); @NotNull
CalculatorEventDataId evaluate(@NotNull JsclOperation operation,
@NotNull @NotNull String expression,
CalculatorEventDataId evaluate(@NotNull JsclOperation operation, @Nullable MessageRegistry mr);
@NotNull String expression, }
@Nullable MessageRegistry mr);
@NotNull
CalculatorEventDataId convert(@NotNull Generic generic, @NotNull NumeralBase to);
}

View File

@ -1,58 +1,58 @@
package org.solovyev.android.calculator; package org.solovyev.android.calculator;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.solovyev.android.calculator.jscl.JsclOperation; import org.solovyev.android.calculator.jscl.JsclOperation;
/** /**
* User: serso * User: serso
* Date: 9/20/12 * Date: 9/20/12
* Time: 10:01 PM * Time: 10:01 PM
*/ */
public class CalculatorEvaluationEventDataImpl implements CalculatorEvaluationEventData { public class CalculatorEvaluationEventDataImpl implements CalculatorEvaluationEventData {
@NotNull @NotNull
private final CalculatorEventData calculatorEventData; private final CalculatorEventData calculatorEventData;
@NotNull @NotNull
private final JsclOperation operation; private final JsclOperation operation;
@NotNull @NotNull
private final String expression; private final String expression;
public CalculatorEvaluationEventDataImpl(@NotNull CalculatorEventData calculatorEventData, public CalculatorEvaluationEventDataImpl(@NotNull CalculatorEventData calculatorEventData,
@NotNull JsclOperation operation, @NotNull JsclOperation operation,
@NotNull String expression) { @NotNull String expression) {
this.calculatorEventData = calculatorEventData; this.calculatorEventData = calculatorEventData;
this.operation = operation; this.operation = operation;
this.expression = expression; this.expression = expression;
} }
@NotNull @NotNull
@Override @Override
public JsclOperation getOperation() { public JsclOperation getOperation() {
return this.operation; return this.operation;
} }
@NotNull @NotNull
@Override @Override
public String getExpression() { public String getExpression() {
return this.expression; return this.expression;
} }
@Override @Override
public long getEventId() { public long getEventId() {
return calculatorEventData.getEventId(); return calculatorEventData.getEventId();
} }
@Override @Override
@Nullable @Nullable
public Long getSequenceId() { public Long getCalculationId() {
return calculatorEventData.getSequenceId(); return calculatorEventData.getCalculationId();
} }
@Override @Override
public boolean isAfter(@NotNull CalculatorEventDataId calculatorEventDataId) { public boolean isAfter(@NotNull CalculatorEventDataId calculatorEventDataId) {
return calculatorEventData.isAfter(calculatorEventDataId); return calculatorEventData.isAfter(calculatorEventDataId);
} }
} }

View File

@ -1,21 +1,20 @@
package org.solovyev.android.calculator; package org.solovyev.android.calculator;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
/** /**
* User: Solovyev_S * User: Solovyev_S
* Date: 20.09.12 * Date: 20.09.12
* Time: 18:18 * Time: 18:18
*/ */
public interface CalculatorEventDataId { public interface CalculatorEventDataId {
// the higher id => the later event // the higher id => the later event
long getEventId(); long getEventId();
// the higher id => the later event @Nullable
@Nullable Long getCalculationId();
Long getSequenceId();
boolean isAfter(@NotNull CalculatorEventDataId calculatorEventDataId);
boolean isAfter(@NotNull CalculatorEventDataId calculatorEventDataId); }
}

View File

@ -1,64 +1,66 @@
package org.solovyev.android.calculator; package org.solovyev.android.calculator;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
/** /**
* User: Solovyev_S * User: Solovyev_S
* Date: 20.09.12 * Date: 20.09.12
* Time: 18:18 * Time: 18:18
*/ */
class CalculatorEventDataIdImpl implements CalculatorEventDataId { class CalculatorEventDataIdImpl implements CalculatorEventDataId {
private final long eventId; private final long eventId;
@Nullable @Nullable
private final Long sequenceId; private final Long calculationId;
private CalculatorEventDataIdImpl(long id, @Nullable Long sequenceId) { private CalculatorEventDataIdImpl(long id,
this.eventId = id; @Nullable Long calculationId) {
this.sequenceId = sequenceId; this.eventId = id;
} this.calculationId = calculationId;
}
@NotNull
static CalculatorEventDataId newInstance(long id, @Nullable Long sequenceId) { @NotNull
return new CalculatorEventDataIdImpl(id, sequenceId); static CalculatorEventDataId newInstance(long id,
} @Nullable Long calculationId) {
return new CalculatorEventDataIdImpl(id, calculationId);
@Override }
public long getEventId() {
return this.eventId; @Override
} public long getEventId() {
return this.eventId;
@Nullable }
@Override
public Long getSequenceId() { @Nullable
return this.sequenceId; @Override
} public Long getCalculationId() {
return this.calculationId;
@Override }
public boolean isAfter(@NotNull CalculatorEventDataId calculatorEventDataId) {
return this.eventId > calculatorEventDataId.getEventId(); @Override
} public boolean isAfter(@NotNull CalculatorEventDataId calculatorEventDataId) {
return this.eventId > calculatorEventDataId.getEventId();
@Override }
public boolean equals(Object o) {
if (this == o) return true; @Override
if (!(o instanceof CalculatorEventDataIdImpl)) return false; public boolean equals(Object o) {
if (this == o) return true;
CalculatorEventDataIdImpl that = (CalculatorEventDataIdImpl) o; if (!(o instanceof CalculatorEventDataIdImpl)) return false;
if (eventId != that.eventId) return false; CalculatorEventDataIdImpl that = (CalculatorEventDataIdImpl) o;
if (sequenceId != null ? !sequenceId.equals(that.sequenceId) : that.sequenceId != null)
return false; if (eventId != that.eventId) return false;
if (calculationId != null ? !calculationId.equals(that.calculationId) : that.calculationId != null)
return true; return false;
}
return true;
@Override }
public int hashCode() {
int result = (int) (eventId ^ (eventId >>> 32)); @Override
result = 31 * result + (sequenceId != null ? sequenceId.hashCode() : 0); public int hashCode() {
return result; int result = (int) (eventId ^ (eventId >>> 32));
} result = 31 * result + (calculationId != null ? calculationId.hashCode() : 0);
} return result;
}
}

View File

@ -1,57 +1,57 @@
package org.solovyev.android.calculator; package org.solovyev.android.calculator;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
/** /**
* User: Solovyev_S * User: Solovyev_S
* Date: 20.09.12 * Date: 20.09.12
* Time: 16:54 * Time: 16:54
*/ */
class CalculatorEventDataImpl implements CalculatorEventData { class CalculatorEventDataImpl implements CalculatorEventData {
@NotNull @NotNull
private CalculatorEventDataId calculatorEventDataId; private CalculatorEventDataId calculatorEventDataId;
private CalculatorEventDataImpl(@NotNull CalculatorEventDataId calculatorEventDataId) { private CalculatorEventDataImpl(@NotNull CalculatorEventDataId calculatorEventDataId) {
this.calculatorEventDataId = calculatorEventDataId; this.calculatorEventDataId = calculatorEventDataId;
} }
@NotNull @NotNull
public static CalculatorEventData newInstance(@NotNull CalculatorEventDataId calculatorEventDataId) { public static CalculatorEventData newInstance(@NotNull CalculatorEventDataId calculatorEventDataId) {
return new CalculatorEventDataImpl(calculatorEventDataId); return new CalculatorEventDataImpl(calculatorEventDataId);
} }
@Override @Override
public long getEventId() { public long getEventId() {
return calculatorEventDataId.getEventId(); return calculatorEventDataId.getEventId();
} }
@Override @Override
@Nullable @Nullable
public Long getSequenceId() { public Long getCalculationId() {
return calculatorEventDataId.getSequenceId(); return calculatorEventDataId.getCalculationId();
} }
@Override @Override
public boolean isAfter(@NotNull CalculatorEventDataId calculatorEventDataId) { public boolean isAfter(@NotNull CalculatorEventDataId calculatorEventDataId) {
return this.calculatorEventDataId.isAfter(calculatorEventDataId); return this.calculatorEventDataId.isAfter(calculatorEventDataId);
} }
@Override @Override
public boolean equals(Object o) { public boolean equals(Object o) {
if (this == o) return true; if (this == o) return true;
if (!(o instanceof CalculatorEventDataImpl)) return false; if (!(o instanceof CalculatorEventDataImpl)) return false;
CalculatorEventDataImpl that = (CalculatorEventDataImpl) o; CalculatorEventDataImpl that = (CalculatorEventDataImpl) o;
if (!calculatorEventDataId.equals(that.calculatorEventDataId)) return false; if (!calculatorEventDataId.equals(that.calculatorEventDataId)) return false;
return true; return true;
} }
@Override @Override
public int hashCode() { public int hashCode() {
return calculatorEventDataId.hashCode(); return calculatorEventDataId.hashCode();
} }
} }

View File

@ -1,55 +1,43 @@
package org.solovyev.android.calculator; package org.solovyev.android.calculator;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
/** /**
* User: Solovyev_S * User: Solovyev_S
* Date: 20.09.12 * Date: 20.09.12
* Time: 16:40 * Time: 16:40
*/ */
public enum CalculatorEventType { public enum CalculatorEventType {
/* /*
********************************************************************** **********************************************************************
* *
* org.solovyev.android.calculator.CalculatorEvaluationEventData * org.solovyev.android.calculator.CalculatorEvaluationEventData
* *
********************************************************************** **********************************************************************
*/ */
// @NotNull org.solovyev.android.calculator.CalculatorInput // @NotNull org.solovyev.android.calculator.CalculatorInput
calculation_started, calculation_started,
// @NotNull org.solovyev.android.calculator.CalculatorOutput // @NotNull org.solovyev.android.calculator.CalculatorOutput
calculation_result, calculation_result,
calculation_cancelled, calculation_cancelled,
calculation_finished, calculation_finished,
// @NotNull org.solovyev.android.calculator.CalculatorFailure // @NotNull org.solovyev.android.calculator.CalculatorFailure
calculation_failed, calculation_failed;
/* public boolean isOfType(@NotNull CalculatorEventType... types) {
********************************************************************** for (CalculatorEventType type : types) {
* if ( this == type ) {
* CONVERSION return true;
* }
********************************************************************** }
*/
conversion_started, return false;
}
// @NotNull String conversion result
conversion_finished; }
public boolean isOfType(@NotNull CalculatorEventType... types) {
for (CalculatorEventType type : types) {
if ( this == type ) {
return true;
}
}
return false;
}
}

View File

@ -1,291 +1,209 @@
package org.solovyev.android.calculator; package org.solovyev.android.calculator;
import jscl.AbstractJsclArithmeticException; import jscl.AbstractJsclArithmeticException;
import jscl.NumeralBase; import jscl.NumeralBaseException;
import jscl.NumeralBaseException; import jscl.math.Generic;
import jscl.math.Generic; import jscl.text.ParseInterruptedException;
import jscl.text.ParseInterruptedException; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.Nullable; import org.solovyev.android.calculator.jscl.JsclOperation;
import org.solovyev.android.calculator.jscl.JsclOperation; import org.solovyev.android.calculator.text.TextProcessor;
import org.solovyev.android.calculator.text.TextProcessor; import org.solovyev.common.msg.MessageRegistry;
import org.solovyev.common.msg.MessageRegistry; import org.solovyev.common.msg.MessageType;
import org.solovyev.common.msg.MessageType;
import org.solovyev.common.text.StringUtils; import java.util.List;
import org.solovyev.math.units.UnitConverter; import java.util.concurrent.Executor;
import org.solovyev.math.units.UnitImpl; import java.util.concurrent.Executors;
import org.solovyev.math.units.UnitType; import java.util.concurrent.atomic.AtomicLong;
import java.util.List; /**
import java.util.concurrent.Executor; * User: Solovyev_S
import java.util.concurrent.Executors; * Date: 20.09.12
import java.util.concurrent.atomic.AtomicLong; * Time: 16:42
*/
/** public class CalculatorImpl implements Calculator {
* User: Solovyev_S
* Date: 20.09.12 private static final long FIRST_ID = 0;
* Time: 16:42
*/ @NotNull
public class CalculatorImpl implements Calculator { private final CalculatorEventContainer calculatorEventContainer = new ListCalculatorEventContainer();
private static final long FIRST_ID = 0; @NotNull
private final AtomicLong counter = new AtomicLong(FIRST_ID);
@NotNull
private final CalculatorEventContainer calculatorEventContainer = new ListCalculatorEventContainer(); @NotNull
private final Object lock = new Object();
@NotNull
private final AtomicLong counter = new AtomicLong(FIRST_ID); @NotNull
private final TextProcessor<PreparedExpression, String> preprocessor = ToJsclTextProcessor.getInstance();
@NotNull
private final Object lock = new Object(); @NotNull
private final Executor threadPoolExecutor = Executors.newFixedThreadPool(10);
@NotNull
private final TextProcessor<PreparedExpression, String> preprocessor = ToJsclTextProcessor.getInstance(); public CalculatorImpl() {
}
@NotNull
private final Executor threadPoolExecutor = Executors.newFixedThreadPool(10); @NotNull
private CalculatorEventDataId nextCalculatorEventDataId() {
public CalculatorImpl() { long eventId = counter.incrementAndGet();
} return CalculatorEventDataIdImpl.newInstance(eventId, eventId);
}
@NotNull
public static String doConversion(@NotNull UnitConverter<String> converter, @NotNull
@Nullable String from, private CalculatorEventDataId nextEventDataId(@NotNull Long calculationId) {
@NotNull UnitType<String> fromUnitType, long eventId = counter.incrementAndGet();
@NotNull UnitType<String> toUnitType) throws ConversionException{ return CalculatorEventDataIdImpl.newInstance(eventId, calculationId);
final String result; }
if (StringUtils.isEmpty(from)) { /*
result = ""; **********************************************************************
} else { *
* CALCULATION
String to = null; *
try { **********************************************************************
if (converter.isSupported(fromUnitType, toUnitType)) { */
to = converter.convert(UnitImpl.newInstance(from, fromUnitType), toUnitType).getValue();
} @NotNull
} catch (RuntimeException e) { @Override
throw new ConversionException(e); public CalculatorEventDataId createFirstEventDataId() {
} return CalculatorEventDataIdImpl.newInstance(FIRST_ID, FIRST_ID);
}
result = to;
} @Override
public void evaluate(@NotNull JsclOperation operation,
return result; @NotNull String expression) {
} evaluate(operation, expression, null);
}
@NotNull
private CalculatorEventDataId nextCalculatorEventDataId() { @Override
long eventId = counter.incrementAndGet(); @NotNull
return CalculatorEventDataIdImpl.newInstance(eventId, eventId); public CalculatorEventDataId evaluate(@NotNull final JsclOperation operation,
} @NotNull final String expression,
@Nullable final MessageRegistry mr) {
@NotNull
private CalculatorEventDataId nextEventDataId(@NotNull Long sequenceId) { final CalculatorEventDataId eventDataId = nextCalculatorEventDataId();
long eventId = counter.incrementAndGet();
return CalculatorEventDataIdImpl.newInstance(eventId, sequenceId); threadPoolExecutor.execute(new Runnable() {
} @Override
public void run() {
/* CalculatorImpl.this.evaluate(eventDataId.getCalculationId(), operation, expression, mr);
********************************************************************** }
* });
* CALCULATION
* return eventDataId;
********************************************************************** }
*/
private void evaluate(@NotNull Long calculationId,
@NotNull @NotNull JsclOperation operation,
@Override @NotNull String expression,
public CalculatorEventDataId createFirstEventDataId() { @Nullable MessageRegistry mr) {
return CalculatorEventDataIdImpl.newInstance(FIRST_ID, FIRST_ID); synchronized (lock) {
}
PreparedExpression preparedExpression = null;
@Override
public void evaluate(@NotNull JsclOperation operation, fireCalculatorEvent(newCalculationEventData(operation, expression, calculationId), CalculatorEventType.calculation_started, new CalculatorInputImpl(expression, operation));
@NotNull String expression) {
evaluate(operation, expression, null); try {
} preparedExpression = preprocessor.process(expression);
@Override final String jsclExpression = preparedExpression.toString();
@NotNull
public CalculatorEventDataId evaluate(@NotNull final JsclOperation operation, try {
@NotNull final String expression,
@Nullable final MessageRegistry mr) { final Generic result = operation.evaluateGeneric(jsclExpression);
final CalculatorEventDataId eventDataId = nextCalculatorEventDataId(); // NOTE: toString() method must be called here as ArithmeticOperationException may occur in it (just to avoid later check!)
result.toString();
threadPoolExecutor.execute(new Runnable() {
@Override final CalculatorOutputImpl data = new CalculatorOutputImpl(operation.getFromProcessor().process(result), operation, result);
public void run() { fireCalculatorEvent(newCalculationEventData(operation, expression, calculationId), CalculatorEventType.calculation_result, data);
CalculatorImpl.this.evaluate(eventDataId.getSequenceId(), operation, expression, mr);
} } catch (AbstractJsclArithmeticException e) {
}); handleException(calculationId, operation, expression, mr, new CalculatorEvalException(e, e, jsclExpression));
}
return eventDataId;
} } catch (ArithmeticException e) {
handleException(calculationId, operation, expression, mr, preparedExpression, new CalculatorParseException(expression, new CalculatorMessage(CalculatorMessages.msg_001, MessageType.error, e.getMessage())));
@NotNull } catch (StackOverflowError e) {
@Override handleException(calculationId, operation, expression, mr, preparedExpression, new CalculatorParseException(expression, new CalculatorMessage(CalculatorMessages.msg_002, MessageType.error)));
public CalculatorEventDataId convert(@NotNull final Generic generic, } catch (jscl.text.ParseException e) {
@NotNull final NumeralBase to) { handleException(calculationId, operation, expression, mr, preparedExpression, new CalculatorParseException(e));
final CalculatorEventDataId eventDataId = nextCalculatorEventDataId(); } catch (ParseInterruptedException e) {
threadPoolExecutor.execute(new Runnable() { // do nothing - we ourselves interrupt the calculations
@Override fireCalculatorEvent(newCalculationEventData(operation, expression, calculationId), CalculatorEventType.calculation_cancelled, null);
public void run() {
final Long sequenceId = eventDataId.getSequenceId(); } catch (CalculatorParseException e) {
assert sequenceId != null; handleException(calculationId, operation, expression, mr, preparedExpression, e);
} finally {
fireCalculatorEvent(newConversionEventData(sequenceId), CalculatorEventType.conversion_started, null); fireCalculatorEvent(newCalculationEventData(operation, expression, calculationId), CalculatorEventType.calculation_finished, null);
}
final NumeralBase from = CalculatorLocatorImpl.getInstance().getCalculatorEngine().getEngine().getNumeralBase(); }
}
if (from != to) {
String fromString = generic.toString(); @NotNull
if (!StringUtils.isEmpty(fromString)) { private CalculatorEventData newCalculationEventData(@NotNull JsclOperation operation,
try { @NotNull String expression,
fromString = ToJsclTextProcessor.getInstance().process(fromString).getExpression(); @NotNull Long calculationId) {
} catch (CalculatorParseException e) { return new CalculatorEvaluationEventDataImpl(CalculatorEventDataImpl.newInstance(nextEventDataId(calculationId)), operation, expression);
// ok, problems while processing occurred }
}
} private void handleException(@NotNull Long calculationId,
@NotNull JsclOperation operation,
// todo serso: continue @NotNull String expression,
//doConversion(AndroidNumeralBase.getConverter(), fromString, AndroidNumeralBase.valueOf(fromString), AndroidNumeralBase.valueOf(to)); @Nullable MessageRegistry mr,
} else { @Nullable PreparedExpression preparedExpression,
fireCalculatorEvent(newConversionEventData(sequenceId), CalculatorEventType.conversion_finished, generic.toString()); @NotNull CalculatorParseException parseException) {
}
} if (operation == JsclOperation.numeric
}); && preparedExpression != null
&& preparedExpression.isExistsUndefinedVar()) {
return eventDataId;
} evaluate(calculationId, JsclOperation.simplify, expression, mr);
@NotNull }
private CalculatorEventData newConversionEventData(@NotNull Long sequenceId) {
return CalculatorEventDataImpl.newInstance(nextEventDataId(sequenceId)); fireCalculatorEvent(newCalculationEventData(operation, expression, calculationId), CalculatorEventType.calculation_failed, new CalculatorFailureImpl(parseException));
} }
private void evaluate(@NotNull Long sequenceId, private void handleException(@NotNull Long calculationId,
@NotNull JsclOperation operation, @NotNull JsclOperation operation,
@NotNull String expression, @NotNull String expression,
@Nullable MessageRegistry mr) { @Nullable MessageRegistry mr,
synchronized (lock) { @NotNull CalculatorEvalException evalException) {
PreparedExpression preparedExpression = null; if (operation == JsclOperation.numeric && evalException.getCause() instanceof NumeralBaseException) {
evaluate(calculationId, JsclOperation.simplify, expression, mr);
fireCalculatorEvent(newCalculationEventData(operation, expression, sequenceId), CalculatorEventType.calculation_started, new CalculatorInputImpl(expression, operation)); }
try { fireCalculatorEvent(newCalculationEventData(operation, expression, calculationId), CalculatorEventType.calculation_failed, new CalculatorFailureImpl(evalException));
preparedExpression = preprocessor.process(expression); }
final String jsclExpression = preparedExpression.toString(); /*
**********************************************************************
try { *
* EVENTS
final Generic result = operation.evaluateGeneric(jsclExpression); *
**********************************************************************
// NOTE: toString() method must be called here as ArithmeticOperationException may occur in it (just to avoid later check!) */
result.toString();
@Override
final CalculatorOutputImpl data = new CalculatorOutputImpl(operation.getFromProcessor().process(result), operation, result); public void addCalculatorEventListener(@NotNull CalculatorEventListener calculatorEventListener) {
fireCalculatorEvent(newCalculationEventData(operation, expression, sequenceId), CalculatorEventType.calculation_result, data); calculatorEventContainer.addCalculatorEventListener(calculatorEventListener);
}
} catch (AbstractJsclArithmeticException e) {
handleException(sequenceId, operation, expression, mr, new CalculatorEvalException(e, e, jsclExpression)); @Override
} public void removeCalculatorEventListener(@NotNull CalculatorEventListener calculatorEventListener) {
calculatorEventContainer.removeCalculatorEventListener(calculatorEventListener);
} catch (ArithmeticException e) { }
handleException(sequenceId, operation, expression, mr, preparedExpression, new CalculatorParseException(expression, new CalculatorMessage(CalculatorMessages.msg_001, MessageType.error, e.getMessage())));
} catch (StackOverflowError e) { @Override
handleException(sequenceId, operation, expression, mr, preparedExpression, new CalculatorParseException(expression, new CalculatorMessage(CalculatorMessages.msg_002, MessageType.error))); public void fireCalculatorEvent(@NotNull CalculatorEventData calculatorEventData, @NotNull CalculatorEventType calculatorEventType, @Nullable Object data) {
} catch (jscl.text.ParseException e) { calculatorEventContainer.fireCalculatorEvent(calculatorEventData, calculatorEventType, data);
handleException(sequenceId, operation, expression, mr, preparedExpression, new CalculatorParseException(e)); }
} catch (ParseInterruptedException e) {
@Override
// do nothing - we ourselves interrupt the calculations public void fireCalculatorEvents(@NotNull List<CalculatorEvent> calculatorEvents) {
fireCalculatorEvent(newCalculationEventData(operation, expression, sequenceId), CalculatorEventType.calculation_cancelled, null); calculatorEventContainer.fireCalculatorEvents(calculatorEvents);
}
} catch (CalculatorParseException e) { }
handleException(sequenceId, operation, expression, mr, preparedExpression, e);
} finally {
fireCalculatorEvent(newCalculationEventData(operation, expression, sequenceId), CalculatorEventType.calculation_finished, null);
}
}
}
@NotNull
private CalculatorEventData newCalculationEventData(@NotNull JsclOperation operation,
@NotNull String expression,
@NotNull Long calculationId) {
return new CalculatorEvaluationEventDataImpl(CalculatorEventDataImpl.newInstance(nextEventDataId(calculationId)), operation, expression);
}
private void handleException(@NotNull Long calculationId,
@NotNull JsclOperation operation,
@NotNull String expression,
@Nullable MessageRegistry mr,
@Nullable PreparedExpression preparedExpression,
@NotNull CalculatorParseException parseException) {
if (operation == JsclOperation.numeric
&& preparedExpression != null
&& preparedExpression.isExistsUndefinedVar()) {
evaluate(calculationId, JsclOperation.simplify, expression, mr);
}
fireCalculatorEvent(newCalculationEventData(operation, expression, calculationId), CalculatorEventType.calculation_failed, new CalculatorFailureImpl(parseException));
}
private void handleException(@NotNull Long calculationId,
@NotNull JsclOperation operation,
@NotNull String expression,
@Nullable MessageRegistry mr,
@NotNull CalculatorEvalException evalException) {
if (operation == JsclOperation.numeric && evalException.getCause() instanceof NumeralBaseException) {
evaluate(calculationId, JsclOperation.simplify, expression, mr);
}
fireCalculatorEvent(newCalculationEventData(operation, expression, calculationId), CalculatorEventType.calculation_failed, new CalculatorFailureImpl(evalException));
}
/*
**********************************************************************
*
* EVENTS
*
**********************************************************************
*/
@Override
public void addCalculatorEventListener(@NotNull CalculatorEventListener calculatorEventListener) {
calculatorEventContainer.addCalculatorEventListener(calculatorEventListener);
}
@Override
public void removeCalculatorEventListener(@NotNull CalculatorEventListener calculatorEventListener) {
calculatorEventContainer.removeCalculatorEventListener(calculatorEventListener);
}
@Override
public void fireCalculatorEvent(@NotNull CalculatorEventData calculatorEventData, @NotNull CalculatorEventType calculatorEventType, @Nullable Object data) {
calculatorEventContainer.fireCalculatorEvent(calculatorEventData, calculatorEventType, data);
}
@Override
public void fireCalculatorEvents(@NotNull List<CalculatorEvent> calculatorEvents) {
calculatorEventContainer.fireCalculatorEvents(calculatorEvents);
}
public static final class ConversionException extends Exception {
private ConversionException() {
}
private ConversionException(Throwable throwable) {
super(throwable);
}
}
}

View File

@ -1,19 +1,20 @@
# This file is automatically generated by Android Tools. # This file is automatically generated by Android Tools.
# Do not modify this file -- YOUR CHANGES WILL BE ERASED! # Do not modify this file -- YOUR CHANGES WILL BE ERASED!
# #
# This file must be checked in Version Control Systems. # This file must be checked in Version Control Systems.
# #
# To customize properties used by the Ant build system use, # To customize properties used by the Ant build system use,
# "ant.properties", and override values to adapt the script to your # "ant.properties", and override values to adapt the script to your
# project structure. # project structure.
# Project target. # Project target.
target=android-15 target=android-15
android.library.reference.1=gen-external-apklibs/org.solovyev.android_android-common-core_1.0.0 android.library.reference.1=../calculatorpp-service
android.library.reference.2=gen-external-apklibs/org.solovyev.android_android-common-ads_1.0.0 android.library.reference.2=gen-external-apklibs/org.solovyev.android_android-common-core_1.0.0
android.library.reference.3=gen-external-apklibs/org.solovyev.android_android-common-view_1.0.0 android.library.reference.3=gen-external-apklibs/org.solovyev.android_android-common-ads_1.0.0
android.library.reference.4=gen-external-apklibs/org.solovyev.android_android-common-preferences_1.0.0 android.library.reference.4=gen-external-apklibs/org.solovyev.android_android-common-view_1.0.0
android.library.reference.5=gen-external-apklibs/org.solovyev.android_android-common-other_1.0.0 android.library.reference.5=gen-external-apklibs/org.solovyev.android_android-common-preferences_1.0.0
android.library.reference.6=gen-external-apklibs/org.solovyev.android_android-common-menu_1.0.0 android.library.reference.6=gen-external-apklibs/org.solovyev.android_android-common-other_1.0.0
android.library.reference.7=gen-external-apklibs/org.solovyev.android_android-common-menu_1.0.0

View File

@ -1,235 +1,316 @@
/* /*
* Copyright (c) 2009-2011. Created by serso aka se.solovyev. * Copyright (c) 2009-2011. Created by serso aka se.solovyev.
* For more information, please, contact se.solovyev@gmail.com * For more information, please, contact se.solovyev@gmail.com
*/ */
package org.solovyev.android.calculator; package org.solovyev.android.calculator;
import android.content.Context; import android.content.Context;
import android.graphics.Color; import android.graphics.Color;
import android.text.Html; import android.text.Html;
import android.util.AttributeSet; import android.util.AttributeSet;
import android.util.Log; import android.util.Log;
import jscl.math.Generic; import jscl.NumeralBase;
import jscl.math.function.Constant; import jscl.math.Generic;
import jscl.math.function.IConstant; import jscl.math.function.Constant;
import org.jetbrains.annotations.NotNull; import jscl.math.function.IConstant;
import org.solovyev.android.calculator.jscl.JsclOperation; import org.jetbrains.annotations.NotNull;
import org.solovyev.android.calculator.model.CalculatorEngine; import org.solovyev.android.calculator.jscl.JsclOperation;
import org.solovyev.android.calculator.text.TextProcessor; import org.solovyev.android.calculator.model.CalculatorEngine;
import org.solovyev.android.calculator.view.NumeralBaseConverterDialog; import org.solovyev.android.calculator.text.TextProcessor;
import org.solovyev.android.calculator.view.TextHighlighter; import org.solovyev.android.calculator.view.NumeralBaseConverterDialog;
import org.solovyev.android.menu.LabeledMenuItem; import org.solovyev.android.calculator.view.TextHighlighter;
import org.solovyev.android.view.AutoResizeTextView; import org.solovyev.android.calculator.view.UnitConverterViewBuilder;
import org.solovyev.common.collections.CollectionsUtils; import org.solovyev.android.menu.AMenuItem;
import org.solovyev.android.menu.LabeledMenuItem;
import java.util.HashSet; import org.solovyev.android.view.AutoResizeTextView;
import java.util.Set; import org.solovyev.common.collections.CollectionsUtils;
import org.solovyev.common.text.StringUtils;
/**
* User: serso import java.util.HashSet;
* Date: 9/17/11 import java.util.Set;
* Time: 10:58 PM
*/ /**
public class AndroidCalculatorDisplayView extends AutoResizeTextView implements CalculatorDisplayView { * User: serso
* Date: 9/17/11
public static enum MenuItem implements LabeledMenuItem<CalculatorDisplayViewState> { * Time: 10:58 PM
*/
copy(R.string.c_copy) { public class AndroidCalculatorDisplayView extends AutoResizeTextView implements CalculatorDisplayView {
@Override
public void onClick(@NotNull CalculatorDisplayViewState data, @NotNull Context context) { private static enum ConversionMenuItem implements AMenuItem<CalculatorDisplayView> {
CalculatorModel.copyResult(context, data); convert_to_bin(NumeralBase.bin),
} convert_to_dec(NumeralBase.dec),
}, convert_to_hex(NumeralBase.hex);
convert_to_bin(R.string.convert_to_bin) { @NotNull
@Override private final NumeralBase toNumeralBase;
public void onClick(@NotNull CalculatorDisplayViewState data, @NotNull Context context) {
ConversionMenuItem.convert_to_bin.onClick(data, context); private ConversionMenuItem(@NotNull NumeralBase toNumeralBase) {
} this.toNumeralBase = toNumeralBase;
}
@Override
protected boolean isItemVisibleFor(@NotNull Generic generic, @NotNull JsclOperation operation) { protected boolean isItemVisibleFor(@NotNull Generic generic, @NotNull JsclOperation operation) {
return ConversionMenuItem.convert_to_bin.isItemVisibleFor(generic, operation); boolean result = false;
}
}, if (operation == JsclOperation.numeric) {
if (generic.getConstants().isEmpty()) {
convert_to_dec(R.string.convert_to_dec) { try {
@Override convert(generic);
public void onClick(@NotNull CalculatorDisplayViewState data, @NotNull Context context) {
ConversionMenuItem.convert_to_dec.onClick(data, context); // conversion possible => return true
} result = true;
@Override } catch (UnitConverterViewBuilder.ConversionException e) {
protected boolean isItemVisibleFor(@NotNull Generic generic, @NotNull JsclOperation operation) { // conversion is not possible => return false
return ConversionMenuItem.convert_to_dec.isItemVisibleFor(generic, operation); }
} }
}, }
convert_to_hex(R.string.convert_to_hex) { return result;
@Override }
public void onClick(@NotNull CalculatorDisplayViewState data, @NotNull Context context) {
ConversionMenuItem.convert_to_hex.onClick(data, context); @Override
} public void onClick(@NotNull CalculatorDisplayView data, @NotNull Context context) {
final NumeralBase fromNumeralBase = CalculatorEngine.instance.getEngine().getNumeralBase();
@Override
protected boolean isItemVisibleFor(@NotNull Generic generic, @NotNull JsclOperation operation) { final Generic lastResult = CalculatorLocatorImpl.getInstance().getCalculatorDisplay().getViewState().getResult();
return ConversionMenuItem.convert_to_hex.isItemVisibleFor(generic, operation);
} if (lastResult != null) {
}, String to;
try {
convert(R.string.c_convert) { to = convert(lastResult);
@Override
public void onClick(@NotNull CalculatorDisplayViewState data, @NotNull Context context) { // add prefix
final Generic result = data.getResult(); if (fromNumeralBase != toNumeralBase) {
if (result != null) { to = toNumeralBase.getJsclPrefix() + to;
new NumeralBaseConverterDialog(result.toString()).show(context); }
} } catch (UnitConverterViewBuilder.ConversionException e) {
} to = context.getString(R.string.c_error);
}
@Override
protected boolean isItemVisibleFor(@NotNull Generic generic, @NotNull JsclOperation operation) { data.setText(to);
return operation == JsclOperation.numeric && generic.getConstants().isEmpty(); //data.redraw();
} }
}, }
plot(R.string.c_plot) { @NotNull
@Override private String convert(@NotNull Generic generic) throws UnitConverterViewBuilder.ConversionException {
public void onClick(@NotNull CalculatorDisplayViewState data, @NotNull Context context) { final NumeralBase fromNumeralBase = CalculatorEngine.instance.getEngine().getNumeralBase();
final Generic generic = data.getResult();
assert generic != null; if (fromNumeralBase != toNumeralBase) {
String from = generic.toString();
final Constant constant = CollectionsUtils.getFirstCollectionElement(getNotSystemConstants(generic)); if (!StringUtils.isEmpty(from)) {
assert constant != null; try {
CalculatorActivityLauncher.plotGraph(context, generic, constant); from = ToJsclTextProcessor.getInstance().process(from).getExpression();
} } catch (CalculatorParseException e) {
// ok, problems while processing occurred
@Override }
protected boolean isItemVisibleFor(@NotNull Generic generic, @NotNull JsclOperation operation) { }
boolean result = false;
return UnitConverterViewBuilder.doConversion(AndroidNumeralBase.getConverter(), from, AndroidNumeralBase.valueOf(fromNumeralBase), AndroidNumeralBase.valueOf(toNumeralBase));
if (operation == JsclOperation.simplify) { } else {
if (getNotSystemConstants(generic).size() == 1) { return generic.toString();
result = true; }
} }
} }
return result; public static enum MenuItem implements LabeledMenuItem<CalculatorDisplayView> {
}
copy(R.string.c_copy) {
@NotNull @Override
private Set<Constant> getNotSystemConstants(@NotNull Generic generic) { public void onClick(@NotNull CalculatorDisplayView data, @NotNull Context context) {
final Set<Constant> notSystemConstants = new HashSet<Constant>(); CalculatorModel.copyResult(context, data);
}
for (Constant constant : generic.getConstants()) { },
IConstant var = CalculatorEngine.instance.getVarsRegistry().get(constant.getName());
if (var != null && !var.isSystem() && !var.isDefined()) { convert_to_bin(R.string.convert_to_bin) {
notSystemConstants.add(constant); @Override
} public void onClick(@NotNull CalculatorDisplayView data, @NotNull Context context) {
} ConversionMenuItem.convert_to_bin.onClick(data, context);
}
return notSystemConstants;
} @Override
}; protected boolean isItemVisibleFor(@NotNull Generic generic, @NotNull JsclOperation operation) {
return ConversionMenuItem.convert_to_bin.isItemVisibleFor(generic, operation);
private final int captionId; }
},
MenuItem(int captionId) {
this.captionId = captionId; convert_to_dec(R.string.convert_to_dec) {
} @Override
public void onClick(@NotNull CalculatorDisplayView data, @NotNull Context context) {
public final boolean isItemVisible(@NotNull CalculatorDisplayViewState displayViewState) { ConversionMenuItem.convert_to_dec.onClick(data, context);
//noinspection ConstantConditions }
return displayViewState.isValid() && displayViewState.getResult() != null && isItemVisibleFor(displayViewState.getResult(), displayViewState.getOperation());
} @Override
protected boolean isItemVisibleFor(@NotNull Generic generic, @NotNull JsclOperation operation) {
protected boolean isItemVisibleFor(@NotNull Generic generic, @NotNull JsclOperation operation) { return ConversionMenuItem.convert_to_dec.isItemVisibleFor(generic, operation);
return true; }
} },
@NotNull convert_to_hex(R.string.convert_to_hex) {
@Override @Override
public String getCaption(@NotNull Context context) { public void onClick(@NotNull CalculatorDisplayView data, @NotNull Context context) {
return context.getString(captionId); ConversionMenuItem.convert_to_hex.onClick(data, context);
} }
}
@Override
@NotNull protected boolean isItemVisibleFor(@NotNull Generic generic, @NotNull JsclOperation operation) {
private CalculatorDisplayViewState state = CalculatorDisplayViewStateImpl.newDefaultInstance(); return ConversionMenuItem.convert_to_hex.isItemVisibleFor(generic, operation);
}
@NotNull },
private final static TextProcessor<TextHighlighter.Result, String> textHighlighter = new TextHighlighter(Color.WHITE, false, CalculatorEngine.instance.getEngine());
convert(R.string.c_convert) {
public AndroidCalculatorDisplayView(Context context) { @Override
super(context); public void onClick(@NotNull CalculatorDisplayView data, @NotNull Context context) {
} final Generic result = data.getState().getResult();
if (result != null) {
public AndroidCalculatorDisplayView(Context context, AttributeSet attrs) { new NumeralBaseConverterDialog(result.toString()).show(context);
super(context, attrs); }
} }
public AndroidCalculatorDisplayView(Context context, AttributeSet attrs, int defStyle) { @Override
super(context, attrs, defStyle); protected boolean isItemVisibleFor(@NotNull Generic generic, @NotNull JsclOperation operation) {
} return operation == JsclOperation.numeric && generic.getConstants().isEmpty();
}
public boolean isValid() { },
return this.state.isValid();
} plot(R.string.c_plot) {
@Override
public void onClick(@NotNull CalculatorDisplayView data, @NotNull Context context) {
@Override final Generic generic = data.getState().getResult();
public void setState(@NotNull CalculatorDisplayViewState state) { assert generic != null;
this.state = state;
if ( state.isValid() ) { final Constant constant = CollectionsUtils.getFirstCollectionElement(getNotSystemConstants(generic));
setTextColor(getResources().getColor(R.color.default_text_color)); assert constant != null;
setText(state.getStringResult()); CalculatorActivityLauncher.plotGraph(context, generic, constant);
} else { }
setTextColor(getResources().getColor(R.color.display_error_text_color));
setText(state.getErrorMessage()); @Override
} protected boolean isItemVisibleFor(@NotNull Generic generic, @NotNull JsclOperation operation) {
} boolean result = false;
@NotNull if (operation == JsclOperation.simplify) {
@Override if (getNotSystemConstants(generic).size() == 1) {
public CalculatorDisplayViewState getState() { result = true;
return this.state; }
} }
@Override return result;
public void setText(CharSequence text, BufferType type) { }
super.setText(text, type);
} @NotNull
private Set<Constant> getNotSystemConstants(@NotNull Generic generic) {
public synchronized void redraw() { final Set<Constant> notSystemConstants = new HashSet<Constant>();
if (isValid()) {
String text = getText().toString(); for (Constant constant : generic.getConstants()) {
IConstant var = CalculatorEngine.instance.getVarsRegistry().get(constant.getName());
Log.d(this.getClass().getName(), text); if (var != null && !var.isSystem() && !var.isDefined()) {
notSystemConstants.add(constant);
try { }
TextHighlighter.Result result = textHighlighter.process(text); }
text = result.toString();
} catch (CalculatorParseException e) { return notSystemConstants;
Log.e(this.getClass().getName(), e.getMessage(), e); }
} };
Log.d(this.getClass().getName(), text); private final int captionId;
super.setText(Html.fromHtml(text), BufferType.EDITABLE);
} MenuItem(int captionId) {
this.captionId = captionId;
// todo serso: think where to move it (keep in mind org.solovyev.android.view.AutoResizeTextView.resetTextSize()) }
setAddEllipsis(false);
setMinTextSize(10); public final boolean isItemVisible(@NotNull CalculatorDisplayViewState displayViewState) {
resizeText(); //noinspection ConstantConditions
} return displayViewState.isValid() && displayViewState.getResult() != null && isItemVisibleFor(displayViewState.getResult(), displayViewState.getOperation());
}
@Override
public int getSelection() { protected boolean isItemVisibleFor(@NotNull Generic generic, @NotNull JsclOperation operation) {
return this.getSelectionStart(); return true;
} }
@Override @NotNull
public void setSelection(int selection) { @Override
// not supported by TextView public String getCaption(@NotNull Context context) {
} return context.getString(captionId);
} }
}
@NotNull
private CalculatorDisplayViewState state = CalculatorDisplayViewStateImpl.newDefaultInstance();
@NotNull
private final static TextProcessor<TextHighlighter.Result, String> textHighlighter = new TextHighlighter(Color.WHITE, false, CalculatorEngine.instance.getEngine());
public AndroidCalculatorDisplayView(Context context) {
super(context);
}
public AndroidCalculatorDisplayView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public AndroidCalculatorDisplayView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public boolean isValid() {
return this.state.isValid();
}
@Override
public void setState(@NotNull CalculatorDisplayViewState state) {
this.state = state;
if ( state.isValid() ) {
setTextColor(getResources().getColor(R.color.default_text_color));
setText(state.getStringResult());
} else {
setTextColor(getResources().getColor(R.color.display_error_text_color));
setText(state.getErrorMessage());
}
}
@NotNull
@Override
public CalculatorDisplayViewState getState() {
return this.state;
}
@Override
public void setText(CharSequence text, BufferType type) {
super.setText(text, type);
}
public synchronized void redraw() {
if (isValid()) {
String text = getText().toString();
Log.d(this.getClass().getName(), text);
try {
TextHighlighter.Result result = textHighlighter.process(text);
text = result.toString();
} catch (CalculatorParseException e) {
Log.e(this.getClass().getName(), e.getMessage(), e);
}
Log.d(this.getClass().getName(), text);
super.setText(Html.fromHtml(text), BufferType.EDITABLE);
}
// todo serso: think where to move it (keep in mind org.solovyev.android.view.AutoResizeTextView.resetTextSize())
setAddEllipsis(false);
setMinTextSize(10);
resizeText();
}
@Override
public int getSelection() {
return this.getSelectionStart();
}
@Override
public void setSelection(int selection) {
// not supported by TextView
}
}

View File

@ -1,411 +1,412 @@
/* /*
* Copyright (c) 2009-2011. Created by serso aka se.solovyev. * Copyright (c) 2009-2011. Created by serso aka se.solovyev.
* For more information, please, contact se.solovyev@gmail.com * For more information, please, contact se.solovyev@gmail.com
*/ */
package org.solovyev.android.calculator; package org.solovyev.android.calculator;
import android.app.Activity; import android.app.Activity;
import android.app.AlertDialog; import android.app.AlertDialog;
import android.content.Context; import android.content.Context;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.os.Handler; import android.os.Handler;
import android.text.ClipboardManager; import android.text.ClipboardManager;
import android.util.Log; import android.util.Log;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.widget.EditText; import android.widget.EditText;
import android.widget.TextView; import android.widget.TextView;
import android.widget.Toast; import android.widget.Toast;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.solovyev.android.CursorControl; import org.solovyev.android.CursorControl;
import org.solovyev.android.calculator.history.AndroidCalculatorHistoryImpl; import org.solovyev.android.calculator.history.AndroidCalculatorHistoryImpl;
import org.solovyev.android.calculator.history.CalculatorHistoryState; import org.solovyev.android.calculator.history.CalculatorHistoryState;
import org.solovyev.android.calculator.history.TextViewEditorAdapter; import org.solovyev.android.calculator.history.TextViewEditorAdapter;
import org.solovyev.android.calculator.jscl.JsclOperation; import org.solovyev.android.calculator.jscl.JsclOperation;
import org.solovyev.android.calculator.math.MathType; import org.solovyev.android.calculator.math.MathType;
import org.solovyev.android.calculator.model.CalculatorEngine; import org.solovyev.android.calculator.model.CalculatorEngine;
import org.solovyev.android.history.HistoryControl; import org.solovyev.android.history.HistoryControl;
import org.solovyev.android.menu.AMenuBuilder; import org.solovyev.android.menu.AMenuBuilder;
import org.solovyev.android.menu.MenuImpl; import org.solovyev.android.menu.MenuImpl;
import org.solovyev.common.MutableObject; import org.solovyev.common.MutableObject;
import org.solovyev.common.history.HistoryAction; import org.solovyev.common.history.HistoryAction;
import org.solovyev.common.msg.Message; import org.solovyev.common.msg.Message;
import org.solovyev.common.text.StringUtils; import org.solovyev.common.text.StringUtils;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
/** /**
* User: serso * User: serso
* Date: 9/12/11 * Date: 9/12/11
* Time: 11:15 PM * Time: 11:15 PM
*/ */
public enum CalculatorModel implements CursorControl, HistoryControl<CalculatorHistoryState>, CalculatorEngineControl { public enum CalculatorModel implements CursorControl, HistoryControl<CalculatorHistoryState>, CalculatorEngineControl {
instance; instance;
// millis to wait before evaluation after user edit action // millis to wait before evaluation after user edit action
public static final int EVAL_DELAY_MILLIS = 0; public static final int EVAL_DELAY_MILLIS = 0;
@NotNull @NotNull
private CalculatorEditor editor; private CalculatorEditor editor;
@NotNull @NotNull
private AndroidCalculatorDisplayView display; private AndroidCalculatorDisplayView display;
@NotNull @NotNull
private CalculatorEngine calculatorEngine; private CalculatorEngine calculatorEngine;
public CalculatorModel init(@NotNull final Activity activity, @NotNull SharedPreferences preferences, @NotNull CalculatorEngine calculator) { public CalculatorModel init(@NotNull final Activity activity, @NotNull SharedPreferences preferences, @NotNull CalculatorEngine calculator) {
Log.d(this.getClass().getName(), "CalculatorModel initialization with activity: " + activity); Log.d(this.getClass().getName(), "CalculatorModel initialization with activity: " + activity);
this.calculatorEngine = calculator; this.calculatorEngine = calculator;
this.editor = (CalculatorEditor) activity.findViewById(R.id.calculatorEditor); this.editor = (CalculatorEditor) activity.findViewById(R.id.calculatorEditor);
this.editor.init(preferences); this.editor.init(preferences);
preferences.registerOnSharedPreferenceChangeListener(editor); preferences.registerOnSharedPreferenceChangeListener(editor);
this.display = (AndroidCalculatorDisplayView) activity.findViewById(R.id.calculatorDisplay); this.display = (AndroidCalculatorDisplayView) activity.findViewById(R.id.calculatorDisplay);
this.display.setOnClickListener(new CalculatorDisplayOnClickListener(activity)); this.display.setOnClickListener(new CalculatorDisplayOnClickListener(activity));
final CalculatorHistoryState lastState = AndroidCalculatorHistoryImpl.instance.getLastHistoryState(); final CalculatorHistoryState lastState = AndroidCalculatorHistoryImpl.instance.getLastHistoryState();
if (lastState == null) { if (lastState == null) {
saveHistoryState(); saveHistoryState();
} else { } else {
setCurrentHistoryState(lastState); setCurrentHistoryState(lastState);
} }
return this; return this;
} }
private static void showEvaluationError(@NotNull Activity activity, @NotNull final String errorMessage) { private static void showEvaluationError(@NotNull Activity activity, @NotNull final String errorMessage) {
final LayoutInflater layoutInflater = (LayoutInflater) activity.getSystemService(Activity.LAYOUT_INFLATER_SERVICE); final LayoutInflater layoutInflater = (LayoutInflater) activity.getSystemService(Activity.LAYOUT_INFLATER_SERVICE);
final View errorMessageView = layoutInflater.inflate(R.layout.display_error_message, null); final View errorMessageView = layoutInflater.inflate(R.layout.display_error_message, null);
((TextView) errorMessageView.findViewById(R.id.error_message_text_view)).setText(errorMessage); ((TextView) errorMessageView.findViewById(R.id.error_message_text_view)).setText(errorMessage);
final AlertDialog.Builder builder = new AlertDialog.Builder(activity) final AlertDialog.Builder builder = new AlertDialog.Builder(activity)
.setPositiveButton(R.string.c_cancel, null) .setPositiveButton(R.string.c_cancel, null)
.setView(errorMessageView); .setView(errorMessageView);
builder.create().show(); builder.create().show();
} }
public void copyResult(@NotNull Context context) { public void copyResult(@NotNull Context context) {
copyResult(context, display); copyResult(context, display);
} }
public static void copyResult(@NotNull Context context, @NotNull final CalculatorDisplayViewState viewState) { public static void copyResult(@NotNull Context context, @NotNull final CalculatorDisplayView display) {
if (viewState.isValid()) { final CalculatorDisplayViewState displayViewState = display.getState();
final CharSequence text = viewState.getText(); if (displayViewState.isValid()) {
if (!StringUtils.isEmpty(text)) { final CharSequence text = display.getText();
final ClipboardManager clipboard = (ClipboardManager) context.getSystemService(Activity.CLIPBOARD_SERVICE); if (!StringUtils.isEmpty(text)) {
clipboard.setText(text.toString()); final ClipboardManager clipboard = (ClipboardManager) context.getSystemService(Activity.CLIPBOARD_SERVICE);
Toast.makeText(context, context.getText(R.string.c_result_copied), Toast.LENGTH_SHORT).show(); clipboard.setText(text.toString());
} Toast.makeText(context, context.getText(R.string.c_result_copied), Toast.LENGTH_SHORT).show();
} }
} }
}
private void saveHistoryState() {
AndroidCalculatorHistoryImpl.instance.addState(getCurrentHistoryState()); private void saveHistoryState() {
} AndroidCalculatorHistoryImpl.instance.addState(getCurrentHistoryState());
}
public void setCursorOnStart() {
editor.setSelection(0); public void setCursorOnStart() {
} editor.setSelection(0);
}
public void setCursorOnEnd() {
editor.setSelection(editor.getText().length()); public void setCursorOnEnd() {
} editor.setSelection(editor.getText().length());
}
public void moveCursorLeft() {
if (editor.getSelectionStart() > 0) { public void moveCursorLeft() {
editor.setSelection(editor.getSelectionStart() - 1); if (editor.getSelectionStart() > 0) {
} editor.setSelection(editor.getSelectionStart() - 1);
} }
}
public void moveCursorRight() {
if (editor.getSelectionStart() < editor.getText().length()) { public void moveCursorRight() {
editor.setSelection(editor.getSelectionStart() + 1); if (editor.getSelectionStart() < editor.getText().length()) {
} editor.setSelection(editor.getSelectionStart() + 1);
} }
}
public void doTextOperation(@NotNull TextOperation operation) {
doTextOperation(operation, true); public void doTextOperation(@NotNull TextOperation operation) {
} doTextOperation(operation, true);
}
public void doTextOperation(@NotNull TextOperation operation, boolean delayEvaluate) {
doTextOperation(operation, delayEvaluate, JsclOperation.numeric, false); public void doTextOperation(@NotNull TextOperation operation, boolean delayEvaluate) {
} doTextOperation(operation, delayEvaluate, JsclOperation.numeric, false);
}
public void doTextOperation(@NotNull TextOperation operation, boolean delayEvaluate, @NotNull JsclOperation jsclOperation, boolean forceEval) {
final String editorStateBefore = this.editor.getText().toString(); public void doTextOperation(@NotNull TextOperation operation, boolean delayEvaluate, @NotNull JsclOperation jsclOperation, boolean forceEval) {
final String editorStateBefore = this.editor.getText().toString();
Log.d(CalculatorModel.class.getName(), "Editor state changed before '" + editorStateBefore + "'");
operation.doOperation(this.editor); Log.d(CalculatorModel.class.getName(), "Editor state changed before '" + editorStateBefore + "'");
//Log.d(CalculatorModel.class.getName(), "Doing text operation" + StringUtils.fromStackTrace(Thread.currentThread().getStackTrace())); operation.doOperation(this.editor);
//Log.d(CalculatorModel.class.getName(), "Doing text operation" + StringUtils.fromStackTrace(Thread.currentThread().getStackTrace()));
final String editorStateAfter = this.editor.getText().toString();
if (forceEval ||!editorStateBefore.equals(editorStateAfter)) { final String editorStateAfter = this.editor.getText().toString();
if (forceEval ||!editorStateBefore.equals(editorStateAfter)) {
editor.redraw();
editor.redraw();
evaluate(delayEvaluate, editorStateAfter, jsclOperation, null);
} evaluate(delayEvaluate, editorStateAfter, jsclOperation, null);
} }
}
@NotNull
private final static MutableObject<Runnable> pendingOperation = new MutableObject<Runnable>(); @NotNull
private final static MutableObject<Runnable> pendingOperation = new MutableObject<Runnable>();
private void evaluate(boolean delayEvaluate,
@NotNull final String expression, private void evaluate(boolean delayEvaluate,
@NotNull final JsclOperation operation, @NotNull final String expression,
@Nullable CalculatorHistoryState historyState) { @NotNull final JsclOperation operation,
@Nullable CalculatorHistoryState historyState) {
final CalculatorHistoryState localHistoryState;
if (historyState == null) { final CalculatorHistoryState localHistoryState;
//this.display.setText(""); if (historyState == null) {
localHistoryState = getCurrentHistoryState(); //this.display.setText("");
} else { localHistoryState = getCurrentHistoryState();
this.display.setText(historyState.getDisplayState().getEditorState().getText()); } else {
localHistoryState = historyState; this.display.setText(historyState.getDisplayState().getEditorState().getText());
} localHistoryState = historyState;
}
pendingOperation.setObject(new Runnable() {
@Override pendingOperation.setObject(new Runnable() {
public void run() { @Override
// allow only one runner at one time public void run() {
synchronized (pendingOperation) { // allow only one runner at one time
//lock all operations with history synchronized (pendingOperation) {
if (pendingOperation.getObject() == this) { //lock all operations with history
// actually nothing shall be logged while text operations are done if (pendingOperation.getObject() == this) {
evaluate(expression, operation, this); // actually nothing shall be logged while text operations are done
evaluate(expression, operation, this);
if (pendingOperation.getObject() == this) {
// todo serso: of course there is small probability that someone will set pendingOperation after if statement but before .setObject(null) if (pendingOperation.getObject() == this) {
pendingOperation.setObject(null); // todo serso: of course there is small probability that someone will set pendingOperation after if statement but before .setObject(null)
localHistoryState.setDisplayState(getCurrentHistoryState().getDisplayState()); pendingOperation.setObject(null);
} localHistoryState.setDisplayState(getCurrentHistoryState().getDisplayState());
} }
} }
} }
}); }
});
if (delayEvaluate) {
if (historyState == null) { if (delayEvaluate) {
AndroidCalculatorHistoryImpl.instance.addState(localHistoryState); if (historyState == null) {
} AndroidCalculatorHistoryImpl.instance.addState(localHistoryState);
// todo serso: this is not correct - operation is processing still in the same thread }
new Handler().postDelayed(pendingOperation.getObject(), EVAL_DELAY_MILLIS); // todo serso: this is not correct - operation is processing still in the same thread
} else { new Handler().postDelayed(pendingOperation.getObject(), EVAL_DELAY_MILLIS);
pendingOperation.getObject().run(); } else {
if (historyState == null) { pendingOperation.getObject().run();
AndroidCalculatorHistoryImpl.instance.addState(localHistoryState); if (historyState == null) {
} AndroidCalculatorHistoryImpl.instance.addState(localHistoryState);
} }
} }
}
@Override
public void evaluate() { @Override
evaluate(false, this.editor.getText().toString(), JsclOperation.numeric, null); public void evaluate() {
} evaluate(false, this.editor.getText().toString(), JsclOperation.numeric, null);
}
public void evaluate(@NotNull JsclOperation operation) {
evaluate(false, this.editor.getText().toString(), operation, null); public void evaluate(@NotNull JsclOperation operation) {
} evaluate(false, this.editor.getText().toString(), operation, null);
}
@Override
public void simplify() { @Override
evaluate(false, this.editor.getText().toString(), JsclOperation.simplify, null); public void simplify() {
} evaluate(false, this.editor.getText().toString(), JsclOperation.simplify, null);
}
private void evaluate(@Nullable final String expression,
@NotNull JsclOperation operation, private void evaluate(@Nullable final String expression,
@NotNull Runnable currentRunner) { @NotNull JsclOperation operation,
@NotNull Runnable currentRunner) {
if (!StringUtils.isEmpty(expression)) {
try { if (!StringUtils.isEmpty(expression)) {
Log.d(CalculatorModel.class.getName(), "Trying to evaluate '" + operation + "': " + expression /*+ StringUtils.fromStackTrace(Thread.currentThread().getStackTrace())*/); try {
final CalculatorOutput result = calculatorEngine.evaluate(operation, expression); Log.d(CalculatorModel.class.getName(), "Trying to evaluate '" + operation + "': " + expression /*+ StringUtils.fromStackTrace(Thread.currentThread().getStackTrace())*/);
final CalculatorOutput result = calculatorEngine.evaluate(operation, expression);
// todo serso: second condition might replaced with expression.equals(this.editor.getText().toString()) ONLY if expression will be formatted with text highlighter
if (currentRunner == pendingOperation.getObject() && this.editor.getText().length() > 0) { // todo serso: second condition might replaced with expression.equals(this.editor.getText().toString()) ONLY if expression will be formatted with text highlighter
display.setText(result.getStringResult()); if (currentRunner == pendingOperation.getObject() && this.editor.getText().length() > 0) {
} else { display.setText(result.getStringResult());
display.setText(""); } else {
} display.setText("");
display.setJsclOperation(result.getOperation()); }
display.setGenericResult(result.getResult()); display.setJsclOperation(result.getOperation());
} catch (CalculatorParseException e) { display.setGenericResult(result.getResult());
handleEvaluationException(expression, display, operation, e); } catch (CalculatorParseException e) {
} catch (CalculatorEvalException e) { handleEvaluationException(expression, display, operation, e);
handleEvaluationException(expression, display, operation, e); } catch (CalculatorEvalException e) {
} handleEvaluationException(expression, display, operation, e);
} else { }
this.display.setText(""); } else {
this.display.setJsclOperation(operation); this.display.setText("");
this.display.setGenericResult(null); this.display.setJsclOperation(operation);
} this.display.setGenericResult(null);
}
this.display.redraw();
} this.display.redraw();
}
private void handleEvaluationException(@NotNull String expression,
@NotNull AndroidCalculatorDisplayView localDisplay, private void handleEvaluationException(@NotNull String expression,
@NotNull JsclOperation operation, @NotNull AndroidCalculatorDisplayView localDisplay,
@NotNull Message e) { @NotNull JsclOperation operation,
Log.d(CalculatorModel.class.getName(), "Evaluation failed for : " + expression + ". Error message: " + e); @NotNull Message e) {
if ( StringUtils.isEmpty(localDisplay.getText()) ) { Log.d(CalculatorModel.class.getName(), "Evaluation failed for : " + expression + ". Error message: " + e);
// if previous display state was empty -> show error if ( StringUtils.isEmpty(localDisplay.getText()) ) {
localDisplay.setText(R.string.c_syntax_error); // if previous display state was empty -> show error
} else { localDisplay.setText(R.string.c_syntax_error);
// show previous result instead of error caption (actually previous result will be greyed) } else {
} // show previous result instead of error caption (actually previous result will be greyed)
localDisplay.setJsclOperation(operation); }
localDisplay.setGenericResult(null); localDisplay.setJsclOperation(operation);
localDisplay.setValid(false); localDisplay.setGenericResult(null);
localDisplay.setErrorMessage(e.getLocalizedMessage()); localDisplay.setValid(false);
} localDisplay.setErrorMessage(e.getLocalizedMessage());
}
public void clear() {
if (!StringUtils.isEmpty(editor.getText()) || !StringUtils.isEmpty(display.getText())) { public void clear() {
editor.getText().clear(); if (!StringUtils.isEmpty(editor.getText()) || !StringUtils.isEmpty(display.getText())) {
display.setText(""); editor.getText().clear();
saveHistoryState(); display.setText("");
} saveHistoryState();
} }
}
public void processDigitButtonAction(@Nullable final String text) {
processDigitButtonAction(text, true); public void processDigitButtonAction(@Nullable final String text) {
} processDigitButtonAction(text, true);
}
public void processDigitButtonAction(@Nullable final String text, boolean delayEvaluate) {
public void processDigitButtonAction(@Nullable final String text, boolean delayEvaluate) {
if (!StringUtils.isEmpty(text)) {
doTextOperation(new CalculatorModel.TextOperation() { if (!StringUtils.isEmpty(text)) {
doTextOperation(new CalculatorModel.TextOperation() {
@Override
public void doOperation(@NotNull EditText editor) { @Override
int cursorPositionOffset = 0; public void doOperation(@NotNull EditText editor) {
final StringBuilder textToBeInserted = new StringBuilder(text); int cursorPositionOffset = 0;
final StringBuilder textToBeInserted = new StringBuilder(text);
final MathType.Result mathType = MathType.getType(text, 0, false);
switch (mathType.getMathType()) { final MathType.Result mathType = MathType.getType(text, 0, false);
case function: switch (mathType.getMathType()) {
textToBeInserted.append("()"); case function:
cursorPositionOffset = -1; textToBeInserted.append("()");
break; cursorPositionOffset = -1;
case operator: break;
textToBeInserted.append("()"); case operator:
cursorPositionOffset = -1; textToBeInserted.append("()");
break; cursorPositionOffset = -1;
case comma: break;
textToBeInserted.append(" "); case comma:
break; textToBeInserted.append(" ");
} break;
}
if (cursorPositionOffset == 0) {
if (MathType.openGroupSymbols.contains(text)) { if (cursorPositionOffset == 0) {
cursorPositionOffset = -1; if (MathType.openGroupSymbols.contains(text)) {
} cursorPositionOffset = -1;
} }
}
editor.getText().insert(editor.getSelectionStart(), textToBeInserted.toString());
editor.setSelection(editor.getSelectionStart() + cursorPositionOffset, editor.getSelectionEnd() + cursorPositionOffset); editor.getText().insert(editor.getSelectionStart(), textToBeInserted.toString());
} editor.setSelection(editor.getSelectionStart() + cursorPositionOffset, editor.getSelectionEnd() + cursorPositionOffset);
}, delayEvaluate); }
} }, delayEvaluate);
} }
}
public static interface TextOperation {
public static interface TextOperation {
void doOperation(@NotNull EditText editor);
void doOperation(@NotNull EditText editor);
}
}
@Override
public void doHistoryAction(@NotNull HistoryAction historyAction) { @Override
synchronized (AndroidCalculatorHistoryImpl.instance) { public void doHistoryAction(@NotNull HistoryAction historyAction) {
if (AndroidCalculatorHistoryImpl.instance.isActionAvailable(historyAction)) { synchronized (AndroidCalculatorHistoryImpl.instance) {
final CalculatorHistoryState newState = AndroidCalculatorHistoryImpl.instance.doAction(historyAction, getCurrentHistoryState()); if (AndroidCalculatorHistoryImpl.instance.isActionAvailable(historyAction)) {
if (newState != null) { final CalculatorHistoryState newState = AndroidCalculatorHistoryImpl.instance.doAction(historyAction, getCurrentHistoryState());
setCurrentHistoryState(newState); if (newState != null) {
} setCurrentHistoryState(newState);
} }
} }
} }
}
@Override
public void setCurrentHistoryState(@NotNull CalculatorHistoryState editorHistoryState) { @Override
synchronized (AndroidCalculatorHistoryImpl.instance) { public void setCurrentHistoryState(@NotNull CalculatorHistoryState editorHistoryState) {
Log.d(this.getClass().getName(), "Saved history found: " + editorHistoryState); synchronized (AndroidCalculatorHistoryImpl.instance) {
Log.d(this.getClass().getName(), "Saved history found: " + editorHistoryState);
editorHistoryState.setValuesFromHistory(new TextViewEditorAdapter(this.editor), this.display);
editorHistoryState.setValuesFromHistory(new TextViewEditorAdapter(this.editor), this.display);
final String expression = this.editor.getText().toString();
if ( !StringUtils.isEmpty(expression) ) { final String expression = this.editor.getText().toString();
if ( StringUtils.isEmpty(this.display.getText().toString()) ) { if ( !StringUtils.isEmpty(expression) ) {
evaluate(false, expression, this.display.getJsclOperation(), editorHistoryState); if ( StringUtils.isEmpty(this.display.getText().toString()) ) {
} evaluate(false, expression, this.display.getJsclOperation(), editorHistoryState);
} }
}
editor.redraw();
display.redraw(); editor.redraw();
} display.redraw();
} }
}
@Override
@NotNull @Override
public CalculatorHistoryState getCurrentHistoryState() { @NotNull
synchronized (AndroidCalculatorHistoryImpl.instance) { public CalculatorHistoryState getCurrentHistoryState() {
return CalculatorHistoryState.newInstance(new TextViewEditorAdapter(this.editor), this.display); synchronized (AndroidCalculatorHistoryImpl.instance) {
} return CalculatorHistoryState.newInstance(new TextViewEditorAdapter(this.editor), this.display);
} }
}
@NotNull
public AndroidCalculatorDisplayView getDisplay() { @NotNull
return display; public AndroidCalculatorDisplayView getDisplay() {
} return display;
}
private static class CalculatorDisplayOnClickListener implements View.OnClickListener {
private static class CalculatorDisplayOnClickListener implements View.OnClickListener {
@NotNull
private final Activity activity; @NotNull
private final Activity activity;
public CalculatorDisplayOnClickListener(@NotNull Activity activity) {
this.activity = activity; public CalculatorDisplayOnClickListener(@NotNull Activity activity) {
} this.activity = activity;
}
@Override
public void onClick(View v) { @Override
if (v instanceof CalculatorDisplayView) { public void onClick(View v) {
final CalculatorDisplay cd = CalculatorLocatorImpl.getInstance().getCalculatorDisplay(); if (v instanceof CalculatorDisplayView) {
final CalculatorDisplay cd = CalculatorLocatorImpl.getInstance().getCalculatorDisplay();
final CalculatorDisplayViewState displayViewState = cd.getViewState();
final CalculatorDisplayViewState displayViewState = cd.getViewState();
if (displayViewState.isValid()) {
final List<AndroidCalculatorDisplayView.MenuItem> filteredMenuItems = new ArrayList<AndroidCalculatorDisplayView.MenuItem>(AndroidCalculatorDisplayView.MenuItem.values().length); if (displayViewState.isValid()) {
for (AndroidCalculatorDisplayView.MenuItem menuItem : AndroidCalculatorDisplayView.MenuItem.values()) { final List<AndroidCalculatorDisplayView.MenuItem> filteredMenuItems = new ArrayList<AndroidCalculatorDisplayView.MenuItem>(AndroidCalculatorDisplayView.MenuItem.values().length);
if (menuItem.isItemVisible(displayViewState)) { for (AndroidCalculatorDisplayView.MenuItem menuItem : AndroidCalculatorDisplayView.MenuItem.values()) {
filteredMenuItems.add(menuItem); if (menuItem.isItemVisible(displayViewState)) {
} filteredMenuItems.add(menuItem);
} }
}
if (!filteredMenuItems.isEmpty()) {
AMenuBuilder.newInstance(activity, MenuImpl.newInstance(filteredMenuItems)).create(cd).show(); if (!filteredMenuItems.isEmpty()) {
} AMenuBuilder.newInstance(activity, MenuImpl.newInstance(filteredMenuItems)).create(cd).show();
}
} else {
final String errorMessage = displayViewState.getErrorMessage(); } else {
if (errorMessage != null) { final String errorMessage = displayViewState.getErrorMessage();
showEvaluationError(activity, errorMessage); if (errorMessage != null) {
} showEvaluationError(activity, errorMessage);
} }
} }
} }
} }
} }
}

View File

@ -1,63 +0,0 @@
package org.solovyev.android.calculator;
import android.content.Context;
import jscl.NumeralBase;
import jscl.math.Generic;
import org.jetbrains.annotations.NotNull;
import org.solovyev.android.calculator.jscl.JsclOperation;
import org.solovyev.android.calculator.model.CalculatorEngine;
import org.solovyev.android.menu.AMenuItem;
/**
* User: serso
* Date: 9/21/12
* Time: 12:11 AM
*/
enum ConversionMenuItem implements AMenuItem<CalculatorDisplayViewState> {
convert_to_bin(NumeralBase.bin),
convert_to_dec(NumeralBase.dec),
convert_to_hex(NumeralBase.hex);
@NotNull
private final NumeralBase toNumeralBase;
ConversionMenuItem(@NotNull NumeralBase toNumeralBase) {
this.toNumeralBase = toNumeralBase;
}
protected boolean isItemVisibleFor(@NotNull Generic generic, @NotNull JsclOperation operation) {
boolean result = false;
if (operation == JsclOperation.numeric) {
if (generic.getConstants().isEmpty()) {
try {
convert(generic);
// conversion possible => return true
result = true;
} catch (CalculatorImpl.ConversionException e) {
// conversion is not possible => return false
}
}
}
return result;
}
@Override
public void onClick(@NotNull CalculatorDisplayViewState data, @NotNull Context context) {
final NumeralBase fromNumeralBase = CalculatorEngine.instance.getEngine().getNumeralBase();
final Generic lastResult = data.getResult();
if (lastResult != null) {
convert(lastResult);
}
}
private void convert(@NotNull Generic generic) {
CalculatorLocatorImpl.getInstance().getCalculator().convert(generic, this.toNumeralBase);
}
}

View File

@ -1,224 +1,259 @@
package org.solovyev.android.calculator.view; package org.solovyev.android.calculator.view;
import android.app.Activity; import android.app.Activity;
import android.content.Context; import android.content.Context;
import android.text.ClipboardManager; import android.text.ClipboardManager;
import android.text.Editable; import android.text.Editable;
import android.text.TextWatcher; import android.text.TextWatcher;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.*; import android.widget.*;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.solovyev.android.calculator.CalculatorImpl; import org.solovyev.math.units.Unit;
import org.solovyev.math.units.Unit; import org.solovyev.math.units.UnitConverter;
import org.solovyev.math.units.UnitConverter; import org.solovyev.math.units.UnitImpl;
import org.solovyev.math.units.UnitImpl; import org.solovyev.math.units.UnitType;
import org.solovyev.math.units.UnitType; import org.solovyev.android.calculator.R;
import org.solovyev.android.calculator.R; import org.solovyev.android.view.ViewBuilder;
import org.solovyev.android.view.ViewBuilder; import org.solovyev.android.view.ViewFromLayoutBuilder;
import org.solovyev.android.view.ViewFromLayoutBuilder; import org.solovyev.common.text.StringUtils;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
/** /**
* User: serso * User: serso
* Date: 4/20/12 * Date: 4/20/12
* Time: 4:50 PM * Time: 4:50 PM
*/ */
public class UnitConverterViewBuilder implements ViewBuilder<View> { public class UnitConverterViewBuilder implements ViewBuilder<View> {
@NotNull @NotNull
private List<? extends UnitType<String>> fromUnitTypes = Collections.emptyList(); private List<? extends UnitType<String>> fromUnitTypes = Collections.emptyList();
@NotNull @NotNull
private List<? extends UnitType<String>> toUnitTypes = Collections.emptyList(); private List<? extends UnitType<String>> toUnitTypes = Collections.emptyList();
@Nullable @Nullable
private Unit<String> fromValue; private Unit<String> fromValue;
@NotNull @NotNull
private UnitConverter<String> converter = UnitConverter.Dummy.getInstance(); private UnitConverter<String> converter = UnitConverter.Dummy.getInstance();
@Nullable @Nullable
private View.OnClickListener okButtonOnClickListener; private View.OnClickListener okButtonOnClickListener;
@Nullable @Nullable
private CustomButtonData customButtonData; private CustomButtonData customButtonData;
public void setFromUnitTypes(@NotNull List<? extends UnitType<String>> fromUnitTypes) { public void setFromUnitTypes(@NotNull List<? extends UnitType<String>> fromUnitTypes) {
this.fromUnitTypes = fromUnitTypes; this.fromUnitTypes = fromUnitTypes;
} }
public void setToUnitTypes(@NotNull List<? extends UnitType<String>> toUnitTypes) { public void setToUnitTypes(@NotNull List<? extends UnitType<String>> toUnitTypes) {
this.toUnitTypes = toUnitTypes; this.toUnitTypes = toUnitTypes;
} }
public void setFromValue(@Nullable Unit<String> fromValue) { public void setFromValue(@Nullable Unit<String> fromValue) {
this.fromValue = fromValue; this.fromValue = fromValue;
} }
public void setConverter(@NotNull UnitConverter<String> converter) { public void setConverter(@NotNull UnitConverter<String> converter) {
this.converter = converter; this.converter = converter;
} }
public void setOkButtonOnClickListener(@Nullable View.OnClickListener okButtonOnClickListener) { public void setOkButtonOnClickListener(@Nullable View.OnClickListener okButtonOnClickListener) {
this.okButtonOnClickListener = okButtonOnClickListener; this.okButtonOnClickListener = okButtonOnClickListener;
} }
public void setCustomButtonData(@Nullable CustomButtonData customButtonData) { public void setCustomButtonData(@Nullable CustomButtonData customButtonData) {
this.customButtonData = customButtonData; this.customButtonData = customButtonData;
} }
@NotNull @NotNull
@Override @Override
public View build(@NotNull final Context context) { public View build(@NotNull final Context context) {
final View main = ViewFromLayoutBuilder.newInstance(R.layout.unit_converter).build(context); final View main = ViewFromLayoutBuilder.newInstance(R.layout.unit_converter).build(context);
final Spinner fromSpinner = (Spinner) main.findViewById(R.id.unit_types_from); final Spinner fromSpinner = (Spinner) main.findViewById(R.id.unit_types_from);
final EditText fromEditText = (EditText) main.findViewById(R.id.units_from); final EditText fromEditText = (EditText) main.findViewById(R.id.units_from);
fromEditText.addTextChangedListener(new TextWatcher() { fromEditText.addTextChangedListener(new TextWatcher() {
@Override @Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) { public void beforeTextChanged(CharSequence s, int start, int count, int after) {
} }
@Override @Override
public void onTextChanged(CharSequence s, int start, int before, int count) { public void onTextChanged(CharSequence s, int start, int before, int count) {
} }
@Override @Override
public void afterTextChanged(Editable s) { public void afterTextChanged(Editable s) {
doConversion(main, context, UnitConverterViewBuilder.this.converter); doConversion(main, context, UnitConverterViewBuilder.this.converter);
} }
}); });
fillSpinner(main, context, R.id.unit_types_from, fromUnitTypes); fillSpinner(main, context, R.id.unit_types_from, fromUnitTypes);
fillSpinner(main, context, R.id.unit_types_to, toUnitTypes); fillSpinner(main, context, R.id.unit_types_to, toUnitTypes);
if (fromValue != null) { if (fromValue != null) {
fromEditText.setText(fromValue.getValue()); fromEditText.setText(fromValue.getValue());
int i = fromUnitTypes.indexOf(fromValue.getUnitType()); int i = fromUnitTypes.indexOf(fromValue.getUnitType());
if ( i >= 0 ) { if ( i >= 0 ) {
fromSpinner.setSelection(i); fromSpinner.setSelection(i);
} }
} }
final Button copyButton = (Button) main.findViewById(R.id.unit_converter_copy_button); final Button copyButton = (Button) main.findViewById(R.id.unit_converter_copy_button);
copyButton.setOnClickListener(new View.OnClickListener() { copyButton.setOnClickListener(new View.OnClickListener() {
@Override @Override
public void onClick(View v) { public void onClick(View v) {
final EditText toEditText = (EditText) main.findViewById(R.id.units_to); final EditText toEditText = (EditText) main.findViewById(R.id.units_to);
final ClipboardManager clipboard = (ClipboardManager) context.getSystemService(Activity.CLIPBOARD_SERVICE); final ClipboardManager clipboard = (ClipboardManager) context.getSystemService(Activity.CLIPBOARD_SERVICE);
clipboard.setText(toEditText.getText().toString()); clipboard.setText(toEditText.getText().toString());
Toast.makeText(context, context.getText(R.string.c_result_copied), Toast.LENGTH_SHORT).show(); Toast.makeText(context, context.getText(R.string.c_result_copied), Toast.LENGTH_SHORT).show();
} }
}); });
final Button okButton = (Button) main.findViewById(R.id.unit_converter_ok_button); final Button okButton = (Button) main.findViewById(R.id.unit_converter_ok_button);
if ( okButtonOnClickListener == null ) { if ( okButtonOnClickListener == null ) {
((ViewGroup) okButton.getParent()).removeView(okButton); ((ViewGroup) okButton.getParent()).removeView(okButton);
} else { } else {
okButton.setOnClickListener(this.okButtonOnClickListener); okButton.setOnClickListener(this.okButtonOnClickListener);
} }
final Button customButton = (Button) main.findViewById(R.id.unit_converter_custom_button); final Button customButton = (Button) main.findViewById(R.id.unit_converter_custom_button);
if ( customButtonData == null ) { if ( customButtonData == null ) {
((ViewGroup) customButton.getParent()).removeView(customButton); ((ViewGroup) customButton.getParent()).removeView(customButton);
} else { } else {
customButton.setText(customButtonData.text); customButton.setText(customButtonData.text);
customButton.setOnClickListener(new View.OnClickListener() { customButton.setOnClickListener(new View.OnClickListener() {
@Override @Override
public void onClick(View v) { public void onClick(View v) {
customButtonData.clickListener.onClick(getFromUnit(main), getToUnit(main)); customButtonData.clickListener.onClick(getFromUnit(main), getToUnit(main));
} }
}); });
} }
return main; return main;
} }
private void fillSpinner(@NotNull final View main, private void fillSpinner(@NotNull final View main,
@NotNull final Context context, @NotNull final Context context,
final int spinnerId, final int spinnerId,
@NotNull List<? extends UnitType<String>> unitTypes) { @NotNull List<? extends UnitType<String>> unitTypes) {
final Spinner spinner = (Spinner) main.findViewById(spinnerId); final Spinner spinner = (Spinner) main.findViewById(spinnerId);
final ArrayAdapter<UnitType<String>> adapter = new ArrayAdapter<UnitType<String>>(context, android.R.layout.simple_spinner_item); final ArrayAdapter<UnitType<String>> adapter = new ArrayAdapter<UnitType<String>>(context, android.R.layout.simple_spinner_item);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
for (UnitType<String> fromUnitType : unitTypes) { for (UnitType<String> fromUnitType : unitTypes) {
adapter.add(fromUnitType); adapter.add(fromUnitType);
} }
spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override @Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
doConversion(main, context, UnitConverterViewBuilder.this.converter); doConversion(main, context, UnitConverterViewBuilder.this.converter);
} }
@Override @Override
public void onNothingSelected(AdapterView<?> parent) { public void onNothingSelected(AdapterView<?> parent) {
} }
}); });
spinner.setAdapter(adapter); spinner.setAdapter(adapter);
} }
private static void doConversion(@NotNull View main, @NotNull Context context, @NotNull UnitConverter<String> converter) { private static void doConversion(@NotNull View main, @NotNull Context context, @NotNull UnitConverter<String> converter) {
final EditText fromEditText = (EditText) main.findViewById(R.id.units_from); final EditText fromEditText = (EditText) main.findViewById(R.id.units_from);
final EditText toEditText = (EditText) main.findViewById(R.id.units_to); final EditText toEditText = (EditText) main.findViewById(R.id.units_to);
final String from = fromEditText.getText().toString(); final String from = fromEditText.getText().toString();
try { try {
toEditText.setText(CalculatorImpl.doConversion(converter, from, getFromUnitType(main), getToUnitType(main))); toEditText.setText(doConversion(converter, from, getFromUnitType(main), getToUnitType(main)));
} catch (CalculatorImpl.ConversionException e) { } catch (ConversionException e) {
toEditText.setText(context.getString(R.string.c_error)); toEditText.setText(context.getString(R.string.c_error));
} }
} }
@NotNull public static final class ConversionException extends Exception {
private static Unit<String> getToUnit(@NotNull View main) { private ConversionException() {
final EditText toUnits = (EditText) main.findViewById(R.id.units_to); }
return UnitImpl.newInstance(toUnits.getText().toString(), getToUnitType(main));
} private ConversionException(Throwable throwable) {
super(throwable);
@NotNull }
private static UnitType<String> getToUnitType(@NotNull View main) { }
final Spinner toSpinner = (Spinner) main.findViewById(R.id.unit_types_to);
return (UnitType<String>) toSpinner.getSelectedItem(); @NotNull
} public static String doConversion(@NotNull UnitConverter<String> converter,
@Nullable String from,
@NotNull @NotNull UnitType<String> fromUnitType,
private static Unit<String> getFromUnit(@NotNull View main) { @NotNull UnitType<String> toUnitType) throws ConversionException{
final EditText fromUnits = (EditText) main.findViewById(R.id.units_from); final String result;
return UnitImpl.newInstance(fromUnits.getText().toString(), getFromUnitType(main));
} if (StringUtils.isEmpty(from)) {
result = "";
@NotNull } else {
private static UnitType<String> getFromUnitType(@NotNull View main) {
final Spinner fromSpinner = (Spinner) main.findViewById(R.id.unit_types_from); String to = null;
return (UnitType<String>) fromSpinner.getSelectedItem(); try {
} if (converter.isSupported(fromUnitType, toUnitType)) {
to = converter.convert(UnitImpl.newInstance(from, fromUnitType), toUnitType).getValue();
public static class CustomButtonData { }
} catch (RuntimeException e) {
@NotNull throw new ConversionException(e);
private String text; }
@NotNull result = to;
private CustomButtonOnClickListener clickListener; }
return result;
public CustomButtonData(@NotNull String text, @NotNull CustomButtonOnClickListener clickListener) { }
this.text = text;
this.clickListener = clickListener; @NotNull
} private static Unit<String> getToUnit(@NotNull View main) {
} final EditText toUnits = (EditText) main.findViewById(R.id.units_to);
return UnitImpl.newInstance(toUnits.getText().toString(), getToUnitType(main));
public static interface CustomButtonOnClickListener { }
void onClick(@NotNull Unit<String> fromUnits, @NotNull Unit<String> toUnits);
} @NotNull
} private static UnitType<String> getToUnitType(@NotNull View main) {
final Spinner toSpinner = (Spinner) main.findViewById(R.id.unit_types_to);
return (UnitType<String>) toSpinner.getSelectedItem();
}
@NotNull
private static Unit<String> getFromUnit(@NotNull View main) {
final EditText fromUnits = (EditText) main.findViewById(R.id.units_from);
return UnitImpl.newInstance(fromUnits.getText().toString(), getFromUnitType(main));
}
@NotNull
private static UnitType<String> getFromUnitType(@NotNull View main) {
final Spinner fromSpinner = (Spinner) main.findViewById(R.id.unit_types_from);
return (UnitType<String>) fromSpinner.getSelectedItem();
}
public static class CustomButtonData {
@NotNull
private String text;
@NotNull
private CustomButtonOnClickListener clickListener;
public CustomButtonData(@NotNull String text, @NotNull CustomButtonOnClickListener clickListener) {
this.text = text;
this.clickListener = clickListener;
}
}
public static interface CustomButtonOnClickListener {
void onClick(@NotNull Unit<String> fromUnits, @NotNull Unit<String> toUnits);
}
}