Package er.extensions.eof

Source Code of er.extensions.eof.ERXConstant

/*
* Copyright (C) NetStruxr, Inc. All rights reserved.
*
* This software is published under the terms of the NetStruxr
* Public Software License version 0.5, a copy of which has been
* included with this distribution in the LICENSE.NPL file.  */
package er.extensions.eof;

import java.math.BigDecimal;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

import org.apache.log4j.Logger;

import com.webobjects.foundation.NSArray;
import com.webobjects.foundation.NSData;
import com.webobjects.foundation.NSDictionary;
import com.webobjects.foundation.NSKeyValueCoding;
import com.webobjects.foundation.NSMutableArray;
import com.webobjects.foundation.NSPropertyListSerialization;

import er.extensions.foundation.ERXArrayUtilities;
import er.extensions.foundation.ERXStringUtilities;

/**
* General purpose constant class, useful when you want reference object that are not
* bytes or strings in the DB like what you get with the factory classes. <br />
* If you use objects of this class, you might be able to completely remove the EOSharedEditingContext
* (the google search term for "why does my app lock up").<br />
* <br>
* To use the Number constants, you need to add an entry <code>ERXConstantClassName=Test.Status</code> to the attribute's userInfo
* in question and your EO's class description needs to be a {@link ERXEntityClassDescription}, also
* you must enable the {@link er.extensions.jdbc.ERXJDBCAdaptor}.<br />
* <br>
* The String and Byte based constants can be used with a custom class type:<pre><code>
*
* ERCMailMessage.plist:
* ...
* {
*     columnName = MAIL_STATE_ID;
*     name = state;
*     prototypeName = osType;
*     adaptorValueConversionMethodName = value;
*     factoryMethodArgumentType = EOFactoryMethodArgumentIsNSString;
*     valueClassName = er.corebusinesslogic.ERCMailState;
*     valueFactoryMethodName = mailState;
* }
* ...
*
* public class ERCMailMessage extends EOGenericRecord {
* ...
*   public ERCMailState state() {
*       return (ERCMailState) storedValueForKey("state");
*   }
*  
*   public void setState(ERCMailState value) {
*       takeStoredValueForKey(value, "state");
*   }
* ...
* }
*
* public class ERCMailState extends ERXConstant.StringConstant {
*
*     public ERCMailState(String key, String name) {
*         super(key, name);
*     }
*    
*     public static ERCMailState mailState(String key) {
*      return (ERCMailState) constantForClassNamed(key, ERCMailState.class.getName());
*     }
*
*     public static ERCMailState EXCEPTION_STATE = new ERCMailState ("xcpt", "Exception");
*     public static ERCMailState READY_TO_BE_SENT_STATE = new ERCMailState("rtbs", "Ready to be sent");
*     public static ERCMailState SENT_STATE = new ERCMailState("sent", "Sent");
*     public static ERCMailState RECEIVED_STATE = new ERCMailState("rcvd", "Received");
*     public static ERCMailState WAIT_STATE = new ERCMailState("wait", "Wait");
*     public static ERCMailState PROCESSING_STATE = new ERCMailState("proc", "Processing");
* }
* </code></pre>
* <br />
* An example would be:
* <pre><code>
* public class Test extends EOGenericRecord {
*   // your "status" attribute need a userInfo entry
*   // "ERXConstantClassName" = "Test.Status";
*  // Normally, the class name would be "Test$Status", this form is used to help you use EOGenerator
*   public static class Status extends ERXConstant.NumberConstant {
*     protected Status(int value, String name) {
*       super(value, name);
*     }
*   }
*  
*   public Status OFF = new Status(0, "Off");
*   public Status ON = new Status(1, "On");
*  
*     public Test() {
*         super();
*     }
*
*     public Status status() {
*         return (Status)storedValueForKey("status");
*     }
*
*     public void setStatus(Constant aValue) {
*         takeStoredValueForKey(aValue, "status");
*     }
*    
*     public boolean isOn() {
*       return status() == ON;
*     }
* }
*
* Test test = (Test)EOUtilities.createAndInsertInstance(ec, "Test");
* test.setTest(Test.Status.OFF);
* test = (Test)EOUtilities.createAndInsertInstance(ec, "Test");
* test.setStatus(Test.Status.ON);
* ec.saveChanges();
*
* NSArray objects;
* NSArray all = EOUtilities.objectsForEntityNamed(ec, "Test");
* EOQualifier q;
*
* objects = EOUtilities.objectsMatchingKeyAndValue(ec, "Test", "status", Test.Status.OFF);
* log.info("Test.Status.OFF: " + objects);
* q = new EOKeyValueQualifier("status", EOQualifier.QualifierOperatorEqual, Test.Status.OFF);
* log.info("Test.Status.OFF: " + EOQualifier.filteredArrayWithQualifier(all, q));
*
* // this might be a problem: equal number values match in the DB, but not in memory
* objects = EOUtilities.objectsMatchingKeyAndValue(ec, "Test", "status", ERXConstant.OneInteger);
* log.info("Number.OFF: " + objects);
* q = new EOKeyValueQualifier("status", EOQualifier.QualifierOperatorEqual, ERXConstant.OneInteger);
* log.info("Number.OFF: " + EOQualifier.filteredArrayWithQualifier(all, q));
*
* // you can compare by equality
* test.getStatus() == Test.Status.ON
* </pre></code>
* Note that upon class initialization 2500 Integers will be created and cached, from 0 - 2499.
*/
public abstract class ERXConstant {
 
