com.sciencegadgets.client.conversion.ConversionActivity.java Source code

Java tutorial

Introduction

Here is the source code for com.sciencegadgets.client.conversion.ConversionActivity.java

Source

/*******************************************************************************
 *     This file is part of ScienceGadgets, a collection of educational tools
 *     Copyright (C) 2012-2015 by John Gralyan
 *
 *     ScienceGadgets is free software: you can redistribute it and/or modify
 *     it under the terms of the GNU Affero General Public License as
 *     published by the Free Software Foundation, either version 3 of
 *     the License, or (at your option) any later version.
 *
 *     ScienceGadgets is distributed in the hope that it will be useful,
 *     but WITHOUT ANY WARRANTY; without even the implied warranty of
 *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *     GNU Affero General Public License for more details.
 *
 *     You should have received a copy of the GNU Affero General Public License
 *     along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *     
 *     Contact us at info@sciencegadgets.org
 *******************************************************************************/
package com.sciencegadgets.client.conversion;

import java.math.BigDecimal;
import java.math.MathContext;
import java.util.LinkedList;
import java.util.Map.Entry;

import com.google.gwt.core.client.GWT;
import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.NodeList;
import com.google.gwt.dom.client.Style.VerticalAlign;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.uibinder.client.UiBinder;
import com.google.gwt.uibinder.client.UiField;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.AbsolutePanel;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.Label;
import com.sciencegadgets.client.JSNICalls;
import com.sciencegadgets.client.Moderator;
import com.sciencegadgets.client.Moderator.ActivityType;
import com.sciencegadgets.client.algebra.AlgebraActivity;
import com.sciencegadgets.client.algebra.EquationHTML;
import com.sciencegadgets.client.algebra.EquationTree;
import com.sciencegadgets.client.algebra.EquationTree.EquationNode;
import com.sciencegadgets.client.algebra.Wrapper;
import com.sciencegadgets.client.algebra.transformations.Skill;
import com.sciencegadgets.client.entities.DataModerator;
import com.sciencegadgets.client.entities.Equation;
import com.sciencegadgets.client.entities.Unit;
import com.sciencegadgets.client.ui.CSS;
import com.sciencegadgets.client.ui.SelectionPanel;
import com.sciencegadgets.client.ui.SelectionPanel.Cell;
import com.sciencegadgets.client.ui.SelectionPanel.SelectionHandler;
import com.sciencegadgets.client.ui.UnitSelection;
import com.sciencegadgets.client.ui.specification.NumberSpecification;
import com.sciencegadgets.shared.MathAttribute;
import com.sciencegadgets.shared.TypeSGET;
import com.sciencegadgets.shared.TypeSGET.Operator;
import com.sciencegadgets.shared.dimensions.CommonDerivedUnits;
import com.sciencegadgets.shared.dimensions.UnitAttribute;
import com.sciencegadgets.shared.dimensions.UnitHTML;
import com.sciencegadgets.shared.dimensions.UnitMap;
import com.sciencegadgets.shared.dimensions.UnitMultiple;
import com.sciencegadgets.shared.dimensions.UnitName;

public class ConversionActivity extends AbsolutePanel {

    interface ConversionUiBinder extends UiBinder<FlowPanel, ConversionActivity> {
    }

    private static ConversionUiBinder conversionUiBinder = GWT.create(ConversionUiBinder.class);

    @UiField
    FlowPanel dimensionalAnalysisArea;
    @UiField
    FlowPanel unitSelectionArea;
    @UiField
    FlowPanel deriveUnitArea;
    @UiField
    AbsolutePanel wrapperArea;
    @UiField
    Button convertButton;
    @UiField
    Label valueDisplay;

    static final UnitSelection unitSelection = new UnitSelection(false, true);
    static final SelectionPanel derivedUnitsSelection = new SelectionPanel("Base Units");
    private EquationTree mTree = null;
    private EquationNode node;

    // wrapped nodes and history nodes

