New core implementation
This commit is contained in:
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2011. Created by serso aka se.solovyev.
|
||||
* For more information, please, contact se.solovyev@gmail.com
|
||||
* or visit http://se.solovyev.org
|
||||
*/
|
||||
|
||||
package org.solovyev.android.calculator;
|
||||
|
||||
import jscl.MathEngine;
|
||||
import jscl.NumeralBase;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.solovyev.android.calculator.math.MathType;
|
||||
import org.solovyev.common.text.StringUtils;
|
||||
|
||||
/**
|
||||
* User: serso
|
||||
* Date: 12/15/11
|
||||
* Time: 9:01 PM
|
||||
*/
|
||||
public abstract class AbstractNumberBuilder {
|
||||
|
||||
@NotNull
|
||||
protected final MathEngine engine;
|
||||
|
||||
@Nullable
|
||||
protected StringBuilder numberBuilder = null;
|
||||
|
||||
@Nullable
|
||||
protected NumeralBase nb;
|
||||
|
||||
protected AbstractNumberBuilder(@NotNull MathEngine engine) {
|
||||
this.engine = engine;
|
||||
this.nb = engine.getNumeralBase();
|
||||
}
|
||||
|
||||
/**
|
||||
* Method determines if we can continue to process current number
|
||||
*
|
||||
* @param mathTypeResult current math type result
|
||||
* @return true if we can continue of processing of current number, if false - new number should be constructed
|
||||
*/
|
||||
protected boolean canContinue(@NotNull MathType.Result mathTypeResult) {
|
||||
boolean result = mathTypeResult.getMathType().getGroupType() == MathType.MathGroupType.number &&
|
||||
!spaceBefore(mathTypeResult) &&
|
||||
numeralBaseCheck(mathTypeResult) &&
|
||||
numeralBaseInTheStart(mathTypeResult.getMathType()) || isSignAfterE(mathTypeResult);
|
||||
return result;
|
||||
}
|
||||
|
||||
private boolean spaceBefore(@NotNull MathType.Result mathTypeResult) {
|
||||
return numberBuilder == null && StringUtils.isEmpty(mathTypeResult.getMatch().trim());
|
||||
}
|
||||
|
||||
private boolean numeralBaseInTheStart(@NotNull MathType mathType) {
|
||||
return mathType != MathType.numeral_base || numberBuilder == null;
|
||||
}
|
||||
|
||||
private boolean numeralBaseCheck(@NotNull MathType.Result mathType) {
|
||||
return mathType.getMathType() != MathType.digit || getNumeralBase().getAcceptableCharacters().contains(mathType.getMatch().charAt(0));
|
||||
}
|
||||
|
||||
private boolean isSignAfterE(@NotNull MathType.Result mathTypeResult) {
|
||||
if (!isHexMode()) {
|
||||
if ("-".equals(mathTypeResult.getMatch()) || "+".equals(mathTypeResult.getMatch())) {
|
||||
final StringBuilder localNb = numberBuilder;
|
||||
if (localNb != null && localNb.length() > 0) {
|
||||
if (localNb.charAt(localNb.length() - 1) == MathType.POWER_10) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean isHexMode() {
|
||||
return nb == NumeralBase.hex || (nb == null && engine.getNumeralBase() == NumeralBase.hex);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
protected NumeralBase getNumeralBase() {
|
||||
return nb == null ? engine.getNumeralBase() : nb;
|
||||
}
|
||||
}
|
@@ -0,0 +1,9 @@
|
||||
package org.solovyev.android.calculator;
|
||||
|
||||
/**
|
||||
* User: Solovyev_S
|
||||
* Date: 20.09.12
|
||||
* Time: 16:38
|
||||
*/
|
||||
public interface Calculator extends CalculatorEventContainer {
|
||||
}
|
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2011. Created by serso aka se.solovyev.
|
||||
* For more information, please, contact se.solovyev@gmail.com
|
||||
* or visit http://se.solovyev.org
|
||||
*/
|
||||
|
||||
package org.solovyev.android.calculator;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.solovyev.common.exceptions.SersoException;
|
||||
import org.solovyev.common.msg.Message;
|
||||
import org.solovyev.common.msg.MessageType;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* User: serso
|
||||
* Date: 12/8/11
|
||||
* Time: 1:27 AM
|
||||
*/
|
||||
public class CalculatorEvalException extends SersoException implements Message {
|
||||
|
||||
@NotNull
|
||||
private final Message message;
|
||||
|
||||
@NotNull
|
||||
private final String expression;
|
||||
|
||||
public CalculatorEvalException(@NotNull Message message, @NotNull Throwable cause, String expression) {
|
||||
super(cause);
|
||||
this.message = message;
|
||||
this.expression = expression;
|
||||
}
|
||||
|
||||
|
||||
@NotNull
|
||||
public String getExpression() {
|
||||
return expression;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String getMessageCode() {
|
||||
return this.message.getMessageCode();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public List<Object> getParameters() {
|
||||
return this.message.getParameters();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public MessageType getMessageType() {
|
||||
return this.message.getMessageType();
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public String getLocalizedMessage() {
|
||||
return this.message.getLocalizedMessage(Locale.getDefault());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String getLocalizedMessage(@NotNull Locale locale) {
|
||||
return this.message.getLocalizedMessage(locale);
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,57 @@
|
||||
package org.solovyev.android.calculator;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* User: Solovyev_S
|
||||
* Date: 20.09.12
|
||||
* Time: 16:39
|
||||
*/
|
||||
public interface CalculatorEventContainer {
|
||||
|
||||
void addCalculatorEventListener(@NotNull CalculatorEventListener calculatorEventListener);
|
||||
|
||||
void removeCalculatorEventListener(@NotNull CalculatorEventListener calculatorEventListener);
|
||||
|
||||
void fireCalculatorEvent(@NotNull CalculatorEventData calculatorEventData, @NotNull CalculatorEventType calculatorEventType, @Nullable Object data);
|
||||
|
||||
void fireCalculatorEvents(@NotNull List<CalculatorEvent> calculatorEvents);
|
||||
|
||||
public static class CalculatorEvent {
|
||||
|
||||
@NotNull
|
||||
private CalculatorEventData calculatorEventData;
|
||||
|
||||
@NotNull
|
||||
private CalculatorEventType calculatorEventType;
|
||||
|
||||
@Nullable
|
||||
private Object data;
|
||||
|
||||
public CalculatorEvent(@NotNull CalculatorEventData calculatorEventData,
|
||||
@NotNull CalculatorEventType calculatorEventType,
|
||||
@Nullable Object data) {
|
||||
this.calculatorEventData = calculatorEventData;
|
||||
this.calculatorEventType = calculatorEventType;
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public CalculatorEventData getCalculatorEventData() {
|
||||
return calculatorEventData;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public CalculatorEventType getCalculatorEventType() {
|
||||
return calculatorEventType;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Object getData() {
|
||||
return data;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
package org.solovyev.android.calculator;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* User: Solovyev_S
|
||||
* Date: 20.09.12
|
||||
* Time: 16:51
|
||||
*/
|
||||
public interface CalculatorEventData extends CalculatorEventDataId {
|
||||
}
|
@@ -0,0 +1,17 @@
|
||||
package org.solovyev.android.calculator;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* User: Solovyev_S
|
||||
* Date: 20.09.12
|
||||
* Time: 18:18
|
||||
*/
|
||||
public interface CalculatorEventDataId {
|
||||
|
||||
// the higher id => the later event
|
||||
long getEventId();
|
||||
|
||||
@Nullable
|
||||
Long getCalculationId();
|
||||
}
|
@@ -0,0 +1,61 @@
|
||||
package org.solovyev.android.calculator;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* User: Solovyev_S
|
||||
* Date: 20.09.12
|
||||
* Time: 18:18
|
||||
*/
|
||||
public class CalculatorEventDataIdImpl implements CalculatorEventDataId {
|
||||
|
||||
private final long eventId;
|
||||
|
||||
@Nullable
|
||||
private final Long calculationId;
|
||||
|
||||
private CalculatorEventDataIdImpl(long id,
|
||||
@Nullable Long calculationId) {
|
||||
this.eventId = id;
|
||||
this.calculationId = calculationId;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static CalculatorEventDataId newInstance(long id,
|
||||
@Nullable Long calculationId) {
|
||||
return new CalculatorEventDataIdImpl(id, calculationId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getEventId() {
|
||||
return this.eventId;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Long getCalculationId() {
|
||||
return this.calculationId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (!(o instanceof CalculatorEventDataIdImpl)) return false;
|
||||
|
||||
CalculatorEventDataIdImpl that = (CalculatorEventDataIdImpl) o;
|
||||
|
||||
if (eventId != that.eventId) return false;
|
||||
if (calculationId != null ? !calculationId.equals(that.calculationId) : that.calculationId != null)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = (int) (eventId ^ (eventId >>> 32));
|
||||
result = 31 * result + (calculationId != null ? calculationId.hashCode() : 0);
|
||||
return result;
|
||||
}
|
||||
}
|
@@ -0,0 +1,47 @@
|
||||
package org.solovyev.android.calculator;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* User: Solovyev_S
|
||||
* Date: 20.09.12
|
||||
* Time: 16:54
|
||||
*/
|
||||
class CalculatorEventDataImpl implements CalculatorEventData {
|
||||
|
||||
@NotNull
|
||||
private CalculatorEventDataId calculatorEventDataId;
|
||||
|
||||
CalculatorEventDataImpl(@NotNull CalculatorEventDataId calculatorEventDataId) {
|
||||
this.calculatorEventDataId = calculatorEventDataId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getEventId() {
|
||||
return calculatorEventDataId.getEventId();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public Long getCalculationId() {
|
||||
return calculatorEventDataId.getCalculationId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (!(o instanceof CalculatorEventDataImpl)) return false;
|
||||
|
||||
CalculatorEventDataImpl that = (CalculatorEventDataImpl) o;
|
||||
|
||||
if (!calculatorEventDataId.equals(that.calculatorEventDataId)) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return calculatorEventDataId.hashCode();
|
||||
}
|
||||
}
|
@@ -0,0 +1,17 @@
|
||||
package org.solovyev.android.calculator;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.EventListener;
|
||||
|
||||
/**
|
||||
* User: Solovyev_S
|
||||
* Date: 20.09.12
|
||||
* Time: 16:39
|
||||
*/
|
||||
public interface CalculatorEventListener extends EventListener {
|
||||
|
||||
void onCalculatorEvent(@NotNull CalculatorEventData calculatorEventData, @NotNull CalculatorEventType calculatorEventType, @Nullable Object data);
|
||||
|
||||
}
|
@@ -0,0 +1,9 @@
|
||||
package org.solovyev.android.calculator;
|
||||
|
||||
/**
|
||||
* User: Solovyev_S
|
||||
* Date: 20.09.12
|
||||
* Time: 16:40
|
||||
*/
|
||||
public enum CalculatorEventType {
|
||||
}
|
@@ -0,0 +1,168 @@
|
||||
package org.solovyev.android.calculator;
|
||||
|
||||
import jscl.AbstractJsclArithmeticException;
|
||||
import jscl.NumeralBaseException;
|
||||
import jscl.math.Generic;
|
||||
import jscl.text.ParseInterruptedException;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.solovyev.android.calculator.jscl.JsclOperation;
|
||||
import org.solovyev.android.calculator.text.TextProcessor;
|
||||
import org.solovyev.common.msg.MessageRegistry;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
/**
|
||||
* User: Solovyev_S
|
||||
* Date: 20.09.12
|
||||
* Time: 16:42
|
||||
*/
|
||||
public class CalculatorImpl implements Calculator {
|
||||
|
||||
@NotNull
|
||||
private final CalculatorEventContainer calculatorEventContainer = new ListCalculatorEventContainer();
|
||||
|
||||
@NotNull
|
||||
private static final Calculator instance = new CalculatorImpl();
|
||||
|
||||
@NotNull
|
||||
private final AtomicLong counter = new AtomicLong(0);
|
||||
|
||||
@NotNull
|
||||
private final Object lock = new Object();
|
||||
|
||||
@NotNull
|
||||
private final TextProcessor<PreparedExpression, String> preprocessor = ToJsclTextProcessor.getInstance();
|
||||
|
||||
@NotNull
|
||||
private final Executor threadPoolExecutor = Executors.newFixedThreadPool(10);
|
||||
|
||||
private CalculatorImpl() {
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static Calculator getInstance() {
|
||||
return instance;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private CalculatorEventDataId nextCalculatorEventDataId() {
|
||||
long eventId = counter.incrementAndGet();
|
||||
return CalculatorEventDataIdImpl.newInstance(eventId, eventId);
|
||||
}
|
||||
|
||||
/*
|
||||
**********************************************************************
|
||||
*
|
||||
* CALCULATION
|
||||
*
|
||||
**********************************************************************
|
||||
*/
|
||||
|
||||
public void evaluate(@NotNull JsclOperation operation,
|
||||
@NotNull String expression) {
|
||||
evaluate(operation, expression, null);
|
||||
}
|
||||
|
||||
public void evaluate(@NotNull final JsclOperation operation,
|
||||
@NotNull final String expression,
|
||||
@Nullable final MessageRegistry mr) {
|
||||
|
||||
final CalculatorEventDataId eventDataId = nextCalculatorEventDataId();
|
||||
|
||||
threadPoolExecutor.execute(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
CalculatorImpl.this.evaluate(eventDataId, operation, expression, mr);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void evaluate(@NotNull CalculatorEventDataId eventDataId,
|
||||
@NotNull JsclOperation operation,
|
||||
@NotNull String expression,
|
||||
@Nullable MessageRegistry mr) {
|
||||
synchronized (lock) {
|
||||
PreparedExpression preparedExpression = null;
|
||||
|
||||
try {
|
||||
preparedExpression = preprocessor.process(expression);
|
||||
|
||||
final String jsclExpression = preparedExpression.toString();
|
||||
try {
|
||||
|
||||
final Generic genericResult = operation.evaluateGeneric(jsclExpression);
|
||||
|
||||
// NOTE: toString() method must be called here as ArithmeticOperationException may occur in it (just to avoid later check!)
|
||||
genericResult.toString();
|
||||
|
||||
//return new Result(operation.getFromProcessor().process(genericResult), operation, genericResult);
|
||||
} catch (AbstractJsclArithmeticException e) {
|
||||
handleException(eventDataId, operation, expression, mr, preparedExpression, null, new CalculatorEvalException(e, e, jsclExpression));
|
||||
}
|
||||
|
||||
} catch (ArithmeticException e) {
|
||||
//final AndroidMessage androidMessage = new AndroidMessage(R.string.msg_1, MessageType.error, CalculatorApplication.getInstance(), e.getMessage());
|
||||
handleException(operation, expression, mr, preparedExpression, new CalculatorParseException(jsclExpression, androidMessage));
|
||||
} catch (StackOverflowError e) {
|
||||
//final AndroidMessage androidMessage = new AndroidMessage(R.string.msg_2, MessageType.error, CalculatorApplication.getInstance());
|
||||
handleException(eventDataId, operation, expression, mr, preparedExpression, new CalculatorParseException(e), null);
|
||||
} catch (jscl.text.ParseException e) {
|
||||
//System.out.println(e.getMessage());
|
||||
handleException(eventDataId, operation, expression, mr, preparedExpression, new CalculatorParseException(e), null);
|
||||
} catch (ParseInterruptedException e) {
|
||||
// do nothing - we ourselves interrupt the calculations
|
||||
} catch (CalculatorParseException e) {
|
||||
handleException(eventDataId, operation, expression, mr, preparedExpression, e, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void handleException(@NotNull CalculatorEventDataId eventDataId,
|
||||
@NotNull JsclOperation operation,
|
||||
@NotNull String expression,
|
||||
@Nullable MessageRegistry mr,
|
||||
@Nullable PreparedExpression preparedExpression,
|
||||
@Nullable CalculatorParseException parseException,
|
||||
@Nullable CalculatorEvalException evalException) {
|
||||
if (operation == JsclOperation.numeric && (preparedExpression != null && preparedExpression.isExistsUndefinedVar() || (evalException != null && evalException.getCause() instanceof NumeralBaseException))) {
|
||||
evaluate(eventDataId, JsclOperation.simplify, expression, mr);
|
||||
}
|
||||
|
||||
if (parseException != null) {
|
||||
throw parseException;
|
||||
} else {
|
||||
throw 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);
|
||||
}
|
||||
}
|
@@ -17,7 +17,7 @@ import org.solovyev.android.calculator.jscl.JsclOperation;
|
||||
* Date: 12/17/11
|
||||
* Time: 9:45 PM
|
||||
*/
|
||||
public interface ICalculatorDisplay extends Editor{
|
||||
public interface JCalculatorDisplay extends Editor{
|
||||
|
||||
boolean isValid();
|
||||
|
@@ -0,0 +1,50 @@
|
||||
package org.solovyev.android.calculator;
|
||||
|
||||
import android.util.Log;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.solovyev.common.utils.ListListenersContainer;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* User: Solovyev_S
|
||||
* Date: 20.09.12
|
||||
* Time: 16:42
|
||||
*/
|
||||
public class ListCalculatorEventContainer implements CalculatorEventContainer {
|
||||
|
||||
@NotNull
|
||||
private static final String TAG = "CalculatorEventData";
|
||||
|
||||
@NotNull
|
||||
private final ListListenersContainer<CalculatorEventListener> listeners = new ListListenersContainer<CalculatorEventListener>();
|
||||
|
||||
@Override
|
||||
public void addCalculatorEventListener(@NotNull CalculatorEventListener calculatorEventListener) {
|
||||
listeners.addListener(calculatorEventListener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeCalculatorEventListener(@NotNull CalculatorEventListener calculatorEventListener) {
|
||||
listeners.removeListener(calculatorEventListener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fireCalculatorEvent(@NotNull CalculatorEventData calculatorEventData, @NotNull CalculatorEventType calculatorEventType, @Nullable Object data) {
|
||||
fireCalculatorEvents(Arrays.asList(new CalculatorEvent(calculatorEventData, calculatorEventType, data)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fireCalculatorEvents(@NotNull List<CalculatorEvent> calculatorEvents) {
|
||||
final List<CalculatorEventListener> listeners = this.listeners.getListeners();
|
||||
|
||||
for (CalculatorEvent e : calculatorEvents) {
|
||||
Log.d(TAG, "Event: " + e.getCalculatorEventType() + " with data: " + e.getData());
|
||||
for (CalculatorEventListener listener : listeners) {
|
||||
listener.onCalculatorEvent(e.getCalculatorEventData(), e.getCalculatorEventType(), e.getData());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2011. Created by serso aka se.solovyev.
|
||||
* For more information, please, contact se.solovyev@gmail.com
|
||||
* or visit http://se.solovyev.org
|
||||
*/
|
||||
|
||||
package org.solovyev.android.calculator;
|
||||
|
||||
import jscl.MathEngine;
|
||||
import jscl.NumeralBase;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.solovyev.android.calculator.math.MathType;
|
||||
|
||||
/**
|
||||
* User: serso
|
||||
* Date: 12/15/11
|
||||
* Time: 8:33 PM
|
||||
*/
|
||||
|
||||
public class LiteNumberBuilder extends AbstractNumberBuilder {
|
||||
|
||||
public LiteNumberBuilder(@NotNull MathEngine engine) {
|
||||
super(engine);
|
||||
this.nb = engine.getNumeralBase();
|
||||
}
|
||||
|
||||
public void process(@NotNull MathType.Result mathTypeResult) {
|
||||
if (canContinue(mathTypeResult)) {
|
||||
// let's continue building number
|
||||
if (numberBuilder == null) {
|
||||
// if new number => create new builder
|
||||
numberBuilder = new StringBuilder();
|
||||
}
|
||||
|
||||
if (mathTypeResult.getMathType() != MathType.numeral_base) {
|
||||
// just add matching string
|
||||
numberBuilder.append(mathTypeResult.getMatch());
|
||||
} else {
|
||||
// set explicitly numeral base (do not include it into number)
|
||||
nb = NumeralBase.getByPrefix(mathTypeResult.getMatch());
|
||||
}
|
||||
|
||||
} else {
|
||||
// process current number (and go to the next one)
|
||||
if (numberBuilder != null) {
|
||||
numberBuilder = null;
|
||||
|
||||
// must set default numeral base (exit numeral base mode)
|
||||
nb = engine.getNumeralBase();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -0,0 +1,203 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2011. Created by serso aka se.solovyev.
|
||||
* For more information, please, contact se.solovyev@gmail.com
|
||||
* or visit http://se.solovyev.org
|
||||
*/
|
||||
|
||||
package org.solovyev.android.calculator;
|
||||
|
||||
import jscl.MathContext;
|
||||
import jscl.MathEngine;
|
||||
import jscl.NumeralBase;
|
||||
import jscl.math.numeric.Real;
|
||||
import jscl.text.*;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.solovyev.android.calculator.AbstractNumberBuilder;
|
||||
import org.solovyev.android.calculator.math.MathType;
|
||||
import org.solovyev.common.MutableObject;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* User: serso
|
||||
* Date: 10/23/11
|
||||
* Time: 2:57 PM
|
||||
*/
|
||||
public class NumberBuilder extends AbstractNumberBuilder {
|
||||
|
||||
public NumberBuilder(@NotNull MathEngine engine) {
|
||||
super(engine);
|
||||
}
|
||||
|
||||
/**
|
||||
* Method replaces number in text according to some rules (e.g. formatting)
|
||||
*
|
||||
* @param text text where number can be replaced
|
||||
* @param mathTypeResult math type result of current token
|
||||
* @param offset offset between new number length and old number length (newNumberLength - oldNumberLength)
|
||||
*
|
||||
*
|
||||
* @return new math type result (as one can be changed due to substituting of number with constant)
|
||||
*/
|
||||
@NotNull
|
||||
public MathType.Result process(@NotNull StringBuilder text, @NotNull MathType.Result mathTypeResult, @Nullable MutableObject<Integer> offset) {
|
||||
final MathType.Result possibleResult;
|
||||
if (canContinue(mathTypeResult)) {
|
||||
// let's continue building number
|
||||
if (numberBuilder == null) {
|
||||
// if new number => create new builder
|
||||
numberBuilder = new StringBuilder();
|
||||
}
|
||||
|
||||
if (mathTypeResult.getMathType() != MathType.numeral_base) {
|
||||
// just add matching string
|
||||
numberBuilder.append(mathTypeResult.getMatch());
|
||||
} else {
|
||||
// set explicitly numeral base (do not include it into number)
|
||||
nb = NumeralBase.getByPrefix(mathTypeResult.getMatch());
|
||||
}
|
||||
|
||||
possibleResult = null;
|
||||
} else {
|
||||
// process current number (and go to the next one)
|
||||
possibleResult = processNumber(text, offset);
|
||||
}
|
||||
|
||||
return possibleResult == null ? mathTypeResult : possibleResult;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method replaces number in text according to some rules (e.g. formatting)
|
||||
*
|
||||
* @param text text where number can be replaced
|
||||
* @param offset offset between new number length and old number length (newNumberLength - oldNumberLength)
|
||||
*
|
||||
* @return new math type result (as one can be changed due to substituting of number with constant)
|
||||
*/
|
||||
@Nullable
|
||||
public MathType.Result processNumber(@NotNull StringBuilder text, @Nullable MutableObject<Integer> offset) {
|
||||
// total number of trimmed chars
|
||||
int trimmedChars = 0;
|
||||
|
||||
String number = null;
|
||||
|
||||
// toXml numeral base (as later it might be replaced)
|
||||
final NumeralBase localNb = getNumeralBase();
|
||||
|
||||
if (numberBuilder != null) {
|
||||
try {
|
||||
number = numberBuilder.toString();
|
||||
|
||||
// let's get rid of unnecessary characters (grouping separators, + after E)
|
||||
final List<String> tokens = new ArrayList<String>();
|
||||
tokens.addAll(MathType.grouping_separator.getTokens());
|
||||
// + after E can be omitted: 10+E = 10E (NOTE: - cannot be omitted )
|
||||
tokens.add("+");
|
||||
for (String groupingSeparator : tokens) {
|
||||
final String trimmedNumber = number.replace(groupingSeparator, "");
|
||||
trimmedChars += number.length() - trimmedNumber.length();
|
||||
number = trimmedNumber;
|
||||
}
|
||||
|
||||
// check if number still valid
|
||||
toDouble(number, getNumeralBase(), engine);
|
||||
|
||||
} catch (NumberFormatException e) {
|
||||
// number is not valid => stop
|
||||
number = null;
|
||||
}
|
||||
|
||||
numberBuilder = null;
|
||||
|
||||
// must set default numeral base (exit numeral base mode)
|
||||
nb = engine.getNumeralBase();
|
||||
}
|
||||
|
||||
return replaceNumberInText(text, number, trimmedChars, offset, localNb, engine);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static MathType.Result replaceNumberInText(@NotNull StringBuilder text,
|
||||
@Nullable String number,
|
||||
int trimmedChars,
|
||||
@Nullable MutableObject<Integer> offset,
|
||||
@NotNull NumeralBase nb,
|
||||
@NotNull final MathEngine engine) {
|
||||
MathType.Result result = null;
|
||||
|
||||
if (number != null) {
|
||||
// in any case remove old number from text
|
||||
final int oldNumberLength = number.length() + trimmedChars;
|
||||
text.delete(text.length() - oldNumberLength, text.length());
|
||||
|
||||
final String newNumber = formatNumber(number, nb, engine);
|
||||
if (offset != null) {
|
||||
// register offset between old number and new number
|
||||
offset.setObject(newNumber.length() - oldNumberLength);
|
||||
}
|
||||
text.append(newNumber);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static String formatNumber(@NotNull String number, @NotNull NumeralBase nb, @NotNull MathEngine engine) {
|
||||
String result;
|
||||
|
||||
int indexOfDot = number.indexOf('.');
|
||||
|
||||
if (indexOfDot < 0) {
|
||||
int indexOfE;
|
||||
if (nb == NumeralBase.hex) {
|
||||
indexOfE = -1;
|
||||
} else {
|
||||
indexOfE = number.indexOf(MathType.POWER_10);
|
||||
}
|
||||
if (indexOfE < 0) {
|
||||
result = engine.addGroupingSeparators(nb, number);
|
||||
} else {
|
||||
final String partBeforeE;
|
||||
if (indexOfE != 0) {
|
||||
partBeforeE = engine.addGroupingSeparators(nb, number.substring(0, indexOfE));
|
||||
} else {
|
||||
partBeforeE = "";
|
||||
}
|
||||
result = partBeforeE + number.substring(indexOfE);
|
||||
}
|
||||
} else {
|
||||
final String integerPart;
|
||||
if (indexOfDot != 0) {
|
||||
integerPart = engine.addGroupingSeparators(nb, number.substring(0, indexOfDot));
|
||||
} else {
|
||||
integerPart = "";
|
||||
}
|
||||
result = integerPart + number.substring(indexOfDot);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static Double toDouble(@NotNull String s, @NotNull NumeralBase nb, @NotNull final MathContext mc) throws NumberFormatException {
|
||||
final NumeralBase defaultNb = mc.getNumeralBase();
|
||||
try {
|
||||
mc.setNumeralBase(nb);
|
||||
|
||||
try {
|
||||
return JsclIntegerParser.parser.parse(Parser.Parameters.newInstance(s, new MutableInt(0), mc), null).content().doubleValue();
|
||||
} catch (ParseException e) {
|
||||
try {
|
||||
return ((Real) DoubleParser.parser.parse(Parser.Parameters.newInstance(s, new MutableInt(0), mc), null).content()).doubleValue();
|
||||
} catch (ParseException e1) {
|
||||
throw new NumberFormatException();
|
||||
}
|
||||
}
|
||||
|
||||
} finally {
|
||||
mc.setNumeralBase(defaultNb);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2011. Created by serso aka se.solovyev.
|
||||
* For more information, please, contact se.solovyev@gmail.com
|
||||
* or visit http://se.solovyev.org
|
||||
*/
|
||||
|
||||
package org.solovyev.android.calculator;
|
||||
|
||||
import jscl.math.function.IConstant;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* User: serso
|
||||
* Date: 10/18/11
|
||||
* Time: 10:07 PM
|
||||
*/
|
||||
public class PreparedExpression implements CharSequence{
|
||||
|
||||
@NotNull
|
||||
private String expression;
|
||||
|
||||
@NotNull
|
||||
private List<IConstant> undefinedVars;
|
||||
|
||||
public PreparedExpression(@NotNull String expression, @NotNull List<IConstant> undefinedVars) {
|
||||
this.expression = expression;
|
||||
this.undefinedVars = undefinedVars;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public String getExpression() {
|
||||
return expression;
|
||||
}
|
||||
|
||||
public boolean isExistsUndefinedVar() {
|
||||
return !this.undefinedVars.isEmpty();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public List<IConstant> getUndefinedVars() {
|
||||
return undefinedVars;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int length() {
|
||||
return expression.length();
|
||||
}
|
||||
|
||||
@Override
|
||||
public char charAt(int i) {
|
||||
return expression.charAt(i);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CharSequence subSequence(int i, int i1) {
|
||||
return expression.subSequence(i, i1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.expression;
|
||||
}
|
||||
}
|
@@ -0,0 +1,154 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2011. Created by serso aka se.solovyev.
|
||||
* For more information, please, contact se.solovyev@gmail.com
|
||||
* or visit http://se.solovyev.org
|
||||
*/
|
||||
|
||||
package org.solovyev.android.calculator;
|
||||
|
||||
import jscl.math.function.IConstant;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.solovyev.android.calculator.CalculatorApplication;
|
||||
import org.solovyev.android.calculator.CalculatorParseException;
|
||||
import org.solovyev.android.calculator.PreparedExpression;
|
||||
import org.solovyev.android.calculator.R;
|
||||
import org.solovyev.android.calculator.text.TextProcessor;
|
||||
import org.solovyev.android.msg.AndroidMessage;
|
||||
import org.solovyev.common.StartsWithFinder;
|
||||
import org.solovyev.android.calculator.math.MathType;
|
||||
import org.solovyev.common.collections.CollectionsUtils;
|
||||
import org.solovyev.common.msg.MessageType;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class ToJsclTextProcessor implements TextProcessor<PreparedExpression, String> {
|
||||
|
||||
@NotNull
|
||||
private static final Integer MAX_DEPTH = 20;
|
||||
|
||||
@NotNull
|
||||
private static final TextProcessor<PreparedExpression, String> instance = new ToJsclTextProcessor();
|
||||
|
||||
private ToJsclTextProcessor() {
|
||||
}
|
||||
|
||||
|
||||
@NotNull
|
||||
public static TextProcessor<PreparedExpression, String> getInstance() {
|
||||
return instance;
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public PreparedExpression process(@NotNull String s) throws CalculatorParseException {
|
||||
return processWithDepth(s, 0, new ArrayList<IConstant>());
|
||||
}
|
||||
|
||||
private static PreparedExpression processWithDepth(@NotNull String s, int depth, @NotNull List<IConstant> undefinedVars) throws CalculatorParseException {
|
||||
return replaceVariables(processExpression(s).toString(), depth, undefinedVars);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static StringBuilder processExpression(@NotNull String s) throws CalculatorParseException {
|
||||
final StartsWithFinder startsWithFinder = new StartsWithFinder(s, 0);
|
||||
final StringBuilder result = new StringBuilder();
|
||||
|
||||
MathType.Result mathTypeResult = null;
|
||||
MathType.Result mathTypeBefore;
|
||||
|
||||
final LiteNumberBuilder nb = new LiteNumberBuilder(CalculatorEngine.instance.getEngine());
|
||||
for (int i = 0; i < s.length(); i++) {
|
||||
if (s.charAt(i) == ' ') continue;
|
||||
startsWithFinder.setI(i);
|
||||
|
||||
mathTypeBefore = mathTypeResult == null ? null : mathTypeResult;
|
||||
|
||||
mathTypeResult = MathType.getType(s, i, nb.isHexMode());
|
||||
|
||||
nb.process(mathTypeResult);
|
||||
|
||||
if (mathTypeBefore != null) {
|
||||
|
||||
final MathType current = mathTypeResult.getMathType();
|
||||
|
||||
if (current.isNeedMultiplicationSignBefore(mathTypeBefore.getMathType())) {
|
||||
result.append("*");
|
||||
}
|
||||
}
|
||||
|
||||
if (mathTypeBefore != null &&
|
||||
(mathTypeBefore.getMathType() == MathType.function || mathTypeBefore.getMathType() == MathType.operator) &&
|
||||
CollectionsUtils.find(MathType.openGroupSymbols, startsWithFinder) != null) {
|
||||
final AndroidMessage androidMessage = new AndroidMessage(R.string.msg_5, MessageType.error, CalculatorApplication.getInstance(), mathTypeBefore.getMatch());
|
||||
throw new CalculatorParseException(i, s, androidMessage);
|
||||
}
|
||||
|
||||
i = mathTypeResult.processToJscl(result, i);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static PreparedExpression replaceVariables(@NotNull final String s, int depth, @NotNull List<IConstant> undefinedVars) throws CalculatorParseException {
|
||||
if (depth >= MAX_DEPTH) {
|
||||
final AndroidMessage androidMessage = new AndroidMessage(R.string.msg_6, MessageType.error, CalculatorApplication.getInstance());
|
||||
throw new CalculatorParseException(s, androidMessage);
|
||||
} else {
|
||||
depth++;
|
||||
}
|
||||
|
||||
final StartsWithFinder startsWithFinder = new StartsWithFinder(s, 0);
|
||||
|
||||
final StringBuilder result = new StringBuilder();
|
||||
for (int i = 0; i < s.length(); i++) {
|
||||
startsWithFinder.setI(i);
|
||||
|
||||
int offset = 0;
|
||||
String functionName = CollectionsUtils.find(MathType.function.getTokens(), startsWithFinder);
|
||||
if (functionName == null) {
|
||||
String operatorName = CollectionsUtils.find(MathType.operator.getTokens(), startsWithFinder);
|
||||
if (operatorName == null) {
|
||||
String varName = CollectionsUtils.find(CalculatorEngine.instance.getVarsRegistry().getNames(), startsWithFinder);
|
||||
if (varName != null) {
|
||||
final IConstant var = CalculatorEngine.instance.getVarsRegistry().get(varName);
|
||||
if (var != null) {
|
||||
if (!var.isDefined()) {
|
||||
undefinedVars.add(var);
|
||||
result.append(varName);
|
||||
offset = varName.length();
|
||||
} else {
|
||||
final String value = var.getValue();
|
||||
assert value != null;
|
||||
|
||||
if ( var.getDoubleValue() != null ) {
|
||||
//result.append(value);
|
||||
// NOTE: append varName as JSCL engine will convert it to double if needed
|
||||
result.append(varName);
|
||||
} else {
|
||||
result.append("(").append(processWithDepth(value, depth, undefinedVars)).append(")");
|
||||
}
|
||||
offset = varName.length();
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
result.append(operatorName);
|
||||
offset = operatorName.length();
|
||||
}
|
||||
} else {
|
||||
result.append(functionName);
|
||||
offset = functionName.length();
|
||||
}
|
||||
|
||||
|
||||
if (offset == 0) {
|
||||
result.append(s.charAt(i));
|
||||
} else {
|
||||
i += offset - 1;
|
||||
}
|
||||
}
|
||||
|
||||
return new PreparedExpression(result.toString(), undefinedVars);
|
||||
}
|
||||
}
|
@@ -11,7 +11,7 @@ import org.jetbrains.annotations.Nullable;
|
||||
import org.simpleframework.xml.Element;
|
||||
import org.simpleframework.xml.Root;
|
||||
import org.simpleframework.xml.Transient;
|
||||
import org.solovyev.android.calculator.ICalculatorDisplay;
|
||||
import org.solovyev.android.calculator.JCalculatorDisplay;
|
||||
import org.solovyev.android.calculator.jscl.JsclOperation;
|
||||
|
||||
/**
|
||||
@@ -47,7 +47,7 @@ public class CalculatorDisplayHistoryState implements Cloneable {
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static CalculatorDisplayHistoryState newInstance(@NotNull ICalculatorDisplay display) {
|
||||
public static CalculatorDisplayHistoryState newInstance(@NotNull JCalculatorDisplay display) {
|
||||
final CalculatorDisplayHistoryState result = new CalculatorDisplayHistoryState();
|
||||
|
||||
result.editorState = EditorHistoryState.newInstance(display);
|
||||
@@ -59,7 +59,7 @@ public class CalculatorDisplayHistoryState implements Cloneable {
|
||||
return result;
|
||||
}
|
||||
|
||||
public void setValuesFromHistory(@NotNull ICalculatorDisplay display) {
|
||||
public void setValuesFromHistory(@NotNull JCalculatorDisplay display) {
|
||||
this.getEditorState().setValuesFromHistory(display);
|
||||
display.setValid(this.isValid());
|
||||
display.setErrorMessage(this.getErrorMessage());
|
||||
|
@@ -0,0 +1,20 @@
|
||||
package org.solovyev.android.calculator.history;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.solovyev.common.history.HistoryHelper;
|
||||
|
||||
/**
|
||||
* User: Solovyev_S
|
||||
* Date: 20.09.12
|
||||
* Time: 16:11
|
||||
*/
|
||||
public interface CalculatorHistory extends HistoryHelper<CalculatorHistoryState> {
|
||||
|
||||
void fromXml(@NotNull String xml);
|
||||
|
||||
String toXml();
|
||||
|
||||
void clearSavedHistory();
|
||||
|
||||
void removeSavedHistory(@NotNull CalculatorHistoryState historyState);
|
||||
}
|
@@ -0,0 +1,131 @@
|
||||
package org.solovyev.android.calculator.history;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.solovyev.common.history.HistoryAction;
|
||||
import org.solovyev.common.history.HistoryHelper;
|
||||
import org.solovyev.common.history.SimpleHistoryHelper;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
/**
|
||||
* User: Solovyev_S
|
||||
* Date: 20.09.12
|
||||
* Time: 16:12
|
||||
*/
|
||||
public class CalculatorHistoryImpl implements CalculatorHistory {
|
||||
|
||||
private final AtomicInteger counter = new AtomicInteger(0);
|
||||
|
||||
@NotNull
|
||||
private final HistoryHelper<CalculatorHistoryState> history = new SimpleHistoryHelper<CalculatorHistoryState>();
|
||||
|
||||
@NotNull
|
||||
private final List<CalculatorHistoryState> savedHistory = new ArrayList<CalculatorHistoryState>();
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return this.history.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CalculatorHistoryState getLastHistoryState() {
|
||||
return this.history.getLastHistoryState();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUndoAvailable() {
|
||||
return history.isUndoAvailable();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CalculatorHistoryState undo(@Nullable CalculatorHistoryState currentState) {
|
||||
return history.undo(currentState);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRedoAvailable() {
|
||||
return history.isRedoAvailable();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CalculatorHistoryState redo(@Nullable CalculatorHistoryState currentState) {
|
||||
return history.redo(currentState);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isActionAvailable(@NotNull HistoryAction historyAction) {
|
||||
return history.isActionAvailable(historyAction);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CalculatorHistoryState doAction(@NotNull HistoryAction historyAction, @Nullable CalculatorHistoryState currentState) {
|
||||
return history.doAction(historyAction, currentState);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addState(@Nullable CalculatorHistoryState currentState) {
|
||||
history.addState(currentState);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public List<CalculatorHistoryState> getStates() {
|
||||
return history.getStates();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
this.history.clear();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public List<CalculatorHistoryState> getSavedHistory() {
|
||||
return Collections.unmodifiableList(savedHistory);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public CalculatorHistoryState addSavedState(@NotNull CalculatorHistoryState historyState) {
|
||||
if (historyState.isSaved()) {
|
||||
return historyState;
|
||||
} else {
|
||||
final CalculatorHistoryState savedState = historyState.clone();
|
||||
|
||||
savedState.setId(counter.incrementAndGet());
|
||||
savedState.setSaved(true);
|
||||
|
||||
savedHistory.add(savedState);
|
||||
|
||||
return savedState;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fromXml(@NotNull String xml) {
|
||||
clearSavedHistory();
|
||||
|
||||
HistoryUtils.fromXml(xml, this.savedHistory);
|
||||
for (CalculatorHistoryState historyState : savedHistory) {
|
||||
historyState.setSaved(true);
|
||||
historyState.setId(counter.incrementAndGet());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toXml() {
|
||||
return HistoryUtils.toXml(this.savedHistory);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearSavedHistory() {
|
||||
this.savedHistory.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeSavedHistory(@NotNull CalculatorHistoryState historyState) {
|
||||
this.savedHistory.remove(historyState);
|
||||
}
|
||||
}
|
@@ -9,7 +9,7 @@ import org.jetbrains.annotations.NotNull;
|
||||
import org.simpleframework.xml.Element;
|
||||
import org.simpleframework.xml.Root;
|
||||
import org.solovyev.android.calculator.Editor;
|
||||
import org.solovyev.android.calculator.ICalculatorDisplay;
|
||||
import org.solovyev.android.calculator.JCalculatorDisplay;
|
||||
|
||||
/**
|
||||
* User: serso
|
||||
@@ -38,7 +38,7 @@ public class CalculatorHistoryState extends AbstractHistoryState {
|
||||
this.displayState = displayState;
|
||||
}
|
||||
|
||||
public static CalculatorHistoryState newInstance(@NotNull Editor editor, @NotNull ICalculatorDisplay display) {
|
||||
public static CalculatorHistoryState newInstance(@NotNull Editor editor, @NotNull JCalculatorDisplay display) {
|
||||
final EditorHistoryState editorHistoryState = EditorHistoryState.newInstance(editor);
|
||||
final CalculatorDisplayHistoryState displayHistoryState = CalculatorDisplayHistoryState.newInstance(display);
|
||||
return new CalculatorHistoryState(editorHistoryState, displayHistoryState);
|
||||
@@ -94,7 +94,7 @@ public class CalculatorHistoryState extends AbstractHistoryState {
|
||||
return result;
|
||||
}
|
||||
|
||||
public void setValuesFromHistory(@NotNull Editor editor, @NotNull ICalculatorDisplay display) {
|
||||
public void setValuesFromHistory(@NotNull Editor editor, @NotNull JCalculatorDisplay display) {
|
||||
this.getEditorState().setValuesFromHistory(editor);
|
||||
this.getDisplayState().setValuesFromHistory(display);
|
||||
}
|
||||
|
@@ -0,0 +1,77 @@
|
||||
package org.solovyev.common.utils;
|
||||
|
||||
import com.google.common.base.Predicate;
|
||||
import com.google.common.collect.Iterables;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* User: Solovyev_S
|
||||
* Date: 20.09.12
|
||||
* Time: 16:43
|
||||
*/
|
||||
// todo serso: move to common
|
||||
public class ListListenersContainer<T> {
|
||||
|
||||
@NotNull
|
||||
private final List<WeakReference<T>> listeners = new ArrayList<WeakReference<T>>();
|
||||
|
||||
public void addListener(@NotNull final T listener) {
|
||||
synchronized (listeners) {
|
||||
boolean contains = Iterables.any(listeners, new WeakReferencePredicate<T>(listener));
|
||||
|
||||
if (!contains) {
|
||||
listeners.add(new WeakReference<T>(listener));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void removeListener(@NotNull T listener) {
|
||||
synchronized (listeners) {
|
||||
Iterables.removeIf(listeners, new WeakReferencePredicate<T>(listener));
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public List<T> getListeners() {
|
||||
final List<T> localListeners;
|
||||
|
||||
synchronized (listeners) {
|
||||
localListeners = new ArrayList<T>(listeners.size());
|
||||
|
||||
// copy listeners and remove garbage collected references
|
||||
for ( Iterator<WeakReference<T>> it = listeners.iterator(); it.hasNext(); ) {
|
||||
final WeakReference<T> r = it.next();
|
||||
final T t = r.get();
|
||||
if ( t == null ) {
|
||||
it.remove();
|
||||
} else {
|
||||
localListeners.add(t);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return localListeners;
|
||||
}
|
||||
|
||||
private static class WeakReferencePredicate<T> implements Predicate<WeakReference<T>> {
|
||||
|
||||
@NotNull
|
||||
private final T t;
|
||||
|
||||
public WeakReferencePredicate(T t) {
|
||||
this.t = t;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(@Nullable WeakReference<T> r) {
|
||||
final T t = r != null ? r.get() : null;
|
||||
return this.t.equals(t);
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user