  private static final Logger log = Logger.getLogger(ERXConstant.class);

  /**
   * Holds the value store, grouped by class name.
   */
  private static final Map _store = new HashMap();

  /**
   * Retrieves all constants for the given class name ordered by value. An empty
   * NSArray is returned if the class isn't found.
   * @param clazzName
   */
  public static NSArray constantsForClassName(String clazzName) {
    synchronized (_store) {
      Map map = keyMap(clazzName, false);
      NSMutableArray array = new NSMutableArray(map.values().toArray());
      ERXArrayUtilities.sortArrayWithKey(array, "sortOrder");
      return array;
    }
  }

  public static interface Constant {
    public int sortOrder();
    public String name();
    public Object value();
  }
   
    /**
     * Retrieves the constant for the given class name and value. Null is returned
     * if either class or value isn't found.
     * @param value
     * @param clazzName
     */
    public static Constant constantForClassNamed(Object value, String clazzName) {
        synchronized (_store) {
            Map classMap = keyMap(clazzName, false);
            Constant result = (Constant) classMap.get(value);
            if(log.isDebugEnabled()) {
                log.debug("Getting " + result + " for " + clazzName + " and " + value);
            }
            return result;
        }
    }

  private static int globalSortOrder = 0;
  /**
   * Retrieves the key map for the class name.
   * @param name
   * @param create
   */
  private static Map keyMap(String name, boolean create) {
    Map map = (Map) _store.get(name);
    if(map == null) {
      if(create) {
        map = new HashMap();
        _store.put(name, map);
        name = name.replace('$', '.');
        _store.put(name, map);
      } else {
        map = Collections.EMPTY_MAP;
      }
    }
    return map;
  }
 
    private static void registerConstant(Object key, Constant value, Class clazz) {
        synchronized (_store) {
            String className = clazz.getName();
            Map classMap = keyMap(className, true);
            if(log.isDebugEnabled()) {
                log.debug("Putting " + key + " for " + className);
            }
            classMap.put(key, value);
        }
    }
   
  public static class NumberConstant extends Number implements Constant {
    /**
     * Do I need to update serialVersionUID?
     * See section 5.6 <cite>Type Changes Affecting Serialization</cite> on page 51 of the
     * <a href="http://java.sun.com/j2se/1.4/pdf/serial-spec.pdf">Java Object Serialization Spec</a>
     */
    private static final long serialVersionUID = 1L;

