All Downloads are FREE. Search and download functionalities are using the official Maven repository.

at.spardat.xma.mdl.simple.SimpleWM Maven / Gradle / Ivy

/*******************************************************************************
 * Copyright (c) 2003, 2007 s IT Solutions AT Spardat GmbH .
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     s IT Solutions AT Spardat GmbH - initial API and implementation
 *******************************************************************************/

// @(#) $Id: SimpleWM.java 3240 2009-03-03 16:10:56Z gub $
package at.spardat.xma.mdl.simple;

import java.io.IOException;
import java.math.BigDecimal;
import java.util.Date;

import at.spardat.properties.XProperties;
import at.spardat.xma.mdl.*;
import at.spardat.xma.mdl.util.DNode;
import at.spardat.xma.page.*;
import at.spardat.xma.serializer.XmaInput;
import at.spardat.xma.serializer.XmaOutput;
import at.spardat.xma.test.TestUtil;
import at.spardat.xma.util.Assert;
import at.spardat.enterprise.fmt.AParseException;
import at.spardat.enterprise.fmt.IFmt;
import at.spardat.enterprise.util.*;

/**
 * Implements ISimpleWM.
 */
public class SimpleWM extends WModel implements ISimpleWM {


    /**
     * One of the type codes in enterprise.util.Types.
     */
    protected byte              type_;

    /**
     * A string encoded value of a particular type. This instance is never null.
     */
    protected String            value_ = "";

    /**
     * A copy of a value to support change tracking.
     */
    private String              savedValue_;

    /**
     * Indicates if sloppySetters_ has been computed
     */
    private static boolean      sloppySettersComputed_ = false;

    /**
     * Indicates that set-methods should do nothing on type-violations. This behaviour is supported
     * just for compatibility. In future version, sloppySetters_ will always be false.
     */
    private static boolean      sloppySetters_ = false;



    /**
     * Constructs a SimpleWM.
     *
     * @param id a numeric id which identifies the WidgetModel within its Page.
     * @param type a type constant.
     * @param pm the Page this model belongs to
     */
    public SimpleWM (short id, byte type, Page pm) {
        super (id, pm);
        if (type < Types.FIRST || type > Types.LAST) throw new IllegalArgumentException();
        type_ = type;
        if (!sloppySettersComputed_) {
            sloppySettersComputed_ = true;
            sloppySetters_ = settersShouldBeSloppy(pm);
        }
    }

    /**
     * Determines if setters should ignore type violations.
     */
    protected boolean settersShouldBeSloppy(Page pm){
        // This method is overridden in SimpleWMClient, so this implementation is called
        // only on server side.
        XProperties node = XProperties.getNodeOfPackage("xma.runtime");
        return "true".equals (node.get("sloppyModelSetters", "false"));
     }

    /**
     * Sets this TextModel to the empty string.
     */
    public void clear() {
        handle (new ClearEvent ());
    }

    /**
     * Returns true if this model holds a value.
     *
     * @return true if this has a non empty value.
     */
    public boolean hasValue() {
        return value_.length() > 0;
    }

    /**
     * Returns the type constant.
     *
     * @return the type constant, see {@link at.spardat.enterprise.util.Types}.
     */
    public byte getType() {
        return type_;
    }

    /**
     * @see IAtomic#toString()
     */
    public String toString() {
        return Atom.toStringImpl (type_, value_);
    }

    /**
     * Maps this to string representation using a IFmt object.
     * The provided formatter is only used if it is type compatible
     * with the type of this IAtomic. Otherwise, or if formatter is
     * null, the internal String encoding is returned.
     *
     * @param formatter the IFmt formatter. May be null.
     * @return non null String.
     */
    public String toString (IFmt formatter) {
        if (formatter == null) return value_;
        if (formatter.mayBeAppliedTo(type_)) {
            return formatter.format(value_);
        } else
            return value_;
    }