    LinkedList<UnitDisplay> unitDisplays = new LinkedList<UnitDisplay>();

    //   private Unit selectedUnit = null;
    static ConversionWrapper selectedWrapper = null;

    private EquationNode totalNode;
    private EquationNode wrapperFraction;

    private Element workingHTML;

    public Equation variableEquation;

    public ConversionActivity() {

        add(conversionUiBinder.createAndBindUi(this));

        getElement().setAttribute("id", CSS.CONVERSION_ACTIVITY);

        unitSelection.unitBox.addSelectionHandler(new ConvertSelectHandler());
        unitSelection.addStyleName(CSS.FILL_PARENT);
        unitSelectionArea.add(unitSelection);

        derivedUnitsSelection.addSelectionHandler(new ConvertSelectHandler());
        derivedUnitsSelection.addStyleName(CSS.FILL_PARENT);
        deriveUnitArea.add(derivedUnitsSelection);

        convertButton.addClickHandler(new ConvertCompleteClickHandler());
    }

    public void setNode(EquationNode node) {
        this.node = node;
    }

    public void load(String initialValue, UnitAttribute unitAttribute, boolean allowConvertButton) {
        unitDisplays.clear();
        //      selectedUnit = null;
        selectedWrapper = null;
        convertButton.setVisible(allowConvertButton);

        mTree = new EquationTree(TypeSGET.Term, "", TypeSGET.Term, "", false);

        totalNode = mTree.getLeftSide().append(TypeSGET.Number, initialValue);
        mTree.getRightSide().append(totalNode.clone());
        valueDisplay.setText(initialValue);

        UnitMultiple[] bases = unitAttribute.getUnitMultiples();

        EquationNode fracHistory = mTree.newNode(TypeSGET.Fraction, "");
        EquationNode numerHistory = fracHistory.append(TypeSGET.Term, "");
        EquationNode denomHistory = fracHistory.append(TypeSGET.Term, "");

        EquationNode fracWorking = wrapperFraction = fracHistory.clone();
        EquationNode numerWorking = fracWorking.getChildAt(0);
        EquationNode denomWorking = fracWorking.getChildAt(1);

        for (UnitMultiple base : bases) {
            boolean negExp = base.toString().contains("-");
            String symbol = base.getUnitName().getSymbol();
            String exp = base.getUnitExponent().replace("-", "");

            EquationNode unitNode;
            if ("1".equals(exp)) {
                unitNode = mTree.newNode(TypeSGET.Variable, symbol);
            } else {
                unitNode = mTree.newNode(TypeSGET.Exponential, "");
                unitNode.append(TypeSGET.Variable, symbol);
                unitNode.append(TypeSGET.Number, exp);
            }
            unitNode.setAttribute(MathAttribute.Unit, base.toString().replace("-", ""));
            EquationNode unitNodeClone = unitNode.clone();

            if (negExp) {
                denomWorking.append(unitNodeClone);
                denomHistory.append(unitNode);
            } else {
                numerWorking.append(unitNodeClone);
                numerHistory.append(unitNode);
            }
            unitDisplays.add(new UnitDisplay(unitNodeClone, unitNode, false, !negExp));
        }

        EquationNode[] numAndDens = { numerHistory, denomHistory, numerWorking, denomWorking };
        for (EquationNode numOrDen : numAndDens) {
            if (numOrDen.getChildCount() == 0) {
                numOrDen.append(TypeSGET.Number, "1");
            }
        }

        mTree.getRightSide().append(fracHistory);
        mTree.getLeftSide().append(fracWorking);
    }

    @Override
    protected void onLoad() {
        super.onLoad();
        // Dynamically fills parent
        reloadEquation();
    }