    /**
     * Holds the value.
     */
    private int _value;
   
    /**
     * Holds the name.
     */
    private String _name;
   
    /**
     * Holds the name.
     */
    private int _sortOrder;


    /**
     * Sets the value and puts the object in the store keyed by class name and value.
     * @param value
     */
    protected NumberConstant(int value, String name) {
      _value = value;
      _name = name;
      _sortOrder = globalSortOrder++;
            ERXConstant.registerConstant(integerForInt(value), this, getClass());
    }
   
    /**
     * Returns the sort order of the value.
     */
    public int sortOrder() {
      return _sortOrder;
    }

    /**
     * Number interface implementation, returns the value.
     */
    @Override
    public final double doubleValue() {
      return intValue();
    }

    /**
     * Number interface implementation, returns the value.
     */
    @Override
    public final float floatValue() {
      return intValue();
    }

    /**
     * Number interface implementation, returns the value.
     */
    @Override
    public final int intValue() {
      return _value;
    }

    /**
     * Number interface implementation, returns the value.
     */
    @Override
    public final long longValue() {
      return intValue();
    }

    /**
     * Returns the value.
     */
    @Override
    public final int hashCode() {
      return _value;
    }

   
    public String name() {
      return _name;
    }
   
    public String userPresentableDescription() {
      return name() + " (" + intValue() ")";
    }
   
    @Override
    public String toString() {
      return getClass().getName() + ": " + userPresentableDescription();
    }

    public Number value() {
      return integerForInt(intValue());
    }
   
    /**
     * Overridden to compare by value.
     */
    @Override
    public final boolean equals(Object otherObject) {
      if(otherObject == null) {
        return false;
      }/* AK: we would violate the equals contract here, but we may need this with D2W later?
    if((otherObject instanceof Number) && !(otherObject instanceof ERXConstant)) {
      return ((Number)otherObject).intValue() == intValue();
    }*/
      if(otherObject.getClass() != getClass()) {
        return false;
      }
      return ((NumberConstant)otherObject).intValue() == intValue();
    }

    /**
     * Retrieves the constant for the given class name and value. Null is returned
     * if either class or value isn't found. Note that in case of inner classes, the
       * name should be <code>Test.Status</code>, not <code>Test$Status</code>.
     * @param value
     * @param clazzName
     */
    public static NumberConstant constantForClassNamed(int value, String clazzName) {
      return constantForClassNamed(integerForInt(value), clazzName);
    }
   
    /**
     * Retrieves the constant for the given class name and value. Null is returned
     * if either class or value isn't found.
     * @param value
     * @param clazzName
     */
    public static NumberConstant constantForClassNamed(Number value, String clazzName) {
            return (NumberConstant) ERXConstant.constantForClassNamed(value, clazzName);
    }
   
  }
 
  /**
   * Constant class that can be used with strings in the DB.
   * @author ak
   *
   */
  public static abstract class StringConstant implements Constant {
    private String _value;
    private String _name;
    private int _sortOrder;
   
    public StringConstant(String value, String name) {
      _value = value;
      _name = name;
      _sortOrder = globalSortOrder++;
            ERXConstant.registerConstant(value, this, getClass());
    }
   
    public String name() {
      return _name;
    }
   
    public int sortOrder() {
      return _sortOrder;
    }
   
    public String value() {
      return _value;
    }
   
    public String userPresentableDescription() {
      return name() + " (" + value() ")";
    }
   
    @Override
    public String toString() {
      return getClass().getName() + ": " + userPresentableDescription();
    }
   
    /**
     * Retrieves the constant for the given class name and value. Null is returned
     * if either class or value isn't found.
     * @param value
     * @param clazzName
     */
    public static StringConstant constantForClassNamed(String value, String clazzName) {
            return (StringConstant) ERXConstant.constantForClassNamed(value, clazzName);
    }
  }
 
  /**
   * Constant class that can be used with bytes or NSData in the DB.
   * @author ak
   *
   */
  public static abstract class ByteConstant implements Constant {
    private NSData _value;
    private String _name;
    private int _sortOrder;
   