    /**
     * Sets the value of this model from a string. Clears this if value==null.
     * Does nothing if getType() is not equal to T_STRING.
     *
     * @param value the string to set
     */
    public void set (String value) {
        if (value == null) clear();
        else {
            setInternalFromEvent (new StringChangeEvent (value));
        }
    }

    /**
     * Calls handle for an change event and decides if IllegalStateExceptions should
     * be thrown.
     */
    private void setInternalFromEvent (ModelChangeEvent ev) {
        boolean success = handle (ev);
        if (!sloppySetters_ && !success) throw new IllegalStateException ("type mismatch");
    }

    /**
     * Extracts a double from this. Returns 0.0 if getType() is not equal to T_BCD or !hasValue().
     *
     * @return double
     */
    public double toDouble () {
        if (type_ != Types.T_BCD || !hasValue()) return 0.0;
        return Double.parseDouble(value_);
    }

    /**
     * @see at.spardat.xma.mdl.IAtomic#toFloat()
     */
    public float toFloat() {
        if (type_ != Types.T_BCD || !hasValue()) return 0.0F;
        return Float.parseFloat(value_);
    }


    /**
     * Sets the value from a double. Does nothing if getType() is not equal to T_BCD.
     * Also does nothing if the provided double is NaN or Infinity.
     *
     * @param value the value to set
     * @param precision the number of places after the comma used when converting
     *         the double to a string.
     */
    public void set (double value, int precision) {
        setInternalFromEvent (new BcdChangeEvent (value, precision));
    }

    /**
     * Sets the value from a double. Does nothing if getType() is not equal to T_BCD.
     * Also does nothing if the provided double is NaN or Infinity. If the provided
     * double (which is binary coded) would result in an unexact decimal
     * representation, the fractional part of the result is stripped so that there
     * are no more then 15 significant digits.
     *
     * @param value the value to set
     */
    public void set (double value) {
        setInternalFromEvent (new BcdChangeEvent (value, -1));
    }

    /**
     * @see at.spardat.xma.mdl.simple.ISimpleWM#set(java.math.BigDecimal)
     */
    public void set (BigDecimal value) {
        setInternalFromEvent (new Bcd2ChangeEvent(value));
    }

    /**
     * @see at.spardat.xma.mdl.IAtomic#toBigDecimal()
     */
    public BigDecimal toBigDecimal() {
        if (type_ != Types.T_BCD || !hasValue()) return null;
        return new BigDecimal (value_);
    }


    /**
     * Returns the value of this as int if the type of this is T_BCD. Returns
     * 0 if the type is not T_BCD.
     *
     * @see at.spardat.xma.mdl.IAtomic#toInt()
     */
    public int toInt () {
        if (type_ != Types.T_BCD || !hasValue()) return 0;
        return Integer.parseInt(value_);
    }

    /**
     * @see at.spardat.xma.mdl.IAtomic#toLong()
     */
    public long toLong() {
        if (type_ != Types.T_BCD || !hasValue()) return 0;
        return Long.parseLong (value_);
    }

    /**
     * @see at.spardat.xma.mdl.IAtomic#toByte()
     */
    public byte toByte() {
        if (type_ != Types.T_BCD || !hasValue()) return 0;
        return Byte.parseByte (value_);
    }

    /**
     * @see at.spardat.xma.mdl.IAtomic#toShort()
     */
    public short toShort() {
        if (type_ != Types.T_BCD || !hasValue()) return 0;
        return Short.parseShort (value_);
    }

    /**
     * @see at.spardat.xma.mdl.IAtomic#toByte()
     */
    public Byte toBYTE() {
        if (type_ != Types.T_BCD || !hasValue()) return null;
        return new Byte(value_);
    }

    /**
     * @see at.spardat.xma.mdl.IAtomic#toSHORT()
     */
    public Short toSHORT() {
        if (type_ != Types.T_BCD || !hasValue()) return null;
        return new Short(value_);
    }