    public void reloadEquation() {
        // Make and add entire equation before moving left
        dimensionalAnalysisArea.clear();
        EquationHTML eqHTML = mTree.reloadDisplay(false, false);

        // Unit font
        wrapperArea.addStyleName(CSS.UNIT);
        NodeList<Element> elements = eqHTML.getElement().getElementsByTagName("*");
        for (int i = 0; i < elements.getLength(); i++) {
            elements.getItem(i).addClassName(CSS.UNIT);
        }

        // Recreate wrappers
        placeWrappers();

        // Place entire equation in history area first
        eqHTML.autoFillParent = true;
        dimensionalAnalysisArea.add(eqHTML);

        // No need for equals sign
        mTree.getEquals().getHTML(true, true).removeFromParent();

        // Move left side of equation into wrapper area
        Element newWorkingHTML = mTree.getLeftSide().getHTML(false, false);
        if (workingHTML == null) {
            wrapperArea.getElement().appendChild(newWorkingHTML);
        } else {
            wrapperArea.getElement().replaceChild(newWorkingHTML, workingHTML);
        }
        workingHTML = newWorkingHTML;

        // Resize working area
        double widthRatio = (double) wrapperArea.getOffsetWidth() / workingHTML.getOffsetWidth();
        double heightRatio = (double) wrapperArea.getOffsetHeight() / workingHTML.getOffsetHeight();
        double smallerRatio = (widthRatio > heightRatio) ? heightRatio : widthRatio;
        double fontPercent = smallerRatio * 95;// *95 for looser fit
        workingHTML.getStyle().setFontSize(fontPercent, com.google.gwt.dom.client.Style.Unit.PCT);

        // HashMap<Parameter, String> parameterMap = new HashMap<Parameter,
        // String>();
        // parameterMap
        // .put(Parameter.activity, ActivityType.conversion.toString());
        // parameterMap.put(Parameter.equation, mTree.getMathXMLString());
        // URLParameters.setParameters(parameterMap, false);
    }

    private void placeWrappers() {
        LinkedList<Wrapper> wrappers = mTree.getWrappers();
        wrappers.clear();

        for (UnitDisplay unitDisplay : unitDisplays) {
            EquationNode jointNode = unitDisplay.historyNode;
            if (!unitDisplay.isCanceled) {
                new ConversionWrapper(unitDisplay, wrapperArea, this);
            } else {
                unitDisplay.wrappedNode.getHTML(false, false).removeFromParent();

                if (TypeSGET.Number.equals(jointNode.getType())) {
                    Element[] units = jointNode.getHTMLofUnits();
                    for (Element unit : units) {
                        unit.addClassName("lineThrough");
                    }
                } else {
                    jointNode.getHTML(false, false).addClassName("lineThrough");
                }
            }
        }

        for (Wrapper wrap : wrappers) {
            wrap.addAssociativeDragDrop();
            ((ConversionWrapper) wrap).addUnitCancelDropControllers();
        }
    }

    void fillUnitSelection(final String unitName) {

        //      DataModerator.database.getUnitQuantityKindName(unitName, new AsyncCallback<String>() {
        //         @Override
        //         public void onFailure(Throwable caught) {
        //            JSNICalls.error("getUnit Failed: "+caught.getMessage());
        //         }
        //
        //         @Override
        //         public void onSuccess(String qkName) {
        ////            selectedUnit = result;
        ////            String quantityKind = selectedUnit.getQuantityKindName();
        ////            String excludedUnitName = selectedUnit.getName().toString();
        //
        ////            unitSelection.reloadUnitBox(quantityKind, excludedUnitName);
        //            unitSelection.reloadUnitBox(qkName, unitName, false);
        //         }
        //      });

        String qkName = DataModerator.getQuantityKindByUnitName(unitName);
        if (qkName != null) {
            unitSelection.reloadUnitBox(qkName, unitName, false);
        }

        derivedUnitsSelection.clear();

        try {// Deconstruct option for derived units
            CommonDerivedUnits derivedUnit = CommonDerivedUnits.valueOf(unitName);
            UnitAttribute dataUnitAttribute = derivedUnit.getDerivedMap().getUnitAttribute();
            Element derivedUnitElement = UnitHTML.create(dataUnitAttribute, null, false);
            derivedUnitElement.getStyle().setVerticalAlign(VerticalAlign.MIDDLE);
            String derivedUnitHTML = JSNICalls.elementToString(derivedUnitElement);

            derivedUnitsSelection.add(
                    derivedUnit.getSymbol() + "=" + derivedUnit.getConversionMultiplier() + derivedUnitHTML,
                    derivedUnit.getUnitName().toString(), derivedUnit);
        } catch (IllegalArgumentException e) {
        }
    }