    public ByteConstant(String value, String name) {
      //AK: making a lot of assumptions here... the value is a <hex data>, the result is a valid NSData
      this((NSData)NSPropertyListSerialization.propertyListFromString(value.toString()),name);
    }
   
    public ByteConstant(byte value[], String name) {
      this(new NSData(value),name);
    }
   
    public ByteConstant(NSData value, String name) {
      _value = value;
      _name = name;
      _sortOrder = globalSortOrder++;
            ERXConstant.registerConstant(value, this, getClass());
    }
   
    public String name() {
      return _name;
    }
   
    public int sortOrder() {
      return _sortOrder;
    }
   
    public NSData value() {
      return _value;
    }
   
    public String userPresentableDescription() {
      return name();
    }
   
    @Override
    public String toString() {
      return getClass().getName() + ": " + userPresentableDescription();
    }
       
        /**
         * Retrieves the constant for the given class name and value. Null is returned
         * if either class or value isn't found.
         * @param value
         * @param clazzName
         */
        public static ByteConstant constantForClassNamed(NSData value, String clazzName) {
            return (ByteConstant) ERXConstant.constantForClassNamed(value, clazzName);
        }
       
        /**
         * Retrieves the constant for the given class name and value. Null is returned
         * if either class or value isn't found.
         * @param value
         * @param clazzName
         */
        public static ByteConstant constantForClassNamed(byte value[], String clazzName) {
            return (ByteConstant) ERXConstant.constantForClassNamed(new NSData(value), clazzName);
        }
  }

    public static final int MAX_INT=2500;
   
    protected static Integer[] INTEGERS=new Integer[MAX_INT];
    static {
        for (int i=0; i<MAX_INT; i++) INTEGERS[i]=Integer.valueOf(i);
    }

    public static final Object EmptyObject = new Object();
    public static final String EmptyString = "";
    public static final NSArray EmptyArray = NSArray.EmptyArray;
    public static final NSArray SingleNullValueArray = new NSArray(NSKeyValueCoding.NullValue);
    public static final NSDictionary EmptyDictionary = NSDictionary.EmptyDictionary;
    public static final Integer MinusOneInteger = Integer.valueOf(-1);
    public static final Integer OneInteger = integerForInt(1);
    public static final Integer ZeroInteger = integerForInt(0);
    public static final Integer TwoInteger = integerForInt(2);
    public static final Integer ThreeInteger = integerForInt(3);
    public static final BigDecimal ZeroBigDecimal = new BigDecimal(0.00);
    public static final BigDecimal OneBigDecimal = new BigDecimal(1.00);
    public static final Class[] EmptyClassArray = new Class[0];
    public static final Class[] NotificationClassArray = { com.webobjects.foundation.NSNotification.class };
    public static final Class[] ObjectClassArray = { Object.class };
    public static final Class[] StringClassArray = new Class[] { String.class };
    public static final Object[] EmptyObjectArray = new Object[] {};
    /** an empty gif image */
    public static final NSData EmptyImage = (NSData) NSPropertyListSerialization.propertyListFromString("<47494638396101000100800000ffffff00000021f90401000000002c00000000010001000002024401003b00>");
   
    /**
     * Returns an Integer for a given int
     * @return potentially cache Integer for a given int
     */
    public static Integer integerForInt(int i) {
        return (i>=0 && i<MAX_INT) ? INTEGERS[i] : Integer.valueOf(i);
    }

    /**
     * Returns an Integer for a given String
     * @throws NumberFormatException forwarded from the
     *    parseInt method off of Integer
     * @return potentially cache Integer for a given String
     *
     * @deprecated use {@link ERXStringUtilities#integerWithString(String)}
     */
    @Deprecated
    public static Integer integerForString(String s) throws NumberFormatException {
        return integerForInt(Integer.parseInt(s));
    }
}
TOP

Related Classes of er.extensions.eof.ERXConstant

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.