    /**
     * @see at.spardat.xma.mdl.IAtomic#toINTEGER()
     */
    public Integer toINTEGER() {
        if (type_ != Types.T_BCD || !hasValue()) return null;
        return new Integer(value_);
    }

    /**
     * @see at.spardat.xma.mdl.IAtomic#toLONG()
     */
    public Long toLONG() {
        if (type_ != Types.T_BCD || !hasValue()) return null;
        return new Long(value_);
    }

    /**
     * @see at.spardat.xma.mdl.IAtomic#toFLOAT()
     */
    public Float toFLOAT() {
        if (type_ != Types.T_BCD || !hasValue()) return null;
        return new Float (value_);
    }

    /**
     * @see at.spardat.xma.mdl.IAtomic#toDOUBLE()
     */
    public Double toDOUBLE() {
        if (type_ != Types.T_BCD || !hasValue()) return null;
        return new Double(value_);
    }

    /**
     * @see at.spardat.xma.mdl.simple.ISimpleWM#set(java.lang.Boolean)
     */
    public void set(Boolean val) {
        if (val == null) clear();
        else set (val.booleanValue());
    }

    /**
     * @see at.spardat.xma.mdl.simple.ISimpleWM#set(java.lang.Byte)
     */
    public void set (Byte val) {
        if (val == null) clear();
        else set (val.intValue());
    }

    /**
     * @see at.spardat.xma.mdl.simple.ISimpleWM#set(java.lang.Short)
     */
    public void set (Short val) {
        if (val == null) clear();
        else set (val.intValue());
    }

    /**
     * @see at.spardat.xma.mdl.simple.ISimpleWM#set(java.lang.Integer)
     */
    public void set (Integer val) {
        if (val == null) clear();
        else set (val.intValue());
    }

    /**
     * @see at.spardat.xma.mdl.simple.ISimpleWM#set(java.lang.Long)
     */
    public void set(Long val) {
        if (val == null) clear();
        else set (val.doubleValue());
    }

    /**
     * @see at.spardat.xma.mdl.simple.ISimpleWM#set(java.lang.Float)
     */
    public void set (Float val) {
        if (val == null) clear();
        else set (val.doubleValue());
    }

    /**
     * @see at.spardat.xma.mdl.simple.ISimpleWM#set(java.lang.Double)
     */
    public void set (Double val) {
        if (val == null) clear();
        else set (val.doubleValue());
    }



    /**
     * Sets this from an int. Does nothing if the type of this is not T_BCD.
     *
     * @param value the provided integer to set.
     */
    public void set (int value) {
        setInternalFromEvent (new BcdChangeEvent (value, -1));
    }

    /**
     * Returns a newly constructed java.util.Date object representing the value of this.
     *
     * @return null if the type is not equal to T_DATE or T_TIMESTAMP or !hasValue().
     */
    public Date toDate () {
        return Atom.toDateImpl (type_, value_);
    }

    /**
     * Sets this from a provided java.util.Date. This method does nothing if the type
     * is not equal to T_DATE or T_TIMESTAMP.
     *
     * @param value the value to set
     */
    public void set (Date value) {
        if (value == null) clear();
        else {
            if (type_ == Types.T_DATE) setInternalFromEvent (new DateChangeEvent (value));
            else if (type_ == Types.T_TIMESTAMP) setInternalFromEvent (new TimeStampChangeEvent (value));
            else throw new IllegalStateException ("type mismatch");
        }
    }

    /**
     * Returns true if getType() equals T_BOOLEAN and the stored value equals TRUE. If
     * there is no value stored or the stored value is not TRUE, false is returned.
     *
     * @return boolean indicating the value in this.
     */
    public boolean isTrue() {
        if (type_ != Types.T_BOOLEAN) return false;
        if (!hasValue()) return false;
        return value_.equals("J");
    }