    private void convert(UnitMap toMap, String toMultiplier) {

        // Keep unit exponent constant, all get cancelled
        String exp = selectedWrapper.getNode().getUnitAttribute().getUnitMultiples()[0].getUnitExponent();
        int expAbs = Math.abs(Integer.parseInt(exp));

        Unit selectedUnit = selectedWrapper.getUnit();
        if (selectedUnit == null) {
            return;
        }

        UnitAttribute fromUnitAttribute = new UnitAttribute(
                selectedUnit.getName() + UnitAttribute.EXP_DELIMITER + 1);
        UnitMap fromMap = new UnitMap(fromUnitAttribute);
        String fromMultiplier = selectedUnit.getConversionMultiplier();
        toMap = toMap.getExponential(expAbs, 1);
        fromMap = fromMap.getExponential(expAbs, 1);

        boolean isSelectNum = selectedWrapper.getUnitDisplay().inNumerator;
        UnitMap newUnitMap = isSelectNum ? toMap.getDivision(fromMap) : fromMap.getDivision(toMap);
        String numMultiplier = isSelectNum ? fromMultiplier : toMultiplier;
        String denMultiplier = !isSelectNum ? fromMultiplier : toMultiplier;

        // History fraction of multipliers
        mTree.getRightSide().append(TypeSGET.Operation, Operator.getMultiply().getSign());
        EquationNode newHistoryFrac = mTree.getRightSide().append(TypeSGET.Fraction, "");
        EquationNode newHistoryNum = newHistoryFrac.append(TypeSGET.Sum, "");
        EquationNode newHistoryDen = newHistoryFrac.append(TypeSGET.Sum, "");

        EquationNode numMultiplierNode = mTree.newNode(TypeSGET.Number, numMultiplier);
        EquationNode denMultiplierNode = mTree.newNode(TypeSGET.Number, denMultiplier);

        if (expAbs == 1) {
            newHistoryNum.append(numMultiplierNode);
            newHistoryDen.append(denMultiplierNode);
        } else {
            EquationNode numExp = newHistoryNum.append(TypeSGET.Exponential, "");
            EquationNode denExp = newHistoryDen.append(TypeSGET.Exponential, "");
            numExp.append(numMultiplierNode);
            denExp.append(denMultiplierNode);
            numExp.append(TypeSGET.Number, expAbs + "");
            denExp.append(TypeSGET.Number, expAbs + "");
        }

        // Update Working area
        int selectedIndex = selectedWrapper.getNode().getIndex();
        selectedIndex = newUnitMap.size() == 2 ? selectedIndex : -1;
        for (Entry<UnitName, Integer> entry : newUnitMap.entrySet()) {
            UnitName unitName = entry.getKey();
            Integer unitExp = entry.getValue();
            Integer unitExpAbs = Math.abs(unitExp);
            boolean inNum = unitExp > 0;

            String numSymbol = unitName.getSymbol();

            EquationNode workingNode = mTree.newNode(TypeSGET.Variable, numSymbol);
            if (unitExp > 1 || unitExp < -1) {
                EquationNode workingExp = mTree.newNode(TypeSGET.Exponential, "");
                workingExp.append(workingNode);
                workingExp.append(TypeSGET.Number, "" + unitExpAbs);
                workingNode = workingExp;
            }
            workingNode.setAttribute(MathAttribute.Unit, unitName + UnitAttribute.EXP_DELIMITER + unitExpAbs);
            EquationNode historyNode = workingNode.clone();

            int numOrDen = inNum ? 0 : 1;
            wrapperFraction.getChildAt(numOrDen).addBefore(selectedIndex, workingNode);
            newHistoryFrac.getChildAt(numOrDen).append(historyNode);

            unitDisplays.add(new UnitDisplay(workingNode, historyNode, fromMap.containsKey(unitName), inNum));
        }

        // Calculate conversion
        BigDecimal total = new BigDecimal(totalNode.getSymbol());
        for (int i = 0; i < expAbs; i++) {
            total = total.multiply(new BigDecimal(numMultiplier), MathContext.DECIMAL128);
            total = total.divide(new BigDecimal(denMultiplier), MathContext.DECIMAL128);
        }
        totalNode.setSymbol(total.stripTrailingZeros().toEngineeringString());
        valueDisplay.setText(total.stripTrailingZeros().toPlainString());

        selectedWrapper.getUnitDisplay().isCanceled = true;
        selectedWrapper.unselect();

        reloadEquation();
    }