    /**
     * If getType() equals T_BOOLEAN, this method sets a boolean value, otherwise it does nothing.
     *
     * @param value the boolean value to set
     */
    public void set (boolean value) {
        setInternalFromEvent (new BooleanChangeEvent (value, false));
    }

    /**
     * @see at.spardat.xma.mdl.WModel#changed()
     */
    public boolean changed() {
        return savedValue_ != null;
    }

    /**
     * @see at.spardat.xma.mdl.WModel#commit()
     */
    public void commit() {
        savedValue_ = null;
    }

    /**
     * @see at.spardat.xma.mdl.WModel#handle(ModelChangeEvent)
     */
    public boolean handle (ModelChangeEvent event) {
        return event.execute();
    }

    /**
     * @see at.spardat.xma.mdl.WModel#rollback()
     */
    public void rollback() {
        if (savedValue_ != null) {
            value_ = savedValue_;
            savedValue_ = null;
            handle (new ChangedEvent (false));
        }
    }

    /**
     * @see at.spardat.xma.mdl.Synchronization#externalize(at.spardat.xma.serializer.XmaOutput, boolean)
     */
    public void externalize (XmaOutput xo, boolean forceFull) throws IOException {
        // the content
        xo.writeString("val", value_);
    }

    /**
     * @see at.spardat.xma.mdl.Synchronization#internalize(at.spardat.xma.serializer.XmaInput)
     */
    public void internalize (XmaInput in) throws IOException, ClassNotFoundException {
        value_ = in.readString ();
        // discard history
        savedValue_ = null;
        handle (new ChangedEvent (false));
    }



    /**
     * This method requires that a legal internal representation has been constructed and manages
     * the state in value_ and savedValue_. If there is no saved state (savedValue_ equals null) and
     * the provided value is different, the state is saved before. If there is a value provided
     * which equals the saved state, the saved state is cleared. Either way, the provided value
     * becomes the one stored in instance variable value_.
     *
     * @param value the new value to set
     */
    protected void setInternal (String value) {
        if (value == null) value = "";
        if (value_.equals(value)) return;
        if (savedValue_ == null) {
            // no save state
            savedValue_ = value_;
            value_ = value;
        } else {
            // there is a saved state
            if (savedValue_.equals(value)) {
                // mark this as unchanged
                savedValue_ = null;
            }
            value_ = value;
        }
    }

    /**
     * @see at.spardat.xma.mdl.util.Descriptive#describe(at.spardat.xma.mdl.util.DNode)
     */
    public void describe (DNode n) {
        super.describe(n);
        n.app("type", Atom.type2String(type_)).comma().app("value", value_).comma().app("saved", savedValue_).comma().app("changed", savedValue_ != null);
    }

    /**
     * Makes random changes to this
     */
    public void randomlyChange () {
        if (Assert.ON) {
            if (TestUtil.draw(0.1)) clear();
            else {
                if (type_ == Types.T_BOOLEAN) set (TestUtil.draw(0.5));
                else if (type_ == Types.T_STRING) set (TestUtil.randomString(TestUtil.randomInt(0, 10)));
                else if (type_ == Types.T_BCD) set (TestUtil.randomDouble(-1000, 1000), TestUtil.randomInt(0, 5));
                else if (type_ == Types.T_DATE) set(new java.util.Date());
            }
        }
    }

    /**
     * @see at.spardat.xma.mdl.WModel#equalsCS(at.spardat.xma.mdl.WModel, int)
     */
    public void equalsCS (WModel mServer, int syncPoint) {
        if (Assert.ON) {
            SimpleWM ms = (SimpleWM) mServer;
            if (!value_.equals(ms.value_)) throw new RuntimeException();
        }
    }

    /**
     * Estimates the number of bytes this object consumes in memory.
     */
    public int estimateMemory () {
        return MemoryEstimator.sizeOfObject(4) + MemoryEstimator.sizeOf(value_);
    }

    /**
     * @see at.spardat.xma.mdl.IAtomic#getEncodedValue()
     */
    public String getEncodedValue () {
        return value_;
    }







    /**
     * Programmer initiated change of T_STRING typed SimpleWM
     */
    class StringChangeEvent extends ModelChangeEvent {

        public StringChangeEvent (String newString) {
            super (SimpleWM.this, false);
            newString_ = newString;
        }

        /**
         * @see at.spardat.xma.mdl.ModelChangeEvent#execute()
         */
        public boolean execute() {
            if (newString_ == null) return false;
            if (type_ != Types.T_STRING) return false;
            setInternal(newString_);
            return true;
        }

        private String              newString_;
    }


    /**
     * Programmer initiated change of a T_BCD model providing a double
     */
    class BcdChangeEvent extends ModelChangeEvent {

        /**
         * Constructor.
         *
         * @param numNK number of digits after the comma to be used
         *         when the provided double is converted to a String
         *         (which has to be done since widget models are
         *         string encoded). If numNK
         *         equals -1, the number of the resulting places
         *         after the comma is unknown. Although the caller
         *         can be sure that the result does not consist of
         *         more then 15 significant digits after the comma.
         */
        public BcdChangeEvent (double value, int numNK) {
            super (SimpleWM.this, false);
            value_ = value;
            numNK_ = numNK;
        }

        /**
         * @see at.spardat.xma.mdl.ModelChangeEvent#execute()
         */
        public boolean execute() {
            if (type_ != Types.T_BCD) return false;
            if (Double.isInfinite(value_) || Double.isNaN(value_)) return false;
            setInternal(NumberUtil.double2String(value_));
            return true;
        }

        private double              value_;
        private int                 numNK_;
    }

    /**
     * Programmer initiated change of a T_BCD model providing a BigDecimal
     */
    class Bcd2ChangeEvent extends ModelChangeEvent {

        private BigDecimal          fBigDecimal;

        /**
         * Constructor.
         *
         * @param bigDecimal the BigDecimal to set. if null, this
         *         is cleared
         */
        public Bcd2ChangeEvent (BigDecimal bigDecimal) {
            super (SimpleWM.this, false);
            fBigDecimal = bigDecimal;
        }

        /**
         * @see at.spardat.xma.mdl.ModelChangeEvent#execute()
         */
        public boolean execute() {
            if (type_ != Types.T_BCD) return false;
            if (fBigDecimal == null) clear();
            else setInternal (fBigDecimal.toString());
            return true;
        }
    }

    /**
     * Change of T_BOOLEAN typed SimpleWM
     */
    class BooleanChangeEvent extends ModelChangeEvent {

        /**
         * Constructor
         *
         * @param value the new value
         * @param fromUI indicates whether the source of change is the ui or the programmer
         */
        public BooleanChangeEvent (boolean value, boolean fromUI) {
            super (SimpleWM.this, fromUI);
            value_ = value;
        }

        /**
         * @see at.spardat.xma.mdl.ModelChangeEvent#execute()
         */
        public boolean execute() {
            if (type_ != Types.T_BOOLEAN) return false;
            setInternal(value_ ? "J" : "N");
            return true;
        }

        private boolean             value_;
    }

    /**
     * Programmer initiated change of a T_DATE typed SimpleWM
     */
    class DateChangeEvent extends ModelChangeEvent {

        /**
         * Constructor
         */
        public DateChangeEvent (Date date) {
            super (SimpleWM.this, false);
            date_ = date;
        }

        /**
         * @see at.spardat.xma.mdl.ModelChangeEvent#execute()
         */
        public boolean execute() {
            if (date_ == null) return false;
            if (type_ != Types.T_DATE) return false;
            setInternal(DateUtil.date2Internal(date_));
            return true;
        }

        private Date                date_;

    }


    /**
     * Programmer initiated change of a T_TIMESTAMP typed SimpleWM
     */
    class TimeStampChangeEvent extends ModelChangeEvent {

        /**
         * Constructor
         */
        public TimeStampChangeEvent (Date date) {
            super (SimpleWM.this, false);
            date_ = date;
        }