    // /////////////////////////////////////////////////////
    // InnerClasses
    // /////////////////////////////////////////////////////

    class UnitDisplay {
        EquationNode wrappedNode;
        EquationNode historyNode;
        boolean isCanceled = false;
        boolean inNumerator = true;

        public UnitDisplay(EquationNode wrappedNode, EquationNode historyNode, boolean isCanceled,
                boolean inNumerator) {
            super();
            this.wrappedNode = wrappedNode;
            this.historyNode = historyNode;
            this.isCanceled = isCanceled;
            this.inNumerator = inNumerator;
        }
    }

    class ConvertSelectHandler implements SelectionHandler {
        @Override
        public void onSelect(Cell selected) {
            Object selectedEntity = selected.getEntity();
            if (selectedEntity instanceof CommonDerivedUnits) {
                CommonDerivedUnits deriveUnit = (CommonDerivedUnits) selectedEntity;
                convert(deriveUnit.getDerivedMap(), "1"/*deriveUnit.getConversionMultiplier()*/);
            } else if (selectedEntity instanceof Unit) {
                Unit toUnit = (Unit) selectedEntity;
                convert(new UnitMap(new UnitAttribute(toUnit.getName() + UnitAttribute.EXP_DELIMITER + 1)),
                        toUnit.getConversionMultiplier());
            }
        }
    }

    class ConvertCompleteClickHandler implements ClickHandler {
        @Override
        public void onClick(ClickEvent event) {
            String unitAttribute = "";
            for (UnitDisplay unitDisplay : unitDisplays) {
                if (!unitDisplay.isCanceled) {
                    UnitAttribute unitAtt = unitDisplay.wrappedNode.getUnitAttribute();
                    if (!unitDisplay.inNumerator && !unitAtt.toString().contains("-")) {
                        String exp = unitAtt.getUnitMultiples()[0].getUnitExponent();
                        unitAtt.setString(unitAtt.toString().replace(exp, "-" + exp));
                    }
                    unitAttribute = unitAttribute + "*" + unitAtt;
                }
            }
            unitAttribute = unitAttribute.replaceFirst(UnitAttribute.BASE_DELIMITER_REGEX, "");
            node.setSymbol(totalNode.getSymbol());
            node.setAttribute(MathAttribute.Unit, unitAttribute);
            if (variableEquation == null) {
                Moderator.switchToAlgebra(node.getTree(), ActivityType.interactiveequation, true);
                AlgebraActivity aActivity = Moderator.getCurrentAlgebraActivity();
                aActivity.getAlgebraHistory().updateAlgebraHistory("Conversion", Skill.CONVERSION,
                        aActivity.getEquationTree());
            } else {
                variableEquation.reCreate(node.getTree());
                Moderator.switchBackToProblem();
            }
        }
    }
}