        /**
         * @see at.spardat.xma.mdl.ModelChangeEvent#execute()
         */
        public boolean execute() {
            if (date_ == null) return false;
            if (type_ != Types.T_TIMESTAMP) return false;
            setInternal(TimeStampUtil.date2Internal(date_));
            return true;
        }

        private Date                date_;
    }


    /**
     * Resets the value of the model in order that !hasValue() holds
     * afterwards.
     *
     * @author YSD, 28.10.2003
     */
    class ClearEvent extends ModelChangeEvent {

        /**
         * Konstruktor
         */
        public ClearEvent () {
            super (SimpleWM.this, false);
        }

        /**
         * @see at.spardat.xma.mdl.ModelChangeEvent#execute()
         */
        public boolean execute () {
            setInternal ("");
            return true;
        }
    }

    /**
     * A event which is created if the user modifies text in a UI-control attached
     * to a SimpleUIDelegateClient.
     */
    class UserModifiedTextEvent extends ModelChangeEvent {

        /**
         * Constructor
         *
         * @param newText the new contents of the UI-control after the modification
         */
        public UserModifiedTextEvent (String newText) {
            super (SimpleWM.this, true);
            newText_ = newText;
        }

        /**
         * Reflects the new text in the Widget Model. If the new text is
         * not accepted by the Formatter, this method returns false.
         *
         * @see at.spardat.xma.mdl.ModelChangeEvent#execute()
         */
        public boolean execute() {
            SimpleWMClient            model = (SimpleWMClient) wModel_;
            if (!model.doUITransfer()) return false;
            // get formatter
            IFmt                     formatter = model.getFmtInternal();
            if (formatter == null) {
                // no formatter; set the text directly
                setInternal(newText_);
            } else {
                if (formatter.isOneWay()) return false;
                String              internal = null;
                try {
                    internal = formatter.parse(newText_);
                } catch (AParseException ex) {
                    parseException_ = ex;
                    return false;
                }
                setInternal(internal);
            }
            return true;
        }

        private String              newText_;

        private AParseException     parseException_;

    }

    /**
     * Indicates that something has changed without specifying details.
     *
     * @author YSD, 03.05.2003 19:13:09
     */
    class ChangedEvent extends Notification {
        public ChangedEvent (boolean fromUI) {
            super (SimpleWM.this, fromUI);
        }
    }

    /**
     * Event class used to notify the dynamic registration of a new SimpleWM.
     * @author gub
     * @since 2.1.0
     * @see Page#addWModel(WModel)
    */
    public static class NewSimpleWMEvent extends NewModelEvent {
    	byte modelType;

    	/** empty contructor for deserialization */
		public NewSimpleWMEvent() {}

		/**
		 * constructor which initializes the modelType
		 * @param modelType must be one of the type constants defined in {@link Types}
		 */
		public NewSimpleWMEvent(byte modelType) {
			this.modelType=modelType;
		}

		// see at.spardat.xma.mdl.NewModelEvent.getType()
        public byte getType() {
            return NewModelEventFactory.SimpleWM;
        }

		// see at.spardat.xma.mdl.NewModelEvent.createModel()
        public WModel createModel(short id,Page page) {
    		return new SimpleWM(id,modelType,page);
		}

		// see at.spardat.xma.mdl.NewModelEvent.serialize()
		public void serialize(XmaOutput out) throws IOException {
			super.serialize(out);
            out.writeByte("modelType",modelType);
        }

		// see at.spardat.xma.mdl.NewModelEvent.deserialize()
		public void deserialize(XmaInput in) throws IOException, ClassNotFoundException {
        	super.deserialize(in);
        	modelType=in.readByte();
        }

    }

    // see at.sparda.xma.mdl.WModel.createNewModelEvent()
	public NewModelEvent createNewModelEvent() {
		return new NewSimpleWMEvent(type_);
	}

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy