Initial commit from SVN.
This commit is contained in:
41
Foundation/src/com/foundation/view/AbstractDecoration.java
Normal file
41
Foundation/src/com/foundation/view/AbstractDecoration.java
Normal file
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright (c) 2007,2008 Declarative Engineering LLC.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Declarative Engineering LLC
|
||||
* verson 1 which accompanies this distribution, and is available at
|
||||
* http://declarativeengineering.com/legal/DE_Developer_License_v1.txt
|
||||
*/
|
||||
package com.foundation.view;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import com.common.io.IExternalizable;
|
||||
import com.common.io.IObjectInputStream;
|
||||
import com.common.io.IObjectOutputStream;
|
||||
|
||||
/*
|
||||
* The base class for the decorators.
|
||||
*/
|
||||
public class AbstractDecoration implements IExternalizable {
|
||||
public static final int POSITION_UPPER_LEFT = 0;
|
||||
public static final int POSITION_UPPER_RIGHT = 1;
|
||||
public static final int POSITION_LOWER_LEFT = 2;
|
||||
public static final int POSITION_LOWER_RIGHT = 3;
|
||||
/**
|
||||
* AbstractDecoration constructor.
|
||||
*/
|
||||
public AbstractDecoration() {
|
||||
super();
|
||||
}//AbstractDecoration()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.common.io.IExternalizable#readExternal(com.common.io.IObjectInputStream)
|
||||
*/
|
||||
public Object readExternal(IObjectInputStream in) throws IOException, ClassNotFoundException {
|
||||
return null;
|
||||
}//readExternal()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.common.io.IExternalizable#writeExternal(com.common.io.IObjectOutputStream)
|
||||
*/
|
||||
public void writeExternal(IObjectOutputStream out) throws IOException {
|
||||
}//writeExternal()//
|
||||
}//AbstractDecoration//
|
||||
152
Foundation/src/com/foundation/view/AbstractFormat.java
Normal file
152
Foundation/src/com/foundation/view/AbstractFormat.java
Normal file
@@ -0,0 +1,152 @@
|
||||
package com.foundation.view;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectInput;
|
||||
import java.io.ObjectOutput;
|
||||
import java.text.DateFormat;
|
||||
import java.text.DecimalFormat;
|
||||
import java.text.Format;
|
||||
import java.text.NumberFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.TimeZone;
|
||||
|
||||
/**
|
||||
* Copyright Declarative Engineering LLC 2009<p>
|
||||
* The base class for format types.
|
||||
*/
|
||||
public abstract class AbstractFormat implements IFormat {
|
||||
/**
|
||||
* AbstractFormat constructor.
|
||||
*/
|
||||
public AbstractFormat() {
|
||||
}//AbstractFormat()//
|
||||
/**
|
||||
* Converts the format text into format source code.
|
||||
* <p>Note: This code is a bit brittle. It would be nice if it wasn't so rigid.</p>
|
||||
* @param format The format text.
|
||||
* The format property takes the format type (number, date) followed by space separated parameters that may be optionally placed in quotes ("" or '').
|
||||
* The format type "number" is followed by the named pattern (number, integer, percent, or currency) or the custom pattern (follows the java.text.DecimalFormat syntax).
|
||||
* The format type "date" is followed by the timezone (a value of "local" and all dates are displayed in local time), followed by the named pattern {starting with one of [date, time, or datetime] followed by one or two (optional for datetime) [short, medium, long, full] separated by dashes (-)} or the custom pattern {follows the java.text.SimpleDateFormat syntax}.
|
||||
* @return The source code to create the java.text.Format instance.
|
||||
*/
|
||||
public static IFormat createFormat(String format) {
|
||||
IFormat result = null;
|
||||
String type = null;
|
||||
int index = format.indexOf(' ');
|
||||
|
||||
if(index > 1) {
|
||||
type = format.substring(0, index);
|
||||
format = format.substring(index + 1).trim();
|
||||
|
||||
if(type.equalsIgnoreCase("number")) {
|
||||
if((format.startsWith("'") && format.endsWith("'")) || (format.startsWith("\"") && format.endsWith("\""))) {
|
||||
format = format.substring(1, format.length() - 1);
|
||||
}//if//
|
||||
|
||||
if(format.length() > 0) {
|
||||
if(format.equalsIgnoreCase("number")) {
|
||||
result = new FormatNumber(FormatNumber.TYPE_NUMBER);
|
||||
}//if//
|
||||
else if(format.equalsIgnoreCase("integer")) {
|
||||
result = new FormatNumber(FormatNumber.TYPE_INTEGER);
|
||||
}//else if//
|
||||
else if(format.equalsIgnoreCase("currency")) {
|
||||
result = new FormatNumber(FormatNumber.TYPE_CURRENCY);
|
||||
}//else if//
|
||||
else if(format.equalsIgnoreCase("percent")) {
|
||||
result = new FormatNumber(FormatNumber.TYPE_PERCENT);
|
||||
}//else if//
|
||||
else {
|
||||
result = new FormatNumber(format);
|
||||
}//else//
|
||||
}//if//
|
||||
else {
|
||||
throw new RuntimeException("Expecting a format with space separated parameters starting with 'number', followed by either a predefined number format name like 'integer', 'percent' or 'currency', or a custom format pattern following java.text.DecimalFormat format.");
|
||||
}//else//
|
||||
}//if//
|
||||
else if(type.equalsIgnoreCase("date")) {
|
||||
int index2 = format.indexOf(' ', index + 1);
|
||||
|
||||
if(index2 != -1) {
|
||||
String timeZoneText = format.substring(index + 1, index2);
|
||||
String pattern = format.substring(index2 + 1).trim();
|
||||
String[] predefinedParts = pattern.split("-");
|
||||
|
||||
if(timeZoneText.equalsIgnoreCase("local")) {
|
||||
timeZoneText = null;
|
||||
}//if//
|
||||
|
||||
if(predefinedParts[0].equalsIgnoreCase("date") && predefinedParts.length < 3) {
|
||||
if(predefinedParts.length == 2) {
|
||||
result = new FormatDate(FormatDate.TYPE_DATE, convertDateLength(predefinedParts[1]), timeZoneText);
|
||||
}//if//
|
||||
else {
|
||||
result = new FormatDate(FormatDate.TYPE_DATE, FormatDate.TYPE_FORMAT_SHORT, timeZoneText);
|
||||
}//else//
|
||||
}//if//
|
||||
else if(predefinedParts[0].equalsIgnoreCase("time") && predefinedParts.length < 3) {
|
||||
if(predefinedParts.length == 2) {
|
||||
result = new FormatDate(FormatDate.TYPE_TIME, convertDateLength(predefinedParts[1]), timeZoneText);
|
||||
}//if//
|
||||
else {
|
||||
result = new FormatDate(FormatDate.TYPE_TIME, FormatDate.TYPE_FORMAT_SHORT, timeZoneText);
|
||||
}//else//
|
||||
}//else if//
|
||||
else if(predefinedParts[0].equalsIgnoreCase("datetime") && predefinedParts.length < 4) {
|
||||
if(predefinedParts.length == 3) {
|
||||
result = new FormatDate(FormatDate.TYPE_DATETIME, convertDateLength(predefinedParts[1]), convertDateLength(predefinedParts[2]), timeZoneText);
|
||||
}//if//
|
||||
else if(predefinedParts.length == 2) {
|
||||
result = new FormatDate(FormatDate.TYPE_DATETIME, convertDateLength(predefinedParts[1]), timeZoneText);
|
||||
}//else if//
|
||||
else {
|
||||
result = new FormatDate(FormatDate.TYPE_DATETIME, FormatDate.TYPE_FORMAT_SHORT, timeZoneText);
|
||||
}//else//
|
||||
}//else if//
|
||||
else {
|
||||
result = new FormatDate(pattern, timeZoneText);
|
||||
}//else//
|
||||
}//if//
|
||||
else {
|
||||
throw new RuntimeException("Expecting a format with space separated parameters starting with 'date', followed by either the time zone id or 'local', followed by either a predefined date format name like 'date-short', 'datetime-medium-full' or 'time-long', or a custom format pattern following java.text.SimpleDateFormat format.");
|
||||
}//else//
|
||||
}//else if//
|
||||
else {
|
||||
//TODO: Allow custom? Maybe this could be a qualified class name.
|
||||
throw new RuntimeException("Expecting a format type of either 'date' or 'number'.");
|
||||
}//else//
|
||||
}//if//
|
||||
else {
|
||||
throw new RuntimeException("Expecting a space separated format starting with either 'date' or 'number' followed by their parameters (time zone id or 'local' then predefined time format or custom pattern for the date format, or predefined number format or custom pattern for the number format).");
|
||||
}//else//
|
||||
|
||||
return result;
|
||||
}//createFormat()//
|
||||
/**
|
||||
* Converts the date format length text into an identifier.
|
||||
* @param lengthText
|
||||
* @return
|
||||
*/
|
||||
private static int convertDateLength(String lengthText) {
|
||||
int result;
|
||||
|
||||
if(lengthText.equalsIgnoreCase("short")) {
|
||||
result = FormatDate.TYPE_FORMAT_SHORT;
|
||||
}//if//
|
||||
else if(lengthText.equalsIgnoreCase("medium")) {
|
||||
result = FormatDate.TYPE_FORMAT_MEDIUM;
|
||||
}//else if//
|
||||
else if(lengthText.equalsIgnoreCase("long")) {
|
||||
result = FormatDate.TYPE_FORMAT_LONG;
|
||||
}//else if//
|
||||
else if(lengthText.equalsIgnoreCase("full")) {
|
||||
result = FormatDate.TYPE_FORMAT_FULL;
|
||||
}//else if//
|
||||
else {
|
||||
//TODO: Report error.
|
||||
result = FormatDate.TYPE_FORMAT_SHORT;
|
||||
}//else//
|
||||
|
||||
return result;
|
||||
}//convertDateLength()//
|
||||
}//AbstractFormat//
|
||||
@@ -0,0 +1,254 @@
|
||||
/*
|
||||
* Copyright (c) 2006,2009 Declarative Engineering LLC.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Declarative Engineering LLC
|
||||
* verson 1 which accompanies this distribution, and is available at
|
||||
* http://declarativeengineering.com/legal/DE_Developer_License_v1.txt
|
||||
*/
|
||||
package com.foundation.view;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectInput;
|
||||
import java.io.ObjectOutput;
|
||||
|
||||
import com.common.comparison.Comparator;
|
||||
import com.common.comparison.IComparator;
|
||||
import com.common.util.IHashSet;
|
||||
import com.common.util.IIterator;
|
||||
import com.common.util.LiteHashMap;
|
||||
import com.common.util.LiteHashSet;
|
||||
import com.foundation.view.resource.AbstractResourceService;
|
||||
import com.foundation.view.resource.IResourceListener;
|
||||
import com.foundation.view.resource.ResourceReference;
|
||||
|
||||
/**
|
||||
* Used by components that may receive either a static value or a reference to a resource.
|
||||
* This resource association will take the value, register listeners if it is a resource, get the actual value if it is a resource, and notify the component when the value is set or is changed.
|
||||
* <p><b>Note: It is probably a fair bit more efficient to use an AbstractResourceHolder for each element in collecton style control since the resource manager then performs all the indexing instead of having a middle man.</b></p>
|
||||
* @deprecated This is less efficient than having a resource holder for each input value and should not be used.
|
||||
*/
|
||||
public abstract class AbstractMultiResourceHolder {
|
||||
private LiteHashMap valueByRow = new LiteHashMap(100);
|
||||
private LiteHashSet resourceValueSet = new LiteHashSet(20, LiteHashSet.DEFAULT_LOAD_FACTOR, new IComparator() {
|
||||
public void writeExternal(ObjectOutput out) throws IOException {
|
||||
}//writeExternal()//
|
||||
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
|
||||
}//readExternal()//
|
||||
public int compare(Object value1, Object value2) {
|
||||
return value1 instanceof ResourceValue ? (value1.equals(value2) ? Comparator.EQUAL : Comparator.NOT_EQUAL) : (value2.equals(value1) ? Comparator.EQUAL : Comparator.NOT_EQUAL);
|
||||
}//compare()//
|
||||
public int hash(Object value) {
|
||||
return value.hashCode();
|
||||
}//hash()//
|
||||
}, LiteHashSet.STYLE_NO_DUPLICATES);
|
||||
/** The component that will be notified when the value changes. */
|
||||
private IResourceHolderComponent component;
|
||||
|
||||
private class ResourceValue implements IResourceListener {
|
||||
public Object value = null;
|
||||
public ResourceReference reference = null;
|
||||
public int referenceCounter = 1;
|
||||
public LiteHashSet rowSet = new LiteHashSet(20);
|
||||
|
||||
public ResourceValue(ResourceReference reference, Object row) {
|
||||
this.reference = reference;
|
||||
rowSet.add(row);
|
||||
|
||||
if(getResourceService() == null) {
|
||||
throw new RuntimeException("The current application has not defined the location of the resource service's metadata, or does not call to setup the resource service. No resource service is available to resolve resource references.");
|
||||
}//if//
|
||||
|
||||
this.value = getResourceService().getResourceValue(reference);
|
||||
}//ResourceValue()//
|
||||
/* (non-Javadoc)
|
||||
* @see java.lang.Object#equals(java.lang.Object)
|
||||
*/
|
||||
public boolean equals(Object value) {
|
||||
return ((value instanceof ResourceValue) && (((ResourceValue) value).reference.equals(reference))) || ((value instanceof ResourceReference) && (value.equals(reference)));
|
||||
}//equals()//
|
||||
/* (non-Javadoc)
|
||||
* @see java.lang.Object#hashCode()
|
||||
*/
|
||||
public int hashCode() {
|
||||
return reference.hashCode();
|
||||
}//hashCode()//
|
||||
/**
|
||||
* Removes a row from the resource value's set of referencing rows.
|
||||
* @param row The row to remove.
|
||||
* @return The number of rows still referencing this resource value.
|
||||
*/
|
||||
public int removeRow(Object row) {
|
||||
rowSet.remove(row);
|
||||
|
||||
return --referenceCounter;
|
||||
}//removeRow()//
|
||||
/**
|
||||
* Adds a row to the resource value's set of referencing rows.
|
||||
* @param row The row to add.
|
||||
*/
|
||||
public void addRow(Object row) {
|
||||
rowSet.add(row);
|
||||
}//addRow()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.view.resource.IResourceListener#resourcesChanged()
|
||||
*/
|
||||
public void resourcesChanged() {
|
||||
AbstractResourceService resourceService = getResourceService();
|
||||
Object value = resourceService.getResourceValue(reference);
|
||||
|
||||
if(!Comparator.equals(value, this.value)) {
|
||||
Object oldValue = this.value;
|
||||
|
||||
this.value = value;
|
||||
component.resourceHolderChanged(AbstractMultiResourceHolder.this, (IHashSet) rowSet, oldValue, value);
|
||||
}//if//
|
||||
}//resourcesChanged()//
|
||||
}//ResourceValue//
|
||||
/**
|
||||
* AbstractMultiResourceHolder constructor.
|
||||
* @param component The component that will be notified when the resource value changes.
|
||||
* @deprecated This is less efficient than having a resource holder for each input value and should not be used.
|
||||
*/
|
||||
public AbstractMultiResourceHolder(IResourceHolderComponent component) {
|
||||
super();
|
||||
this.component = component;
|
||||
}//AbstractMultiResourceHolder()//
|
||||
/**
|
||||
* Gets the component that the holder is servicing.
|
||||
* @return The component that is using this resource holder.
|
||||
*/
|
||||
protected IResourceHolderComponent getComponent() {
|
||||
return component;
|
||||
}//getComponent()//
|
||||
/**
|
||||
* Gets the current value for the holder.
|
||||
* @return The current value which will either be the static value last set, or the last set resource reference's referenced value.
|
||||
*/
|
||||
public Object getValue(Object row) {
|
||||
Object result = valueByRow.get(row);
|
||||
|
||||
if(result instanceof ResourceValue) {
|
||||
result = ((ResourceValue) result).value;
|
||||
}//if//
|
||||
|
||||
return result;
|
||||
}//getValue()//
|
||||
/**
|
||||
* Sets the current holder's value.
|
||||
* <p>Note: This method will call the resource holder changed event with the new value and the old value.</p>
|
||||
* @param row The row whose value is being set.
|
||||
* @param value The value which may be a JefResourceReference which would be resolved to a resource value.
|
||||
*/
|
||||
public void setValue(Object row, Object value) {
|
||||
setValue(row, value, false);
|
||||
}//setValue()//
|
||||
/**
|
||||
* Sets the current holder's value.
|
||||
* <p>Note: This method will call the resource holder changed event with the new value and the old value.</p>
|
||||
* @param row The row whose value is being set.
|
||||
* @param value The value which may be a JefResourceReference which would be resolved to a resource value.
|
||||
* @param suspendEvents Whether the events normally fired by the changing of the value will be suspended.
|
||||
*/
|
||||
public void setValue(Object row, Object value, boolean suspendEvents) {
|
||||
Object oldValue = valueByRow.get(row);
|
||||
|
||||
if(!Comparator.equals(value, oldValue)) {
|
||||
AbstractResourceService resourceService = getResourceService();
|
||||
|
||||
if(oldValue instanceof ResourceValue) {
|
||||
if(((ResourceValue) oldValue).removeRow(row) == 0) {
|
||||
resourceValueSet.remove(oldValue);
|
||||
resourceService.removeListener((ResourceValue) oldValue);
|
||||
}//if//
|
||||
|
||||
oldValue = ((ResourceValue) oldValue).value;
|
||||
}//if//
|
||||
|
||||
if(value instanceof ResourceReference) {
|
||||
ResourceValue resourceValue = (ResourceValue) resourceValueSet.get(value);
|
||||
|
||||
if(resourceValue == null) {
|
||||
resourceValue = new ResourceValue((ResourceReference) value, row);
|
||||
resourceService.addListener(resourceValue);
|
||||
resourceValueSet.add(resourceValue);
|
||||
}//if//
|
||||
else {
|
||||
resourceValue.addRow(row);
|
||||
}//else//
|
||||
|
||||
valueByRow.put(row, resourceValue);
|
||||
|
||||
if(!suspendEvents) {
|
||||
component.resourceHolderChanged(this, row, oldValue, resourceValue.value);
|
||||
}//if//
|
||||
}//if//
|
||||
else {
|
||||
valueByRow.put(row, value);
|
||||
|
||||
if(!suspendEvents) {
|
||||
component.resourceHolderChanged(this, row, oldValue, value);
|
||||
}//if//
|
||||
}//else//
|
||||
}//if//
|
||||
}//setValue()//
|
||||
/**
|
||||
* Removes a row from the holder.
|
||||
*/
|
||||
public void remove(Object row) {
|
||||
Object oldValue = valueByRow.remove(row);
|
||||
|
||||
if(oldValue instanceof ResourceValue) {
|
||||
AbstractResourceService resourceService = getResourceService();
|
||||
|
||||
if(((ResourceValue) oldValue).removeRow(row) == 0) {
|
||||
resourceValueSet.remove(oldValue);
|
||||
resourceService.removeListener((ResourceValue) oldValue);
|
||||
}//if//
|
||||
}//if//
|
||||
}//remove()//
|
||||
/**
|
||||
* Removes all rows from the holder.
|
||||
*/
|
||||
public void removeAll() {
|
||||
/* Easier to call release.
|
||||
AbstractResourceService resourceService = getResourceService();
|
||||
IList keys = new LiteList(valueByRow.getSize());
|
||||
|
||||
valueByRow.getKeys(keys);
|
||||
|
||||
for(int index = 0; index < keys.getSize(); index++) {
|
||||
Object row = keys.get(index);
|
||||
Object oldValue = valueByRow.remove(row);
|
||||
|
||||
if(oldValue instanceof ResourceValue) {
|
||||
if(((ResourceValue) oldValue).removeRow(row) == 0) {
|
||||
resourceValueSet.remove(oldValue);
|
||||
resourceService.removeListener((ResourceValue) oldValue);
|
||||
}//if//
|
||||
}//if//
|
||||
}//while//
|
||||
*/
|
||||
release();
|
||||
}//removeAll()//
|
||||
/**
|
||||
* Releases the resource holder and cleans up all the listeners.
|
||||
*/
|
||||
public void release() {
|
||||
AbstractResourceService resourceService = getResourceService();
|
||||
IIterator iterator = resourceValueSet.iterator();
|
||||
|
||||
while(iterator.hasNext()) {
|
||||
ResourceValue resourceValue = (ResourceValue) iterator.next();
|
||||
|
||||
resourceService.removeListener(resourceValue);
|
||||
}//while//
|
||||
|
||||
valueByRow.removeAll();
|
||||
resourceValueSet.removeAll();
|
||||
}//release()//
|
||||
/**
|
||||
* Gets the resource service associated with the application this holder exists under.
|
||||
* @return The application's resource service.
|
||||
*/
|
||||
protected abstract AbstractResourceService getResourceService();
|
||||
}//AbstractMultiResourceHolder//
|
||||
180
Foundation/src/com/foundation/view/AbstractResourceHolder.java
Normal file
180
Foundation/src/com/foundation/view/AbstractResourceHolder.java
Normal file
@@ -0,0 +1,180 @@
|
||||
/*
|
||||
* Copyright (c) 2006,2009 Declarative Engineering LLC.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Declarative Engineering LLC
|
||||
* verson 1 which accompanies this distribution, and is available at
|
||||
* http://declarativeengineering.com/legal/DE_Developer_License_v1.txt
|
||||
*/
|
||||
package com.foundation.view;
|
||||
|
||||
import com.common.comparison.Comparator;
|
||||
import com.foundation.view.resource.AbstractResourceService;
|
||||
import com.foundation.view.resource.IResourceListener;
|
||||
import com.foundation.view.resource.ResourceReference;
|
||||
|
||||
/*
|
||||
* Used by components that may receive either a static value or a reference to a resource.
|
||||
* This resource association will take the value, register listeners if it is a resource, get the actual value if it is a resource, and notify the component when the value is set or is changed.
|
||||
*/
|
||||
public abstract class AbstractResourceHolder implements IResourceListener {
|
||||
/** The current non-resource reference value. */
|
||||
private Object value = null;
|
||||
/** The current resource reference if the current value was derived from a resource. */
|
||||
private ResourceReference reference = null;
|
||||
/** The component that will be notified when the value changes. */
|
||||
private IResourceHolderComponent component;
|
||||
/** Whether the currently held value has been invalidated by the using code. If the value is invalid that means the control has changed the value internally, so future updates should always be passed through even if they match the previous value. This solves the problem where by a text widget is modified and then the model changes the value back, where in the holder for the text says nothing has changed so the widget never updates with the new value. */
|
||||
private boolean invalidValue = false;
|
||||
/**
|
||||
* AbstractResourceHolder constructor.
|
||||
* @param component The component that will be notified when the resource value changes.
|
||||
*/
|
||||
public AbstractResourceHolder(IResourceHolderComponent component) {
|
||||
super();
|
||||
this.component = component;
|
||||
}//AbstractResourceHolder()//
|
||||
/**
|
||||
* Sets the invalid value flag such that the next update to the holder's value (call to setValue(..)) always passes the value through to the component regardless of whether it matches the previously set value.
|
||||
* This solves the problem where by a text widget is modified and then the model changes the value back, where in the holder for the text says nothing has changed so the widget never updates with the new value.
|
||||
*/
|
||||
public void invalidateValue() {
|
||||
invalidValue = true;
|
||||
}//invalidateValue()//
|
||||
/**
|
||||
* Gets the latest input value for the holder.
|
||||
* @return The current holder input.
|
||||
*/
|
||||
public Object getInput() {
|
||||
return reference != null ? reference : value;
|
||||
}//getInput()//
|
||||
/**
|
||||
* Gets the component that the holder is servicing.
|
||||
* @return The component that is using this resource holder.
|
||||
*/
|
||||
protected IResourceHolderComponent getComponent() {
|
||||
return component;
|
||||
}//getComponent()//
|
||||
/**
|
||||
* Gets the current value for the holder.
|
||||
* @return The current value which will either be the static value last set, or the last set resource reference's referenced value.
|
||||
*/
|
||||
public Object getValue() {
|
||||
return value;
|
||||
}//getValue()//
|
||||
/**
|
||||
* Sets the current holder's value.
|
||||
* <p>Note: This method will call the resource holder changed event with the new value and the old value.</p>
|
||||
* @param value The value which may be a JefResourceReference which would be resolved to a resource value.
|
||||
*/
|
||||
public void setValue(Object value) {
|
||||
setValue(value, true, 0);
|
||||
}//setValue()//
|
||||
/**
|
||||
* Sets the current holder's value.
|
||||
* <p>Note: This method will call the resource holder changed event with the new value and the old value.</p>
|
||||
* @param value The value which may be a JefResourceReference which would be resolved to a resource value.
|
||||
*/
|
||||
public void setValue(Object value, int flags) {
|
||||
setValue(value, true, flags);
|
||||
}//setValue()//
|
||||
/**
|
||||
* Sets the current holder's value.
|
||||
* <p>Note: This method will call the resource holder changed event with the new value and the old value.</p>
|
||||
* @param value The value which may be a JefResourceReference which would be resolved to a resource value.
|
||||
* @param fireEvents Whether the holder's listeners should be notified of the value change. This is normally true.
|
||||
*/
|
||||
public void setValue(Object value, boolean fireEvents) {
|
||||
setValue(value, fireEvents, 0);
|
||||
}//setValue()//
|
||||
/**
|
||||
* Sets the current holder's value.
|
||||
* <p>Note: This method will call the resource holder changed event with the new value and the old value.</p>
|
||||
* @param value The value which may be a JefResourceReference which would be resolved to a resource value.
|
||||
* @param fireEvents Whether the holder's listeners should be notified of the value change. This is normally true.
|
||||
* @param flags The flags for the value change. These flags are always the same flags passed by the resource association which feeds the resource holder. Flag options are defined by ISingleResourceAssociationChangeListener.
|
||||
*/
|
||||
public void setValue(Object value, boolean fireEvents, int flags) {
|
||||
AbstractResourceService resourceService = getResourceService();
|
||||
|
||||
if(value instanceof ResourceReference) {
|
||||
if(!Comparator.equals(value, reference)) {
|
||||
Object oldValue = this.value;
|
||||
|
||||
//Unregister the old resource reference if there is one.//
|
||||
if(reference != null && value == null) {
|
||||
resourceService.removeListener(this);
|
||||
reference = null;
|
||||
}//if//
|
||||
else if(reference == null && value != null) {
|
||||
//Store the new resource reference so we can unregister the listener later.//
|
||||
reference = (ResourceReference) value;
|
||||
//Register the resource reference.//
|
||||
resourceService.addListener(this);
|
||||
}//else if//
|
||||
else {
|
||||
//Store the new resource reference so we can unregister the listener later.//
|
||||
reference = (ResourceReference) value;
|
||||
}//else//
|
||||
|
||||
//Get actual value and set it as the current value.//
|
||||
value = resourceService.getResourceValue(reference);
|
||||
//Notify the listeners of the change and update the value.//
|
||||
this.value = value;
|
||||
|
||||
if(fireEvents) {
|
||||
component.resourceHolderChanged(this, oldValue, value, flags);
|
||||
}//if//
|
||||
}//if//
|
||||
}//if//
|
||||
else {
|
||||
//Unregister the old resource reference if there is one.//
|
||||
if(reference != null) {
|
||||
resourceService.removeListener(this);
|
||||
reference = null;
|
||||
}//if//
|
||||
|
||||
if(fireEvents) {
|
||||
//If the value has changed then save the value and notify listeners.//
|
||||
if(invalidValue || !Comparator.equals(value, this.value)) {
|
||||
Object oldValue = this.value;
|
||||
|
||||
this.value = value;
|
||||
invalidValue = false;
|
||||
component.resourceHolderChanged(this, oldValue, value, flags);
|
||||
}//if//
|
||||
}//if//
|
||||
else {
|
||||
this.value = value;
|
||||
}//else//
|
||||
}//else//
|
||||
}//setValue()//
|
||||
/**
|
||||
* Releases the resource holder and cleans up all the listeners.
|
||||
*/
|
||||
public void release() {
|
||||
AbstractResourceService resourceService = getResourceService();
|
||||
|
||||
if((resourceService != null) && (reference != null)) {
|
||||
resourceService.removeListener(this);
|
||||
}//if//
|
||||
}//release()//
|
||||
/**
|
||||
* Gets the resource service associated with the application this holder exists under.
|
||||
* @return The application's resource service.
|
||||
*/
|
||||
protected abstract AbstractResourceService getResourceService();
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.view.resource.IResourceListener#resourcesChanged()
|
||||
*/
|
||||
public void resourcesChanged() {
|
||||
AbstractResourceService resourceService = getResourceService();
|
||||
Object value = resourceService.getResourceValue(reference);
|
||||
|
||||
if(!Comparator.equals(value, this.value)) {
|
||||
Object oldValue = this.value;
|
||||
|
||||
this.value = value;
|
||||
component.resourceHolderChanged(this, oldValue, value, 0);
|
||||
}//if//
|
||||
}//resourcesChanged()//
|
||||
}//AbstractResourceHolder//
|
||||
491
Foundation/src/com/foundation/view/Association.java
Normal file
491
Foundation/src/com/foundation/view/Association.java
Normal file
@@ -0,0 +1,491 @@
|
||||
/*
|
||||
* Copyright (c) 2006,2009 Declarative Engineering LLC.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Declarative Engineering LLC
|
||||
* verson 1 which accompanies this distribution, and is available at
|
||||
* http://declarativeengineering.com/legal/DE_Developer_License_v1.txt
|
||||
*/
|
||||
package com.foundation.view;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
|
||||
import com.common.debug.Debug;
|
||||
import com.common.util.IHashSet;
|
||||
import com.common.util.IIterator;
|
||||
import com.foundation.event.EventSupport;
|
||||
import com.foundation.event.IEventEmitter;
|
||||
import com.foundation.event.IEventHandler;
|
||||
import com.foundation.util.IManagedCollection;
|
||||
import com.foundation.view.IAbstractContainer;
|
||||
import com.foundation.view.IAssociationHandler;
|
||||
import com.foundation.view.IValueHolder;
|
||||
import com.foundation.view.resource.ResourceReference;
|
||||
|
||||
/**
|
||||
* The base class for the single and multi association classes.
|
||||
*/
|
||||
public abstract class Association implements IResourceAssociationTypes {
|
||||
/** The container that contains this association. */
|
||||
private AssociationContainer associationContainer = null;
|
||||
/** The parent association which may be null if this is a root association. */
|
||||
private Association parent = null;
|
||||
/** The name of the value holder whose value will be used as input to the association. This may be null if a row value is to be passed to the association. */
|
||||
private String valueHolderName = null;
|
||||
/** The actual value holder instance at runtime. This will be null if the value holder has not yet been found, or if the value holder name is not set. */
|
||||
private IValueHolder valueHolder = null;
|
||||
/** The optional name of the type the association applies to. If there is a value holder name then this should be the value holder's type or a specialization thereof. */
|
||||
private Class valueHolderHeldType = null;
|
||||
/** The optional name of the type the association applies to. This can be used by single or multi associations and is non-null only if the association will be passed a row value. */
|
||||
private Class rowType = null;
|
||||
/** The view unique number for this association. A value of -1 indicates an invalid value. An invalid value may be used if the association refers to the input value as the output value. */
|
||||
private int associationNumber = -1;
|
||||
/** The handler which is invoked to get and set the attribute value. */
|
||||
private IAssociationHandler handler = null;
|
||||
/** Whether the association should invert boolean logic when getting and setting. This is ignored for associations not resulting in a boolean value (may be null). */
|
||||
private boolean invertLogic = false;
|
||||
//TODO: Are the attributes applied to the result (output) of the association or the input?
|
||||
/** The set of attributes to listen to on the result of the association and whose values are passed to the association nodes. This should be null except for indirect link associations and method target associations. */
|
||||
private AssociationAttribute[] attributes = null;
|
||||
//TODO: What are the initial inputs for the nodes? Are the attributes' values the inputs? Is the association's result (output) the input here? How about the association's input?
|
||||
/** The set of nodes that define an arbitrary tree of listeners which indicate that this association's result may have changed. This should be null except for indirect link associations and method target associations. */
|
||||
private AssociationNode[] nodes = null;
|
||||
/** A reusable array containing one value. */
|
||||
protected Object[] oneArray = null;
|
||||
/** A reusable array containing two values. */
|
||||
protected Object[] twoArray = null;
|
||||
/**
|
||||
* Association constructor.
|
||||
* @param rowType The type of row the association applies to. In the case of a root association in a single association this is the type held by the value holder.
|
||||
* @param associationNumber The association number which will be passed to the handler when the assocation is invoked. This number allows fast indexing of the methods that the handler handles. This value may be -1 if the input value should be used as the output value for the association (short circuiting).
|
||||
* @param handler The object that handles the method invocations to get and set the value.
|
||||
* @param invertLogic Whether the result of the association will be inverted. This is ignored unless the result is a Boolean value.
|
||||
* @param attributes The set of attributes to listen to and whose values are placed into the nodes to listen for change notification.
|
||||
* @param nodes The optional node data used to set up an arbitrary tree of event listeners for notification when the association's result changes.
|
||||
* @param valueHolderName The name of the optional value holder whose value holds the getter and setter methods used by the association. If no value holder is defined then the row object passed to the association must define the getter and setter methods.
|
||||
*/
|
||||
public Association(Class rowType, int associationNumber, IAssociationHandler handler, boolean invertLogic, AssociationAttribute[] attributes, AssociationNode[] nodes, String valueHolderName) {
|
||||
this.rowType = rowType;
|
||||
this.associationNumber = associationNumber;
|
||||
this.handler = handler;
|
||||
this.invertLogic = invertLogic;
|
||||
this.attributes = attributes;
|
||||
this.nodes = nodes;
|
||||
this.valueHolderName = valueHolderName;
|
||||
}//Association()//
|
||||
/**
|
||||
* Initializes the association.
|
||||
* @param associationContainer The container that contains this association.
|
||||
*/
|
||||
public void initialize(AssociationContainer associationContainer) {
|
||||
this.associationContainer = associationContainer;
|
||||
}//initialize()//
|
||||
/**
|
||||
* Releases the association.
|
||||
*/
|
||||
public void release() {
|
||||
this.associationContainer = null;
|
||||
}//release()//
|
||||
/**
|
||||
* Gets the association number used to index the associations by the association handler.
|
||||
* @return The number passed to the handler that identifies this association.
|
||||
*/
|
||||
protected int getAssociationNumber() {
|
||||
return associationNumber;
|
||||
}//getAssociationNumber()//
|
||||
/**
|
||||
* Gets the handler that is invoked when the get or set operations are required by the association.
|
||||
* @return The handler that calls the getter and setter methods for the association.
|
||||
*/
|
||||
protected IAssociationHandler getHandler() {
|
||||
return handler;
|
||||
}//getHandler()//
|
||||
/**
|
||||
* Inverts the logic of a boolean result.
|
||||
* @return Whether boolean results for the association should be inverted.
|
||||
*/
|
||||
public boolean invertLogic() {
|
||||
return invertLogic;
|
||||
}//invertLogic()//
|
||||
/**
|
||||
* Gets the parent association.
|
||||
* @return The parent association or null if this is a root association.
|
||||
*/
|
||||
protected Association getParent() {
|
||||
return parent;
|
||||
}//getParent()//
|
||||
/**
|
||||
* Sets the parent association.
|
||||
* @param parent The parent association or null if this is a root association.
|
||||
*/
|
||||
protected void setParent(Association parent) {
|
||||
this.parent = parent;
|
||||
}//setParent()//
|
||||
/**
|
||||
* Gets the container for this association.
|
||||
* @return The container containing this association.
|
||||
*/
|
||||
protected AssociationContainer getAssociationContainer() {
|
||||
return associationContainer;
|
||||
}//getAssociationContainer()//
|
||||
/**
|
||||
* Gets whether the value holder association is connected to a value holder by name.
|
||||
* @return Whether there is a potential value holder connection.
|
||||
*/
|
||||
public boolean getIsValueHolderAssociated() {
|
||||
return valueHolderName != null;
|
||||
}//getIsValueHolderAssociated()//
|
||||
/**
|
||||
* Gets the value holder associated with the component.
|
||||
* @return The associated value holder, or null if one was not located with the proper name.
|
||||
*/
|
||||
public IValueHolder getValueHolder() {
|
||||
if(valueHolder == null) {
|
||||
valueHolder = locateValueHolder();
|
||||
|
||||
if(valueHolder == null) {
|
||||
Debug.log("Warning: ValueHolder named " + valueHolderName + " could not be found for the view component named '" + getAssociationContainer().getResourceAssociation().getComponent().getName() + "'.");
|
||||
}//if//
|
||||
}//if//
|
||||
|
||||
return valueHolder;
|
||||
}//getValueHolder()//
|
||||
/**
|
||||
* Gets the class of object held by the value holder for this association.
|
||||
* @return The class of object expected to be held by the value holder when using this association.
|
||||
*/
|
||||
public Class getValueHolderHeldType() {
|
||||
return valueHolderHeldType;
|
||||
}//getValueHolderHeldType()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.view.IValueHolderAssociation#getRowType()
|
||||
*/
|
||||
public Class getRowType() {
|
||||
return rowType;
|
||||
}//getRowType()//
|
||||
/**
|
||||
* Locates the value holder by looking in the component's parents (and the component its self if it is a container) until a value holder with the given name is found.
|
||||
* <p>Note: The name is not case sensitive.</p>
|
||||
* @return The found value holder, or null if one could not be found.
|
||||
*/
|
||||
protected IValueHolder locateValueHolder() {
|
||||
IValueHolder result = null;
|
||||
IAbstractContainer container = getAssociationContainer().getResourceAssociation().getContainer();
|
||||
|
||||
while((result == null) && (container != null)) {
|
||||
IIterator iterator = container.getComponents().iterator();
|
||||
|
||||
while((result == null) && (iterator.hasNext())) {
|
||||
Object component = iterator.next();
|
||||
|
||||
if((component instanceof IValueHolder) && (((IValueHolder) component).getName().equalsIgnoreCase(valueHolderName))) {
|
||||
result = (IValueHolder) component;
|
||||
}//if//
|
||||
}//while//
|
||||
|
||||
container = container.getContainer();
|
||||
}//while//
|
||||
|
||||
return result;
|
||||
}//locateValueHolder()//
|
||||
/**
|
||||
* Determines whether the given row value is applicable to this association.
|
||||
* @param row The row value to compare with.
|
||||
* @return Whether this association supports the given row.
|
||||
*/
|
||||
public boolean isApplicable(Object row) {
|
||||
return row != null && getRowType().isAssignableFrom(row.getClass());
|
||||
}//isApplicable()//
|
||||
/**
|
||||
* Gets the set of attributes to listen to on the result of the association and whose values are passed to the association nodes.
|
||||
* This should be null except for indirect link associations and method target associations.
|
||||
* @return The set of attributes the result should look at when registering with the nodes.
|
||||
*/
|
||||
protected AssociationAttribute[] getAttributes() {
|
||||
return attributes;
|
||||
}//getAttributes()//
|
||||
/**
|
||||
* Gets the set of nodes that define an arbitrary tree of listeners which indicate that this association's result may have changed.
|
||||
* This should be null except for indirect link associations and method target associations.
|
||||
* @return The set of nodes that the result's attribute values and node attribute values will be registered with to create an arbitrary tree of event listeners.
|
||||
*/
|
||||
protected AssociationNode[] getNodes() {
|
||||
return nodes;
|
||||
}//getNodes()//
|
||||
/**
|
||||
* Registers listeners with the result.
|
||||
* @param eventSupport The event support used to register for events on the value.
|
||||
* @param trackedValues The values already registered. This is used to prevent a value from being registered multiple times.
|
||||
* @param handler The handler called if any listener fires.
|
||||
* @param value The value that should be plugged into the nodes.
|
||||
*/
|
||||
protected void registerListeners(EventSupport eventSupport, IHashSet trackedValues, IEventHandler handler, Object value) {
|
||||
AssociationNode[] nodes = getNodes();
|
||||
|
||||
if(nodes != null) {
|
||||
for(int nodeIndex = 0; nodeIndex < nodes.length; nodeIndex++) {
|
||||
if(nodes[nodeIndex].isApplicable(value)) {
|
||||
int[] events = nodes[nodeIndex].getEvents();
|
||||
AssociationAttribute[] attributes = nodes[nodeIndex].getAttributes();
|
||||
|
||||
if(events != null) {
|
||||
//Iterate over the defined events and register for changes on the value.//
|
||||
for(int eventIndex = 0; eventIndex < events.length; eventIndex++) {
|
||||
eventSupport.register((IEventEmitter) value, events[eventIndex], handler, true);
|
||||
}//for//
|
||||
}//if//
|
||||
|
||||
if(attributes != null) {
|
||||
//Iterate over the defined attributes and for each get the value and recursively call this registerListeners method.//
|
||||
for(int attributeIndex = 0; attributeIndex < attributes.length; attributeIndex++) {
|
||||
Object attributeValue = attributes[attributeIndex].getValue(value);
|
||||
|
||||
if(!trackedValues.containsValue(attributeValue)) {
|
||||
trackedValues.add(attributeValue);
|
||||
|
||||
//TODO: Should we support other collections? If so we would need to be notified when there are changes...
|
||||
if(attributeValue instanceof IManagedCollection && attributes[attributeIndex].getNavigateCollectionValues()) {
|
||||
IIterator iterator = ((IManagedCollection) attributeValue).iterator();
|
||||
|
||||
//Register for changes in the collected values.//
|
||||
while(iterator.hasNext()) {
|
||||
registerListeners(eventSupport, trackedValues, handler, iterator.next());
|
||||
}//while//
|
||||
|
||||
//Register for changes in the collection.//
|
||||
eventSupport.register((IManagedCollection) attributeValue, IManagedCollection.EVENT, handler, true);
|
||||
}//if//
|
||||
|
||||
//Register for changes in the attribute value.//
|
||||
registerListeners(eventSupport, trackedValues, handler, attributeValue);
|
||||
}//if//
|
||||
}//for//
|
||||
}//if//
|
||||
}//if//
|
||||
}//for//
|
||||
}//if//
|
||||
}//registerListeners()//
|
||||
/**
|
||||
* Ensures that the current thread is the view system thread.
|
||||
*/
|
||||
public void verifyThread() {
|
||||
associationContainer.verifyThread();
|
||||
}//verifyThread()//
|
||||
/**
|
||||
* Converts the given value into a properly typed value for the control, otherwise returns null if that was not possible.
|
||||
* <p>Note: The default value for the association is converted in the ResourceAssociation class.</p>
|
||||
* @param value The value which may not match the display value type identifier.
|
||||
* @return The matching value for the display value type.
|
||||
*/
|
||||
protected Object convertValueToControl(Object value) {
|
||||
Object currentValue = null;
|
||||
|
||||
if(value instanceof ResourceReference) {
|
||||
currentValue = value;
|
||||
}//if//
|
||||
else {
|
||||
switch(getAssociationContainer().getResourceAssociation().getValueType()) {
|
||||
case TYPE_TEXT: {
|
||||
if(value instanceof Number) {
|
||||
currentValue = getAssociationContainer().getResourceAssociation().defaultFormat.format(value);
|
||||
}//if//
|
||||
else {
|
||||
currentValue = value != null ? value.toString() : null;
|
||||
}//else//
|
||||
break;
|
||||
}//case//
|
||||
case TYPE_BOOLEAN: {
|
||||
if(value instanceof Boolean) {
|
||||
currentValue = (Boolean) value;
|
||||
}//if//
|
||||
else if((value instanceof Byte) || (value instanceof Short) || (value instanceof Integer) || (value instanceof Long)) {
|
||||
currentValue = ((Number) value).longValue() == 0 ? Boolean.FALSE : Boolean.TRUE;
|
||||
}//else if//
|
||||
else if((value instanceof Float) || (value instanceof Double)) {
|
||||
currentValue = ((Number) value).doubleValue() == 0 ? Boolean.FALSE : Boolean.TRUE;
|
||||
}//else if//
|
||||
else if(value instanceof IManagedCollection) {
|
||||
currentValue = ((IManagedCollection) value).getSize() > 0 ? Boolean.TRUE : Boolean.FALSE;
|
||||
}//else if//
|
||||
else {
|
||||
currentValue = value == null ? Boolean.FALSE : Boolean.TRUE;
|
||||
}//else//
|
||||
|
||||
if(invertLogic()) {
|
||||
currentValue = ((Boolean) currentValue).booleanValue() ? Boolean.FALSE : Boolean.TRUE;
|
||||
}//if//
|
||||
break;
|
||||
}//case//
|
||||
case TYPE_COLOR: {
|
||||
if(value instanceof JefColor) {
|
||||
currentValue = (JefColor) value;
|
||||
}//if//
|
||||
else if(value instanceof String) {
|
||||
currentValue = new JefColor((String) value);
|
||||
}//else if//
|
||||
break;
|
||||
}//case//
|
||||
case TYPE_FONT: {
|
||||
if(value instanceof JefFont[]) {
|
||||
currentValue = (JefFont[]) value;
|
||||
}//if//
|
||||
else if(value instanceof JefFont) {
|
||||
currentValue = new JefFont[] {(JefFont) value};
|
||||
}//else if//
|
||||
else if(value instanceof String) {
|
||||
currentValue = JefFont.getJefFonts((String) value);
|
||||
}//else if//
|
||||
break;
|
||||
}//case//
|
||||
case TYPE_IMAGE: {
|
||||
if(value instanceof JefImage) {
|
||||
currentValue = (JefImage) value;
|
||||
}//if//
|
||||
else if(value instanceof String) {
|
||||
currentValue = new JefImage((String) value);
|
||||
}//else if//
|
||||
break;
|
||||
}//case//
|
||||
case TYPE_IMAGES: {
|
||||
if(value instanceof JefImage[]) {
|
||||
currentValue = (JefImage[]) value;
|
||||
}//if//
|
||||
else if(value instanceof JefImage) {
|
||||
currentValue = new JefImage[] {(JefImage) value};
|
||||
}//else if//
|
||||
break;
|
||||
}//case//
|
||||
case TYPE_INTEGER: {
|
||||
if(value instanceof Integer) {
|
||||
currentValue = (Integer) value;
|
||||
}//if//
|
||||
else if(value instanceof String) {
|
||||
try {
|
||||
currentValue = Integer.valueOf((String) value);
|
||||
}//try//
|
||||
catch(Throwable e) {
|
||||
Debug.log(new RuntimeException("Could not convert the string '" + value.toString() + "' to an integer value."));
|
||||
}//catch//
|
||||
}//else if//
|
||||
else if(value instanceof Number) {
|
||||
currentValue = new Integer(((Number) value).intValue());
|
||||
}//else if//
|
||||
break;
|
||||
}//case//
|
||||
case TYPE_LONG: {
|
||||
if(value instanceof Long) {
|
||||
currentValue = (Long) value;
|
||||
}//if//
|
||||
else if(value instanceof String) {
|
||||
try {
|
||||
currentValue = Long.valueOf((String) value);
|
||||
}//try//
|
||||
catch(Throwable e) {
|
||||
Debug.log(new RuntimeException("Could not convert the string '" + value.toString() + "' to an long value."));
|
||||
}//catch//
|
||||
}//else if//
|
||||
else if(value instanceof Number) {
|
||||
currentValue = new Long(((Number) value).longValue());
|
||||
}//else if//
|
||||
break;
|
||||
}//case//
|
||||
case TYPE_OBJECT: {
|
||||
currentValue = value;
|
||||
break;
|
||||
}//case//
|
||||
case TYPE_FLOAT: {
|
||||
if(value instanceof Float) {
|
||||
currentValue = (Float) value;
|
||||
}//if//
|
||||
else if(value instanceof String) {
|
||||
try {
|
||||
currentValue = Float.valueOf((String) value);
|
||||
}//try//
|
||||
catch(Throwable e) {
|
||||
Debug.log(new RuntimeException("Could not convert the string '" + value.toString() + "' to a float value."));
|
||||
}//catch//
|
||||
}//else if//
|
||||
else if(value instanceof Number) {
|
||||
currentValue = new Float(((Number) value).floatValue());
|
||||
}//else if//
|
||||
break;
|
||||
}//case//
|
||||
case TYPE_DOUBLE: {
|
||||
if(value instanceof Double) {
|
||||
currentValue = (Double) value;
|
||||
}//if//
|
||||
else if(value instanceof String) {
|
||||
try {
|
||||
currentValue = Double.valueOf((String) value);
|
||||
}//try//
|
||||
catch(Throwable e) {
|
||||
Debug.log(new RuntimeException("Could not convert the string '" + value.toString() + "' to a double value."));
|
||||
}//catch//
|
||||
}//else if//
|
||||
else if(value instanceof Number) {
|
||||
currentValue = new Double(((Number) value).doubleValue());
|
||||
}//else if//
|
||||
break;
|
||||
}//case//
|
||||
case TYPE_DATE: {
|
||||
if(value instanceof Date) {
|
||||
currentValue = (Date) value;
|
||||
}//if//
|
||||
break;
|
||||
}//case//
|
||||
case TYPE_GRADIENT: {
|
||||
if(value instanceof JefColor) {
|
||||
currentValue = new JefGradient((JefColor) value, null, JefGradient.DIAGONAL);
|
||||
}//if//
|
||||
else if(value instanceof String) {
|
||||
currentValue = new JefGradient((String) value);
|
||||
}//else if//
|
||||
else if(value instanceof JefGradient) {
|
||||
currentValue = (JefGradient) value;
|
||||
}//else if//
|
||||
break;
|
||||
}//case//
|
||||
case TYPE_BIG_DECIMAL: {
|
||||
if(value instanceof BigDecimal) {
|
||||
currentValue = (BigDecimal) value;
|
||||
}//if//
|
||||
else {
|
||||
try {
|
||||
currentValue = new BigDecimal(value.toString());
|
||||
}//try//
|
||||
catch(Throwable e) {
|
||||
Debug.log(new RuntimeException("Could not convert the string '" + value.toString() + "' to a BigDecimal value."));
|
||||
}//catch//
|
||||
}//else//
|
||||
break;
|
||||
}//case//
|
||||
default: {
|
||||
throw new RuntimeException("Error: Invalid value type.");
|
||||
}//default//
|
||||
}//switch//
|
||||
}//else//
|
||||
|
||||
return currentValue;
|
||||
}//convertValueToControl()//
|
||||
/**
|
||||
* Converts the given value into a properly typed value for the model, otherwise returns null if that was not possible.
|
||||
* <p>Note: We don't currently store the model type so we will not try to convert to the model's type, but instead assume that it is the same as the control's type.</p>
|
||||
* @param value The value from the control.
|
||||
* @return The matching value for the model.
|
||||
*/
|
||||
protected Object convertValueToModel(Object value) {
|
||||
switch(getAssociationContainer().getResourceAssociation().getValueType()) {
|
||||
case TYPE_BOOLEAN: {
|
||||
if(invertLogic()) {
|
||||
value = ((Boolean) value).booleanValue() ? Boolean.FALSE : Boolean.TRUE;
|
||||
}//if//
|
||||
break;
|
||||
}//case//
|
||||
default: {
|
||||
//Do nothing.//
|
||||
}//default//
|
||||
}//switch//
|
||||
|
||||
return value;
|
||||
}//convertValueToModel()//
|
||||
}//Association//
|
||||
56
Foundation/src/com/foundation/view/AssociationAttribute.java
Normal file
56
Foundation/src/com/foundation/view/AssociationAttribute.java
Normal file
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright (c) 2006,2009 Declarative Engineering LLC.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Declarative Engineering LLC
|
||||
* verson 1 which accompanies this distribution, and is available at
|
||||
* http://declarativeengineering.com/legal/DE_Developer_License_v1.txt
|
||||
*/
|
||||
package com.foundation.view;
|
||||
|
||||
import com.foundation.metadata.Attribute;
|
||||
import com.foundation.view.IAssociationHandler;
|
||||
|
||||
public class AssociationAttribute {
|
||||
/** The view unique number for this association attribute. */
|
||||
private Attribute attribute = null;
|
||||
/** The handler which is invoked to get the attribute value. */
|
||||
private IAssociationHandler handler = null;
|
||||
/** The identifier defined by, and used by the handler to know which attribute is being reqested. */
|
||||
private int attributeIdentifier;
|
||||
/** Whether collection values should be navigated if the value of the attribute is a collection. */
|
||||
private boolean navigateCollectionValues = false;
|
||||
/**
|
||||
* AssociationAttribute constructor.
|
||||
* @param attribute The attribute.
|
||||
* @param attributeIdentifier The identifier defined by the view which is used to identify which attribute is being asked for when getting the value.
|
||||
* @param handler The handler called to get the attribute's value.
|
||||
*/
|
||||
public AssociationAttribute(Attribute attribute, int attributeIdentifier, IAssociationHandler handler, boolean navigateCollectionValues) {
|
||||
this.attribute = attribute;
|
||||
this.handler = handler;
|
||||
this.attributeIdentifier = attributeIdentifier;
|
||||
this.navigateCollectionValues = navigateCollectionValues;
|
||||
}//AssociationAttribute()//
|
||||
/**
|
||||
* Gets the attribute's changed event number.
|
||||
* @return The event number for the event fired when the attribute changes.
|
||||
*/
|
||||
public int getEventNumber() {
|
||||
return attribute.getNumber();
|
||||
}//getEventNumber()//
|
||||
/**
|
||||
* Gets the value for the attribute from the model.
|
||||
* @param row The row object that has the attribute.
|
||||
* @return The value of the attribute.
|
||||
*/
|
||||
public Object getValue(Object row) {
|
||||
return handler.invokeGetMethod(attributeIdentifier, row);
|
||||
}//getValue()//
|
||||
/**
|
||||
* Gets whether collection values should be navigated if the value of the attribute is a collection.
|
||||
* @return Whether to navigate collection values.
|
||||
*/
|
||||
public boolean getNavigateCollectionValues() {
|
||||
return navigateCollectionValues;
|
||||
}//getNavigateCollectionValues()//
|
||||
}//AssociationAttribute//
|
||||
43
Foundation/src/com/foundation/view/AssociationContainer.java
Normal file
43
Foundation/src/com/foundation/view/AssociationContainer.java
Normal file
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright (c) 2006,2007 Declarative Engineering LLC.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Declarative Engineering LLC
|
||||
* verson 1 which accompanies this distribution, and is available at
|
||||
* http://declarativeengineering.com/legal/DE_Developer_License_v1.txt
|
||||
*/
|
||||
package com.foundation.view;
|
||||
|
||||
import com.foundation.view.ResourceAssociation;
|
||||
|
||||
public class AssociationContainer {
|
||||
/** Used to indicate that no target association was found for a given row value. */
|
||||
public static final Object NO_VALUE = new Object();
|
||||
|
||||
/** The resource association that is using this association. */
|
||||
private ResourceAssociation resourceAssociation = null;
|
||||
/**
|
||||
* AssociationContainer constructor.
|
||||
*/
|
||||
public AssociationContainer() {
|
||||
}//AssociationContainer()//
|
||||
/**
|
||||
* Gets the resource association that this association is used by.
|
||||
* @return The linked resource association.
|
||||
*/
|
||||
public ResourceAssociation getResourceAssociation() {
|
||||
return resourceAssociation;
|
||||
}//getResourceAssociation()//
|
||||
/**
|
||||
* Sets the resource association that this association is used by.
|
||||
* @param resourceAssociation The linked resource association.
|
||||
*/
|
||||
public void setResourceAssociation(ResourceAssociation resourceAssociation) {
|
||||
this.resourceAssociation = resourceAssociation;
|
||||
}//setResourceAssociation()//
|
||||
/**
|
||||
* Ensures that the current thread is the view system thread.
|
||||
*/
|
||||
public void verifyThread() {
|
||||
resourceAssociation.verifyThread();
|
||||
}//verifyThread()//
|
||||
}//AssociationContainer//
|
||||
69
Foundation/src/com/foundation/view/AssociationNode.java
Normal file
69
Foundation/src/com/foundation/view/AssociationNode.java
Normal file
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Copyright (c) 2006,2009 Declarative Engineering LLC.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Declarative Engineering LLC
|
||||
* verson 1 which accompanies this distribution, and is available at
|
||||
* http://declarativeengineering.com/legal/DE_Developer_License_v1.txt
|
||||
*/
|
||||
package com.foundation.view;
|
||||
|
||||
import com.common.debug.Debug;
|
||||
|
||||
/**
|
||||
* A node in the arbitrary tree of event listeners used by method target associations and indirect associations to know when their results are no longer valid.
|
||||
*/
|
||||
public class AssociationNode {
|
||||
private String rowTypeName = null;
|
||||
private Class rowType = null;
|
||||
private AssociationAttribute[] attributes = null;
|
||||
private int[] events = null;
|
||||
/**
|
||||
* AssociationNode constructor.
|
||||
* @param rowTypeName The qualified type name for the class of row object that the node applies to.
|
||||
* @param attributes The attributes whose values will be re-inserted into this and other peer nodes.
|
||||
* @param events The events to be listened to on the row object indicating that the result of the Association that references the node has changed.
|
||||
*/
|
||||
public AssociationNode(String rowTypeName, AssociationAttribute[] attributes, int[] events) {
|
||||
this.rowTypeName = rowTypeName;
|
||||
this.attributes = attributes;
|
||||
this.events = events;
|
||||
}//AssociationNode()//
|
||||
/**
|
||||
* Gets the row type that this node applies to.
|
||||
* @return The type of row object which has the attributes and events the node defines.
|
||||
*/
|
||||
protected Class getRowType() {
|
||||
if(rowType == null) {
|
||||
try {
|
||||
rowType = Class.forName(rowTypeName);
|
||||
}//try//
|
||||
catch(ClassNotFoundException e) {
|
||||
Debug.log(e);
|
||||
}//catch//
|
||||
}//if//
|
||||
|
||||
return rowType;
|
||||
}//getRowType()//
|
||||
/**
|
||||
* Determines whether the given row value is applicable to this node.
|
||||
* @param row The row value to compare with.
|
||||
* @return Whether this node supports the given row.
|
||||
*/
|
||||
public boolean isApplicable(Object row) {
|
||||
return row == null ? false : getRowType().isAssignableFrom(row.getClass());
|
||||
}//isApplicable()//
|
||||
/**
|
||||
* Gets the set of attribute associations used to get values for nodes down stream.
|
||||
* @return The attributes defined by the node's input value that retrieve values placed back into the node pool.
|
||||
*/
|
||||
public AssociationAttribute[] getAttributes() {
|
||||
return attributes;
|
||||
}//getAttributes()//
|
||||
/**
|
||||
* Gets the set of events to be listened to on the node's value.
|
||||
* @return The events on the node's input value to listen to for change notification.
|
||||
*/
|
||||
public int[] getEvents() {
|
||||
return events;
|
||||
}//getEvents()//
|
||||
}//AssociationNode//
|
||||
233
Foundation/src/com/foundation/view/AttributeAssociation.java
Normal file
233
Foundation/src/com/foundation/view/AttributeAssociation.java
Normal file
@@ -0,0 +1,233 @@
|
||||
/*
|
||||
* Copyright (c) 2003,2007 Declarative Engineering LLC.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Declarative Engineering LLC
|
||||
* verson 1 which accompanies this distribution, and is available at
|
||||
* http://declarativeengineering.com/legal/DE_Developer_License_v1.txt
|
||||
*/
|
||||
package com.foundation.view;
|
||||
|
||||
import com.common.debug.*;
|
||||
import com.foundation.attribute.AttributeSupport;
|
||||
import com.foundation.event.EventSupport;
|
||||
import com.foundation.metadata.Attribute;
|
||||
|
||||
/*
|
||||
* Models a view association with an entity attribute.
|
||||
*/
|
||||
public class AttributeAssociation extends ValueHolderAssociation implements IAttributeAssociation {
|
||||
/** The view unique number for this association. */
|
||||
private int associationNumber = 0;
|
||||
/** The handler which is invoked to get and set the attribute value. */
|
||||
private IAssociationHandler handler = null;
|
||||
/** The attribute's unique (within the context of the value holder's held type hierarchy) number. */
|
||||
private int attributeNumber = 0;
|
||||
/** The unique (within the context of the value holder's held type hierarchy) name of the attribute. */
|
||||
private String attributeName = null;
|
||||
/** The type of value returned when retreiving the attribute value. Also the type required by the attribute setter method. */
|
||||
private Class attributeType = null;
|
||||
/** The listener for attribute change events. This will either be an instance of IAttributeAssociationChangeListener or IEventAssociationChangeListener. */
|
||||
private Object listener = null;
|
||||
/**
|
||||
* AttributeAssociation constructor.
|
||||
* @param handler The object that handles the method invokations to get and set the attribute value.
|
||||
* @param associationNumber The association number which will be passed to the handler when the assocation is invoked. This number allows fast indexing of the methods that the handler handles.
|
||||
* @param component The component the association is connected to.
|
||||
* @param rowType The class which defines the attribute.
|
||||
* @param attribute The associated attribute's unique (within the class) identifier.
|
||||
* @param attributeType The class assignable by values held by the attribute.
|
||||
*/
|
||||
public AttributeAssociation(IAssociationHandler handler, int associationNumber, IAbstractComponent component, Class rowType, Attribute attribute, Class attributeType) {
|
||||
super(component, null, null, rowType);
|
||||
|
||||
this.handler = handler;
|
||||
this.associationNumber = associationNumber;
|
||||
this.attributeNumber = attribute.getNumber();
|
||||
this.attributeName = AttributeSupport.getAttributeName(rowType, attributeNumber);
|
||||
this.attributeType = attributeType;
|
||||
}//AttributeAssociation()//
|
||||
/**
|
||||
* AttributeAssociation constructor.
|
||||
* @param handler The object that handles the method invokations to get and set the attribute value.
|
||||
* @param associationNumber The association number which will be passed to the handler when the assocation is invoked. This number allows fast indexing of the methods that the handler handles.
|
||||
* @param component The component the association is connected to.
|
||||
* @param rowType The class which defines the attribute.
|
||||
* @param attributeName The associated attribute's unique (within the class) name.
|
||||
* @param attributeType The class assignable by values held by the attribute.
|
||||
*/
|
||||
public AttributeAssociation(IAssociationHandler handler, int associationNumber, IAbstractComponent component, Class rowType, String attributeName, Class attributeType) {
|
||||
super(component, null, null, rowType);
|
||||
|
||||
this.listener = component;
|
||||
this.handler = handler;
|
||||
this.associationNumber = associationNumber;
|
||||
this.attributeName = attributeName;
|
||||
this.attributeNumber = AttributeSupport.getAttributeNumber(rowType, attributeName);
|
||||
this.attributeType = attributeType;
|
||||
}//AttributeAssociation()//
|
||||
/**
|
||||
* AttributeAssociation constructor.
|
||||
* @param handler The object that handles the method invokations to get and set the attribute value.
|
||||
* @param associationNumber The association number which will be passed to the handler when the assocation is invoked. This number allows fast indexing of the methods that the handler handles.
|
||||
* @param component The component the association is connected to.
|
||||
* @param valueHolderName The name of the value holder for the attribute. The value holder's held type defines the association if the value type is null.
|
||||
* @param valueHolderType The type that the held value must match in order to be valid. This is optional and only used if the value holder is non-null.
|
||||
* @param attribute The associated attribute's unique (within the class) identifier.
|
||||
* @param attributeType The class assignable by values held by the attribute.
|
||||
*/
|
||||
public AttributeAssociation(IAssociationHandler handler, int associationNumber, IAbstractComponent component, String valueHolderName, Class valueHolderType, Class rowType, Attribute attribute, Class attributeType) {
|
||||
super(component, valueHolderName, valueHolderType, rowType);
|
||||
|
||||
this.handler = handler;
|
||||
this.associationNumber = associationNumber;
|
||||
this.attributeNumber = attribute.getNumber();
|
||||
this.attributeName = AttributeSupport.getAttributeName(rowType == null ? getValueHolder().getHeldType() : rowType, attributeNumber);
|
||||
this.attributeType = attributeType;
|
||||
}//AttributeAssociation()//
|
||||
/**
|
||||
* AttributeAssociation constructor.
|
||||
* @param handler The object that handles the method invokations to get and set the attribute value.
|
||||
* @param associationNumber The association number which will be passed to the handler when the assocation is invoked. This number allows fast indexing of the methods that the handler handles.
|
||||
* @param component The component the association is connected to.
|
||||
* @param valueHolderName The name of the value holder for the attribute. The value holder's held type defines the association if the value type is null.
|
||||
* @param valueHolderType The type that the held value must match in order to be valid. This is optional and only used if the value holder is non-null.
|
||||
* @param attributeName The associated attribute's unique (within the class) name.
|
||||
* @param attributeType The class assignable by values held by the attribute.
|
||||
*/
|
||||
public AttributeAssociation(IAssociationHandler handler, int associationNumber, IAbstractComponent component, String valueHolderName, Class valueHolderType, String attributeName, Class attributeType) {
|
||||
super(component, valueHolderName, valueHolderType, null);
|
||||
|
||||
this.listener = component;
|
||||
this.handler = handler;
|
||||
this.associationNumber = associationNumber;
|
||||
this.attributeName = attributeName;
|
||||
this.attributeNumber = AttributeSupport.getAttributeNumber(valueHolderType == null ? getValueHolder().getHeldType() : valueHolderType, attributeName);
|
||||
this.attributeType = attributeType;
|
||||
}//AttributeAssociation()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.tcv.swt.IAttributeAssociation#getAttributeValue()
|
||||
*/
|
||||
public Object getAttributeValue() {
|
||||
try {
|
||||
if(getValueHolder().getValue() != null) {
|
||||
return handler.invokeGetMethod(associationNumber, getValueHolder().getValue());
|
||||
}//if//
|
||||
else {
|
||||
return null;
|
||||
}//else//
|
||||
}//try//
|
||||
catch(Throwable e) {
|
||||
Debug.log(e);
|
||||
return null;
|
||||
}//catch//
|
||||
}//getAttributeValue()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.tcv.swt.IAttributeAssociation#setAttributeValue(Object)
|
||||
*/
|
||||
public void setAttributeValue(Object value) {
|
||||
try {
|
||||
handler.invokeSetMethod(associationNumber, getValueHolder().getValue(), value);
|
||||
}//try//
|
||||
catch(Throwable e) {
|
||||
Debug.log(e);
|
||||
}//catch//
|
||||
}//setAttributeValue()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.tcv.swt.IAttributeAssociation#getAttributeValue(java.lang.Object)
|
||||
*/
|
||||
public Object getAttributeValue(Object object) {
|
||||
Object retVal = null;
|
||||
|
||||
try {
|
||||
if(object != null) {
|
||||
retVal = handler.invokeGetMethod(associationNumber, object);
|
||||
}//if//
|
||||
}//try//
|
||||
catch(Throwable e) {
|
||||
Debug.log(e);
|
||||
}//catch//
|
||||
|
||||
return retVal;
|
||||
}//getAttributeValue()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.tcv.swt.IAttributeAssociation#setAttributeValue(java.lang.Object, java.lang.Object)
|
||||
*/
|
||||
public void setAttributeValue(Object object, Object value) {
|
||||
if(object != null) {
|
||||
try {
|
||||
handler.invokeSetMethod(associationNumber, object, value);
|
||||
}//try//
|
||||
catch(Throwable e) {
|
||||
Debug.log(e);
|
||||
}//catch//
|
||||
}//if//
|
||||
}//setAttributeValue()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.tcv.swt.IAttributeAssociation#getAttributeType()
|
||||
*/
|
||||
public Class getAttributeType() {
|
||||
return attributeType;
|
||||
}//getAttributeType()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.tcv.swt.IAttributeAssociation#register()
|
||||
*/
|
||||
public void register() {
|
||||
getValueHolder().registerListener(this);
|
||||
}//register()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.tcv.swt.IAttributeAssociation#unregister()
|
||||
*/
|
||||
public void unregister() {
|
||||
getValueHolder().unregisterListener(this);
|
||||
}//unregister()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.view.IAttributeAssociation#setChangeListener(com.foundation.view.IAttributeAssociationChangeListener)
|
||||
*/
|
||||
public void setChangeListener(IAttributeAssociationChangeListener listener) {
|
||||
if(listener == null) {
|
||||
listener = getComponent();
|
||||
}//if//
|
||||
|
||||
this.listener = listener;
|
||||
}//setChangeListener()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.view.IEventAssociation#setChangeListener(com.foundation.view.IEventAssociationChangeListener)
|
||||
*/
|
||||
public void setChangeListener(IEventAssociationChangeListener listener) {
|
||||
if(listener == null) {
|
||||
listener = getComponent();
|
||||
}//if//
|
||||
|
||||
this.listener = listener;
|
||||
}//setChangeListener()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.common.event.IHandler#evaluate(int, java.lang.Object[], int)
|
||||
*/
|
||||
public void evaluate(int eventNumber, Object[] parameters, int flags) {
|
||||
//Note: I commented out the isTrustedChangeEvent because we don't want to listen to lazy load and reflection load events which are marked as trusted.//
|
||||
//If we did listen to them we would be performing updates before we have finished initializing during an initialization where more than one association is connected to a lazy or reflection loaded attribute.//
|
||||
//We do care about reflection updates which are now marked as trusted when setting the attribute, but are not marked as trusted when firing the events.//
|
||||
//I don't think there is anything else marked as trusted that we care about here - thus the code is commented out.//
|
||||
if(EventSupport.isStandardEvent(flags)/* || EventSupport.isTrustedChangeEvent(flags)*/) {
|
||||
//Notify the listener.//
|
||||
if(listener instanceof IAttributeAssociationChangeListener) {
|
||||
((IAttributeAssociationChangeListener) listener).onValueChanged(this);
|
||||
}//if//
|
||||
else if(listener instanceof IEventAssociationChangeListener) {
|
||||
((IEventAssociationChangeListener) listener).onEventFired(this, EMPTY_ARGS);
|
||||
}//else if//
|
||||
}//if//
|
||||
}//evaluate()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.tcv.swt.IAttributeAssociation#getAttributeName()
|
||||
*/
|
||||
public String getAttributeName() {
|
||||
return attributeName;
|
||||
}//getAttributeName()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.tcv.swt.IEventAssociation#getEventName()
|
||||
*/
|
||||
public int getEventNumber() {
|
||||
return attributeNumber;
|
||||
}//getEventNumber()//
|
||||
}//AttributeAssociation//
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright (c) 2008 Declarative Engineering LLC.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Declarative Engineering LLC
|
||||
* verson 1 which accompanies this distribution, and is available at
|
||||
* http://declarativeengineering.com/legal/DE_Developer_License_v1.txt
|
||||
*/
|
||||
package com.foundation.view;
|
||||
|
||||
public class ChangeNotificationControlDecoration extends ControlDecoration {
|
||||
private String value = null;
|
||||
/**
|
||||
* ChangeNotificationControlDecoration constructor.
|
||||
* @param image
|
||||
* @param toolTip
|
||||
* @param value The value that was set in the model that differs from that set by the user in the view.
|
||||
*/
|
||||
public ChangeNotificationControlDecoration(Object image, Object toolTip, String value) {
|
||||
super(image, toolTip);
|
||||
this.value = value;
|
||||
}//ChangeNotificationControlDecoration()//
|
||||
/**
|
||||
* ChangeNotificationControlDecoration constructor.
|
||||
* @param image
|
||||
* @param toolTip
|
||||
* @param position
|
||||
* @param value The value that was set in the model that differs from that set by the user in the view.
|
||||
*/
|
||||
public ChangeNotificationControlDecoration(Object image, Object toolTip, int position, String value) {
|
||||
super(image, toolTip, position);
|
||||
this.value = value;
|
||||
}//ChangeNotificationControlDecoration()//
|
||||
/**
|
||||
* Gets the value that was set in the model that differs from that set by the user in the view.
|
||||
* @return The value not shown in the view.
|
||||
*/
|
||||
public String getValue() {
|
||||
return value;
|
||||
}//getValue()//
|
||||
}//ChangeNotificationControlDecoration//
|
||||
@@ -0,0 +1,490 @@
|
||||
/*
|
||||
* Copyright (c) 2006,2009 Declarative Engineering LLC.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Declarative Engineering LLC
|
||||
* verson 1 which accompanies this distribution, and is available at
|
||||
* http://declarativeengineering.com/legal/DE_Developer_License_v1.txt
|
||||
*/
|
||||
package com.foundation.view;
|
||||
|
||||
import com.common.util.IHashSet;
|
||||
import com.common.util.IIterator;
|
||||
import com.common.util.LiteHashSet;
|
||||
import com.foundation.event.EventSupport;
|
||||
import com.foundation.event.IEventEmitter;
|
||||
import com.foundation.event.IEventHandler;
|
||||
import com.foundation.util.IManagedCollection;
|
||||
import com.foundation.view.EventAssociation;
|
||||
import com.foundation.view.IAssociationHandler;
|
||||
import com.foundation.view.IEventAssociation;
|
||||
import com.foundation.view.IEventAssociationChangeListener;
|
||||
import com.foundation.view.IValueHolderListener;
|
||||
import com.foundation.view.CollectingMultiAssociationContainer.RowDataContainer;
|
||||
|
||||
/*
|
||||
* Models an association that results in one value for each row value given to it.
|
||||
*/
|
||||
public class CollectingMultiAssociation extends Association implements IEventAssociationChangeListener, IValueHolderListener {
|
||||
/** The child associations in search order. This will be null only if this is a target association (the result is the resource association's result). */
|
||||
private CollectingMultiAssociation[] children = null;
|
||||
/** The set of events that the association registers for when the input changes. If any of these events are triggered it indicates that this association's value has changed. */
|
||||
private EventAssociation[] events = null;
|
||||
/** The depth of this association in the tree. */
|
||||
private int depth = 0;
|
||||
/** The index into the row data container for the array of row data where this row data exists. */
|
||||
private int containerIndex = 0;
|
||||
/** Tracks the row data instances created by the multi-association. */
|
||||
private IHashSet rowDataSet = null;
|
||||
|
||||
public static class RowData extends CollectingMultiAssociationContainer.RowData implements IEventHandler {
|
||||
/** The row that last generated the result. */
|
||||
private Object row = null;
|
||||
/** The last known result for this association. */
|
||||
private Object result = null;
|
||||
/** The last used child association for the current result. This will always be null for target associations. */
|
||||
private CollectingMultiAssociation resultChild = null;
|
||||
/** The support object that tracks events registered via the AssociationNodes. This will only be non-null if association attribute and nodes were provided. */
|
||||
private EventSupport nodeEventSupport = null;
|
||||
/** The container for the row that this row data models. */
|
||||
private CollectingMultiAssociationContainer.RowDataContainer rowDataContainer = null;
|
||||
|
||||
/**
|
||||
* RowData constructor.
|
||||
* @param rowDataContainer The container that this row data exists in.
|
||||
*/
|
||||
public RowData(CollectingMultiAssociation association, Object row, CollectingMultiAssociationContainer.RowDataContainer rowDataContainer) {
|
||||
super(association);
|
||||
this.row = row;
|
||||
this.rowDataContainer = rowDataContainer;
|
||||
}//RowData()//
|
||||
/**
|
||||
* Gets the row data container.
|
||||
* @return The container containing this row data.
|
||||
*/
|
||||
public CollectingMultiAssociationContainer.RowDataContainer getContainer() {
|
||||
return rowDataContainer;
|
||||
}//getContainer()//
|
||||
/**
|
||||
* Gets the row that this row data describes.
|
||||
* @return The row object which was input for this row data.
|
||||
*/
|
||||
public Object getRow() {
|
||||
return row;
|
||||
}//getRow()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.event.IEventHandler#evaluate(com.foundation.event.IEventEmitter, int, java.lang.Object[], int)
|
||||
*/
|
||||
public void evaluate(IEventEmitter eventEmitter, int eventNumber, Object[] eventParameters, int flags) {
|
||||
//Called when an event, registered on the row object that generated the result, is fired.//
|
||||
((CollectingMultiAssociation) getAssociation()).updateAssociation(this);
|
||||
}//evaluate()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.event.IHandler#evaluate(int, java.lang.Object[], int)
|
||||
*/
|
||||
public void evaluate(int eventNumber, Object[] eventParameters, int flags) {
|
||||
//Never called.//
|
||||
}//evaluate()//
|
||||
}//RowData//
|
||||
/**
|
||||
* CollectingMultiAssociation constructor.
|
||||
* @param rowType The type of row the association applies to. In the case of a root association in a single association this is the type held by the value holder.
|
||||
* @param associationNumber The association number which will be passed to the handler when the assocation is invoked. This number allows fast indexing of the methods that the handler handles. This value may be -1 if the input value should be used as the output value for the association (short circuiting).
|
||||
* @param handler The object that handles the method invocations to get and set the value.
|
||||
* @param invertLogic Whether the result of the association will be inverted. This is ignored unless the result is a Boolean value.
|
||||
* @param attributes The attributes to listen to and whose values are passed to the nodes. This may be null.
|
||||
* @param nodes The nodes of the arbitrary tree of listeners for value change events for this association's result value. This may be null.
|
||||
* @param children The child associations in search order. The children will be searched for the first match for this association's result. This should always be null for target associations, and should never be null for non-target associations.
|
||||
* @param events The non-null array of events that will be used to listen to the supplied row object for changes in the result.
|
||||
* @param valueHolderName The name of the optional value holder whose value holds the getter and setter methods used by the association. If no value holder is defined then the row object passed to the association must define the getter and setter methods.
|
||||
*/
|
||||
public CollectingMultiAssociation(Class rowType, int associationNumber, IAssociationHandler handler, boolean invertLogic, AssociationAttribute[] attributes, AssociationNode[] nodes, CollectingMultiAssociation[] children, EventAssociation[] events, String valueHolderName) {
|
||||
super(rowType, associationNumber, handler, invertLogic, attributes, nodes, valueHolderName);
|
||||
|
||||
if(events == null) {
|
||||
throw new IllegalArgumentException("The events parameter may not be null.");
|
||||
}//if//
|
||||
|
||||
this.children = children;
|
||||
this.events = events;
|
||||
|
||||
if(children != null) {
|
||||
for(int index = 0; index < children.length; index++) {
|
||||
children[index].setParent(this);
|
||||
}//for//
|
||||
}//if//
|
||||
|
||||
for(int index = 0; index < events.length; index++) {
|
||||
events[index].setChangeListener(this);
|
||||
}//for//
|
||||
}//CollectingMultiAssociation()//
|
||||
/**
|
||||
* Determines whether this is a target association meaning that its result is the value assigned to the initial association input.
|
||||
* @return Whether this association is a target and the result is not placed as input to another association.
|
||||
*/
|
||||
public boolean isTargetAssociation() {
|
||||
return children == null;
|
||||
}//isTargetAssociation()//
|
||||
/**
|
||||
* Initializes the container index and depth for this multi association.
|
||||
* @param containerIndex The index into the row data container for the array of row data where this row data exists.
|
||||
* @param depth The depth of the association in the tree.
|
||||
* @return The deepest this branch of the tree got.
|
||||
*/
|
||||
public int initializeAssociation(int containerIndex, int depth) {
|
||||
this.depth = depth;
|
||||
this.containerIndex = containerIndex;
|
||||
|
||||
if(children != null) {
|
||||
for(int index = 0; index < children.length; index++) {
|
||||
depth = Math.max(depth, children[index].initializeAssociation(containerIndex, this.depth + 1));
|
||||
}//for//
|
||||
}//if//
|
||||
|
||||
return depth;
|
||||
}//initializeAssociation()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.view.Association#release()
|
||||
*/
|
||||
public void release() {
|
||||
super.release();
|
||||
}//release()//
|
||||
/**
|
||||
* Gets the depth of this association in the tree.
|
||||
* @return The association's depth.
|
||||
*/
|
||||
public int getDepth() {
|
||||
return depth;
|
||||
}//getDepth()//
|
||||
/**
|
||||
* Gets the index into the row data container for the array of row data where this row data exists.
|
||||
* @return The first dimension's index in the row data array in the container.
|
||||
*/
|
||||
public int getContainerIndex() {
|
||||
return containerIndex;
|
||||
}//getContainerIndex()//
|
||||
/**
|
||||
* Gets the latest result for the association.
|
||||
* @param rowDataContainer The container of row data.
|
||||
* @return The association's current result.
|
||||
*/
|
||||
public Object getResult(RowDataContainer rowDataContainer) {
|
||||
return ((RowData) rowDataContainer.getRowData(this)).result;
|
||||
}//getResult()//
|
||||
/**
|
||||
* Sets the latest result for the association.
|
||||
* @param rowDataContainer The container of row data.
|
||||
* @param result The association's new result.
|
||||
*/
|
||||
public void setResult(RowDataContainer rowDataContainer, Object result) {
|
||||
setResult(((RowData) rowDataContainer.getRowData(this)).row, result);
|
||||
}//setResult()//
|
||||
/**
|
||||
* Gets the current result of the association from the model.
|
||||
* @param row The row object that is the input to the association.
|
||||
* @return The result of the association.
|
||||
*/
|
||||
protected Object getResult(Object row) {
|
||||
Object result;
|
||||
|
||||
if(getIsValueHolderAssociated()) {
|
||||
if(getAssociationNumber() != -1) {
|
||||
if(oneArray == null) {
|
||||
oneArray = new Object[1];
|
||||
}//if//
|
||||
|
||||
oneArray[0] = row;
|
||||
result = getHandler().invokeMethod(getAssociationNumber(), getValueHolder().getValue(), oneArray, IAssociationHandler.INVOKE_GETTER_METHOD_FLAG);
|
||||
oneArray[0] = null;
|
||||
}//if//
|
||||
else {
|
||||
result = getValueHolder().getValue();
|
||||
}//else//
|
||||
}//if//
|
||||
else {
|
||||
if(getAssociationNumber() != -1) {
|
||||
result = getHandler().invokeMethod(getAssociationNumber(), row, null, IAssociationHandler.INVOKE_GETTER_METHOD_FLAG);
|
||||
}//if//
|
||||
else {
|
||||
result = row;
|
||||
}//else//
|
||||
}//else//
|
||||
|
||||
if(isTargetAssociation()) {
|
||||
result = convertValueToControl(result);
|
||||
}//if//
|
||||
|
||||
return result;
|
||||
}//getResult()//
|
||||
/**
|
||||
* Sets the new result of the association into the model.
|
||||
* @param row The row object that is the input to the association.
|
||||
* @param result The new result which will be placed in the model.
|
||||
*/
|
||||
protected void setResult(Object row, Object result) {
|
||||
result = convertValueToModel(result);
|
||||
|
||||
if(getIsValueHolderAssociated()) {
|
||||
if(getAssociationNumber() != -1) {
|
||||
if(twoArray == null) {
|
||||
twoArray = new Object[2];
|
||||
}//if//
|
||||
|
||||
twoArray[0] = row;
|
||||
twoArray[1] = result;
|
||||
getHandler().invokeMethod(getAssociationNumber(), getValueHolder().getValue(), twoArray, IAssociationHandler.INVOKE_SETTER_METHOD_FLAG);
|
||||
twoArray[0] = null;
|
||||
twoArray[1] = null;
|
||||
}//if//
|
||||
}//if//
|
||||
else {
|
||||
if(getAssociationNumber() != -1) {
|
||||
if(oneArray == null) {
|
||||
oneArray = new Object[1];
|
||||
}//if//
|
||||
|
||||
oneArray[0] = result;
|
||||
getHandler().invokeMethod(getAssociationNumber(), row, oneArray, IAssociationHandler.INVOKE_SETTER_METHOD_FLAG);
|
||||
oneArray[0] = null;
|
||||
}//if//
|
||||
}//else//
|
||||
}//setResult()//
|
||||
/**
|
||||
* Called by the event handlers to update the association state when the result may have altered.
|
||||
* @param rowData The row data for the row being updated.
|
||||
*/
|
||||
protected void updateAssociation(RowData rowData) {
|
||||
CollectingMultiAssociationContainer.RowDataContainer rowDataContainer = rowData.getContainer();
|
||||
Object row = rowData.row;
|
||||
Object newResult = getResult(row);
|
||||
|
||||
if(newResult != rowData.result) {
|
||||
unregisterChildren(rowDataContainer, rowData);
|
||||
unregisterListeners(row, rowData);
|
||||
//unregister(row, rowDataContainer);
|
||||
rowData.result = newResult;
|
||||
registerChildren(newResult, rowDataContainer, rowData);
|
||||
registerListeners(row, rowData);
|
||||
//register(row, rowDataContainer, newResult);
|
||||
//Notify the ResourceAssociation.//
|
||||
((CollectingMultiResourceAssociation) getAssociationContainer().getResourceAssociation()).modelChanged(row, rowData.getContainer().getData());
|
||||
}//if//
|
||||
else {
|
||||
//Always re-register all the extended listeners since they cannot handle attribute changes on their own.//
|
||||
unregisterListeners(rowData.result, rowData);
|
||||
registerListeners(rowData.result, rowData);
|
||||
}//else//
|
||||
}//updateAssociation()//
|
||||
/**
|
||||
* Registers a value with the association for use by a single association.
|
||||
* @param row The next value in the chain of associations for the single association.
|
||||
* @param rowData The array of row data used by the associations to store depth specific data. This association may only access the index at its depth.
|
||||
*/
|
||||
public void register(Object row, CollectingMultiAssociationContainer.RowDataContainer rowDataContainer) {
|
||||
register(row, rowDataContainer, getResult(row));
|
||||
}//register()//
|
||||
/**
|
||||
* Registers a value with the association for use by a single association.
|
||||
* @param row The next value in the chain of associations for the single association.
|
||||
* @param rowData The array of row data used by the associations to store depth specific data. This association may only access the index at its depth.
|
||||
* @param result The result for the row.
|
||||
*/
|
||||
protected void register(Object row, CollectingMultiAssociationContainer.RowDataContainer rowDataContainer, Object result) {
|
||||
RowData rowData = new RowData(this, row, rowDataContainer);
|
||||
boolean useRowDataSet = false;
|
||||
|
||||
rowDataContainer.setRowData(this, rowData);
|
||||
//Store the result.//
|
||||
rowData.result = result;
|
||||
rowData.resultChild = null;
|
||||
|
||||
//Register all events.//
|
||||
for(int index = 0; index < events.length; index++) {
|
||||
EventAssociation eventAssociation = (EventAssociation) events[index];
|
||||
|
||||
if(eventAssociation.getIsValueHolderAssociated()) {
|
||||
//Register for the value holder's value changing and for the event on the held value (uses the value holder's event support).//
|
||||
eventAssociation.register();
|
||||
}//if//
|
||||
else if(row instanceof IEventEmitter) {
|
||||
//Register with the event on the row object using the resource association's event support.//
|
||||
rowDataContainer.getEventSupport().register((IEventEmitter) row, eventAssociation.getEventNumber(), rowData, true);
|
||||
}//else//
|
||||
}//for//
|
||||
|
||||
//Register the listener with the value holder.//
|
||||
if(getIsValueHolderAssociated()) {
|
||||
getValueHolder().registerListener(this);
|
||||
useRowDataSet = true;
|
||||
}//if//
|
||||
|
||||
if(useRowDataSet) {
|
||||
if(rowDataSet == null) {
|
||||
rowDataSet = new LiteHashSet(100, LiteHashSet.DEFAULT_LOAD_FACTOR, LiteHashSet.DEFAULT_COMPARATOR, LiteHashSet.STYLE_COUNT_DUPLICATES);
|
||||
}//if//
|
||||
|
||||
rowDataSet.add(rowData);
|
||||
}//if//
|
||||
|
||||
registerChildren(result, rowDataContainer, rowData);
|
||||
registerListeners(row, rowData);
|
||||
}//register()//
|
||||
/**
|
||||
* Registers the result of this association with the child associations.
|
||||
* @param result The result of this association which will become the row input for the child association(s).
|
||||
* @param rowDataContainer The container of row data used by the associations to store data.
|
||||
* @param rowData The data for the row that is registering.
|
||||
*/
|
||||
protected void registerChildren(Object result, RowDataContainer rowDataContainer, RowData rowData) {
|
||||
//Register with the first child association, if one exists and matches the input type.//
|
||||
if(children != null) {
|
||||
//Iterate over the children looking for the first match.//
|
||||
for(int index = 0; (rowData.resultChild == null) && (index < children.length); index++) {
|
||||
if(children[index].isApplicable(result)) {
|
||||
rowData.resultChild = children[index];
|
||||
rowData.resultChild.register(result, rowDataContainer);
|
||||
}//if//
|
||||
}//for//
|
||||
}//if//
|
||||
}//registerChildren()//
|
||||
/**
|
||||
* Registers listeners with the result.
|
||||
* @param row The association's row to register all extended listeners with.
|
||||
* @param rowData The data for the row that is registering.
|
||||
*/
|
||||
protected void registerListeners(Object row, RowData rowData) {
|
||||
AssociationAttribute[] attributes = getAttributes();
|
||||
|
||||
if(attributes != null) {
|
||||
IHashSet trackedValues = new LiteHashSet(100);
|
||||
|
||||
if(rowData.nodeEventSupport == null) {
|
||||
rowData.nodeEventSupport = new EventSupport(null);
|
||||
}//if//
|
||||
|
||||
//TODO: It would be more efficient in some cases if we detected duplicate results for different row datas and had the events go to all affected row data's while only registering once.
|
||||
//This idea could turn out to be less efficient than the current design since it would require a more complex data structure.
|
||||
|
||||
for(int index = 0; index < attributes.length; index++) {
|
||||
Object attributeValue = attributes[index].getValue(row);
|
||||
|
||||
if(!trackedValues.containsValue(attributeValue)) {
|
||||
trackedValues.add(attributeValue);
|
||||
|
||||
//TODO: Should we support other collections? If so we would need to be notified when there are changes...
|
||||
if(attributeValue instanceof IManagedCollection) {
|
||||
IIterator iterator = ((IManagedCollection) attributeValue).iterator();
|
||||
|
||||
//Register for changes in the collected values.//
|
||||
while(iterator.hasNext()) {
|
||||
registerListeners(rowData.nodeEventSupport, trackedValues, rowData, iterator.next());
|
||||
}//while//
|
||||
|
||||
//Register for changes in the collection.//
|
||||
rowData.nodeEventSupport.register((IManagedCollection) attributeValue, IManagedCollection.EVENT, rowData, true);
|
||||
}//if//
|
||||
else {
|
||||
//Register for changes in the attribute value.//
|
||||
registerListeners(rowData.nodeEventSupport, trackedValues, rowData, attributeValue);
|
||||
}//else//
|
||||
}//if//
|
||||
}//for//
|
||||
}//if//
|
||||
}//registerListeners()//
|
||||
/**
|
||||
* Unregisters the previous registration.
|
||||
* @param row The optional row value. This should be null if this is the root association.
|
||||
* @param rowData The array of row data used by the associations to store depth specific data. This association may only access the index at its depth.
|
||||
*/
|
||||
public void unregister(Object row, CollectingMultiAssociationContainer.RowDataContainer rowDataContainer) {
|
||||
RowData rowData = (RowData) rowDataContainer.getRowData(this);
|
||||
boolean removeFromRowDataSet = false;
|
||||
|
||||
//Prevent short term memory leaks.//
|
||||
rowDataContainer.setRowData(this, null);
|
||||
unregisterChildren(rowDataContainer, rowData);
|
||||
|
||||
//Unregister all events.//
|
||||
for(int index = 0; index < events.length; index++) {
|
||||
EventAssociation eventAssociation = (EventAssociation) events[index];
|
||||
|
||||
if(eventAssociation.getIsValueHolderAssociated()) {
|
||||
//Register for the value holder's value changing and for the event on the held value (uses the value holder's event support).//
|
||||
eventAssociation.unregister();
|
||||
removeFromRowDataSet = true;
|
||||
}//if//
|
||||
else if(row instanceof IEventEmitter) {
|
||||
//Register with the event on the row object using the resource association's event support.//
|
||||
rowDataContainer.getEventSupport().unregister((IEventEmitter) row, eventAssociation.getEventNumber(), rowData);
|
||||
}//else//
|
||||
}//for//
|
||||
|
||||
//Unregister the listener with the value holder.//
|
||||
if(getIsValueHolderAssociated()) {
|
||||
getValueHolder().unregisterListener(this);
|
||||
removeFromRowDataSet = true;
|
||||
}//if//
|
||||
|
||||
if(removeFromRowDataSet) {
|
||||
rowDataSet.remove(rowData);
|
||||
}//if//
|
||||
|
||||
unregisterListeners(row, rowData);
|
||||
}//unregister()//
|
||||
/**
|
||||
* Unregisters the result of this association with the child associations.
|
||||
* @param rowDataContainer The container of row data used by the associations to store data.
|
||||
* @param rowData The data for the row that is unregistering.
|
||||
*/
|
||||
protected void unregisterChildren(RowDataContainer rowDataContainer, RowData rowData) {
|
||||
if(rowData.resultChild != null) {
|
||||
rowData.resultChild.unregister(rowData.result, rowDataContainer);
|
||||
rowData.resultChild = null;
|
||||
}//if//
|
||||
}//unregisterChildren()//
|
||||
/**
|
||||
* Unregisters listeners with the result.
|
||||
* @param row The association's row to unregister all extended listeners with.
|
||||
* @param rowData The data for the row that is unregistering.
|
||||
*/
|
||||
protected void unregisterListeners(Object row, RowData rowData) {
|
||||
AssociationAttribute[] attributes = getAttributes();
|
||||
|
||||
if(attributes != null) {
|
||||
rowData.nodeEventSupport.unregisterAll();
|
||||
}//if//
|
||||
}//unregisterListeners()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.view.IEventAssociationChangeListener#onEventFired(com.foundation.view.IEventAssociation, java.lang.Object[])
|
||||
*/
|
||||
public void onEventFired(IEventAssociation eventAssociation, Object[] eventArguments) {
|
||||
//Called when an event is fired from a value holder. In this case all rows are affected by the event.//
|
||||
IIterator iterator = rowDataSet.iterator();
|
||||
|
||||
while(iterator.hasNext()) {
|
||||
updateAssociation((RowData) iterator.next());
|
||||
}//while//
|
||||
}//onEventFired()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.view.IValueHolderListener#heldValueChanged()
|
||||
*/
|
||||
public void heldValueChanged() {
|
||||
//Called when a value holder, that returned the result given the row value, has changed its value.//
|
||||
IIterator iterator = rowDataSet.iterator();
|
||||
|
||||
while(iterator.hasNext()) {
|
||||
updateAssociation((RowData) iterator.next());
|
||||
}//while//
|
||||
}//heldValueChanged()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.view.Association#initialize(com.foundation.view.AssociationContainer)
|
||||
*/
|
||||
public void initialize(AssociationContainer associationContainer) {
|
||||
super.initialize(associationContainer);
|
||||
|
||||
if(children != null) {
|
||||
for(int index = 0; index < children.length; index++) {
|
||||
children[index].initialize(associationContainer);
|
||||
}//for//
|
||||
}//if//
|
||||
}//initialize()//
|
||||
}//CollectingMultiAssociation//
|
||||
@@ -0,0 +1,294 @@
|
||||
/*
|
||||
* Copyright (c) 2006,2009 Declarative Engineering LLC.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Declarative Engineering LLC
|
||||
* verson 1 which accompanies this distribution, and is available at
|
||||
* http://declarativeengineering.com/legal/DE_Developer_License_v1.txt
|
||||
*/
|
||||
package com.foundation.view;
|
||||
|
||||
import com.common.util.IList;
|
||||
import com.common.util.LiteHashMap;
|
||||
import com.common.util.LiteList;
|
||||
import com.foundation.event.EventSupport;
|
||||
|
||||
/**
|
||||
* Collects multiple results, one for each root association that matches the row type.
|
||||
* Note that due to efficiency and processing time requirements, only each root association results in a value or collection of values.
|
||||
* This may be changed in the future.
|
||||
*/
|
||||
public class CollectingMultiAssociationContainer extends AssociationContainer {
|
||||
/** The associations that make up the root of the tree. */
|
||||
private CollectingMultiAssociation[] associations = null;
|
||||
/** The map of association data indexed by the initial row value. */
|
||||
private LiteHashMap associationData = new LiteHashMap(1000);
|
||||
/** The maximum depth of the association tree. */
|
||||
private int maxDepth = 0;
|
||||
/** The array of CollectingMultiAssociation instances that match the indexing row class. */
|
||||
private LiteHashMap associationsByRowType = new LiteHashMap(10);
|
||||
|
||||
/**
|
||||
* The row data base class.
|
||||
*/
|
||||
public abstract static class RowData {
|
||||
/** The association which defines this row data. */
|
||||
private CollectingMultiAssociation association = null;
|
||||
|
||||
/**
|
||||
* RowData constructor.
|
||||
* @param association The association that is being serviced by the row data.
|
||||
*/
|
||||
public RowData(CollectingMultiAssociation association) {
|
||||
this.association = association;
|
||||
}//RowData()//
|
||||
/**
|
||||
* Gets the association that is being serviced by the row data.
|
||||
* @return The serviced association.
|
||||
*/
|
||||
public CollectingMultiAssociation getAssociation() {
|
||||
return association;
|
||||
}//getAssociation()//
|
||||
}//RowData//
|
||||
|
||||
public static class RowDataContainer {
|
||||
/** The row data array, one array for each matching root association. */
|
||||
private RowData[][] rowData = null;
|
||||
/** The associations which apply to the row represented by this container. */
|
||||
private CollectingMultiAssociation[] applicableAssociations = null;
|
||||
/** The row object for which this container exists. */
|
||||
private Object initialRowValue = null;
|
||||
/** The event support used for this row. */
|
||||
private EventSupport eventSupport = null;
|
||||
/** The user defined data associated with the row. */
|
||||
private Object data;
|
||||
|
||||
/**
|
||||
* RowDataContainer constructor.
|
||||
* @param maxDepth The maximum depth of the association tree.
|
||||
* @param initialRowValue The initial row value that the container represents data for.
|
||||
* @param data The user defined data associated with the row.
|
||||
*/
|
||||
public RowDataContainer(CollectingMultiAssociation[] applicableAssociations, int maxDepth, Object initialRowValue, Object data) {
|
||||
this.applicableAssociations = applicableAssociations;
|
||||
this.rowData = new RowData[applicableAssociations.length][maxDepth + 1];
|
||||
this.initialRowValue = initialRowValue;
|
||||
this.data = data;
|
||||
}//RowDataContainer()//
|
||||
/**
|
||||
* Gets the user defined data associated with the row.
|
||||
* @return The data that was passed to the container upon construction and is associated with the row by the user of the class.
|
||||
*/
|
||||
public Object getData() {
|
||||
return data;
|
||||
}//getData()//
|
||||
/**
|
||||
* Gets the event support object for this row.
|
||||
* @return The support object that manages event registrations.
|
||||
*/
|
||||
public EventSupport getEventSupport() {
|
||||
return eventSupport != null ? eventSupport : (eventSupport = new EventSupport(null));
|
||||
}//getEventSupport()//
|
||||
/**
|
||||
* Gets the associations which apply to the row represented by this container.
|
||||
* @return The set of associations that are navigated to get the values related to the row.
|
||||
*/
|
||||
public CollectingMultiAssociation[] getApplicableAssociations() {
|
||||
return applicableAssociations;
|
||||
}//getApplicableAssociations()//
|
||||
/**
|
||||
* Gets the last node to be navigated to in the tree.
|
||||
* @param firstAssociation The first association in the chain whose last association is to be located.
|
||||
* @return The last navigated multi association for the row value.
|
||||
*/
|
||||
protected CollectingMultiAssociation getLastNode(CollectingMultiAssociation firstAssociation) {
|
||||
RowData[] chain = rowData[getAssociationIndex(firstAssociation)];
|
||||
CollectingMultiAssociation result = null;
|
||||
|
||||
for(int index = chain.length - 1; result == null && index >= 0; index--) {
|
||||
if(chain[index] != null) {
|
||||
result = chain[index].association;
|
||||
}//if//
|
||||
}//for//
|
||||
|
||||
return result;
|
||||
/* Note: This doesn't work and is overly complex.
|
||||
CollectingMultiAssociation lastNode = null;
|
||||
int firstAssociationIndex = -1;
|
||||
|
||||
//Find the index of the first association in the chain.//
|
||||
for(int index = 0; (firstAssociationIndex == -1) && (index < applicableAssociations.length); index++) {
|
||||
if(applicableAssociations[index] == firstAssociation) {
|
||||
firstAssociationIndex = index;
|
||||
}//if//
|
||||
}//for//
|
||||
|
||||
//Navigate the associations in the chain to find the last one.//
|
||||
for(int index = 0; (index < rowData.length) && (rowData[firstAssociationIndex][index] != null); index++) {
|
||||
lastNode = rowData[firstAssociationIndex][index].getAssociation();
|
||||
}//for//
|
||||
|
||||
return lastNode;
|
||||
*/
|
||||
}//getFirstNode()//
|
||||
/**
|
||||
* Gets the row value that the CollectingMultiAssociationContainer was originally passed and which generated this container.
|
||||
* @return The row value in the control which the association is getting a result for.
|
||||
*/
|
||||
public Object getInitialRowValue() {
|
||||
return initialRowValue;
|
||||
}//getInitialRowValue()//
|
||||
/**
|
||||
* Gets the index in the row data array (first dimension) for the association.
|
||||
* @param association The association whose index is desired.
|
||||
* @return The association index.
|
||||
*/
|
||||
private int getAssociationIndex(CollectingMultiAssociation association) {
|
||||
int result = -1;
|
||||
|
||||
for(int index = 0; (result == -1) && (index < applicableAssociations.length); index++) {
|
||||
//Compare container indices since the passed association might be a sub-component of one of the root associations, but the container index will match it's parent.//
|
||||
if(applicableAssociations[index].getContainerIndex() == association.getContainerIndex()) {
|
||||
result = index;
|
||||
}//if//
|
||||
}//for//
|
||||
|
||||
return result;
|
||||
}//getAssociationIndex()//
|
||||
/**
|
||||
* Gets the row data for the association.
|
||||
* @param association The association whose row data is required.
|
||||
* @return The row data last set by the association.
|
||||
*/
|
||||
public RowData getRowData(CollectingMultiAssociation association) {
|
||||
return rowData[getAssociationIndex(association)][association.getDepth()];
|
||||
}//getRowData()//
|
||||
/**
|
||||
* Sets the row data for the association.
|
||||
* @param association The association whose row data is to be set.
|
||||
* @param rowData The row data to be saved for the association.
|
||||
*/
|
||||
public void setRowData(CollectingMultiAssociation association, RowData rowData) {
|
||||
this.rowData[getAssociationIndex(association)][association.getDepth()] = rowData;
|
||||
}//setRowData()//
|
||||
}//RowDataContainer//
|
||||
/**
|
||||
* CollectingMultiAssociationContainer constructor.
|
||||
*/
|
||||
public CollectingMultiAssociationContainer(CollectingMultiAssociation[] associations) {
|
||||
this.associations = associations;
|
||||
}//CollectingMultiAssociationContainer()//
|
||||
/**
|
||||
* Gets the associations that apply to the given row object.
|
||||
* @param row The object that might have related associations which are then used to access result values for the row.
|
||||
* @return The set of associations, or an empty array if the row has none.
|
||||
*/
|
||||
private CollectingMultiAssociation[] getApplicableAssociations(Object row) {
|
||||
CollectingMultiAssociation[] applicableAssociations = (CollectingMultiAssociation[]) associationsByRowType.get(row.getClass());
|
||||
|
||||
//If we have not yet cached the applicable associations then do so now.//
|
||||
if(applicableAssociations == null) {
|
||||
LiteList applicableAssociationList = new LiteList(associations.length);
|
||||
|
||||
//Find the all applicable associations.//
|
||||
for(int index = 0; index < associations.length; index++) {
|
||||
if(associations[index].isApplicable(row)) {
|
||||
applicableAssociationList.add(associations[index]);
|
||||
}//if//
|
||||
}//for//
|
||||
|
||||
applicableAssociations = new CollectingMultiAssociation[applicableAssociationList.getSize()];
|
||||
applicableAssociationList.toArray(applicableAssociations);
|
||||
associationsByRowType.put(row.getClass(), applicableAssociations);
|
||||
}//if//
|
||||
|
||||
return applicableAssociations;
|
||||
}//getApplicableAssociations()//
|
||||
/**
|
||||
* Checks a row to see if there are related associations.
|
||||
* @param row The row to be checked.
|
||||
* @return Whether the row has related associations that will result in values if registered. This is usefull information for trees trying to determine if it is possible for a node to have children.
|
||||
*/
|
||||
public boolean hasAssociations(Object row) {
|
||||
return getApplicableAssociations(row).length > 0;
|
||||
}//hasAssociations()//
|
||||
/**
|
||||
* Registers a row with the associations.
|
||||
* @param row The row to be registered.
|
||||
* @param data The user defined data associated with the row.
|
||||
*/
|
||||
public void register(Object row, Object data) {
|
||||
CollectingMultiAssociation[] applicableAssociations = getApplicableAssociations(row);
|
||||
|
||||
//If applicable associations were found then register the row with them.//
|
||||
if((applicableAssociations != null) && (applicableAssociations.length > 0)) {
|
||||
RowDataContainer rowDataContainer = new RowDataContainer(applicableAssociations, maxDepth, row, data);
|
||||
|
||||
associationData.put(row, rowDataContainer);
|
||||
|
||||
for(int index = 0; index < applicableAssociations.length; index++) {
|
||||
applicableAssociations[index].register(row, rowDataContainer);
|
||||
}//for//
|
||||
}//if//
|
||||
}//register()//
|
||||
/**
|
||||
* Unregisters a row with the associations.
|
||||
* @param row The row to be unregistered.
|
||||
*/
|
||||
public void unregister(Object row) {
|
||||
RowDataContainer rowDataContainer = (RowDataContainer) associationData.remove(row);
|
||||
|
||||
if(rowDataContainer != null) {
|
||||
CollectingMultiAssociation[] applicableAssociations = rowDataContainer.getApplicableAssociations();
|
||||
|
||||
for(int index = 0; index < applicableAssociations.length; index++) {
|
||||
applicableAssociations[index].unregister(row, rowDataContainer);
|
||||
}//for//
|
||||
}//if//
|
||||
}//unregister()//
|
||||
/**
|
||||
* Gets the values in the model.
|
||||
* @param row The row whose model value is required.
|
||||
* @return The collection of values the row resolves to. These values may not be in a perticular order, nor may the order match previous calls to getValues(Object).
|
||||
*/
|
||||
public IList getValues(Object row) {
|
||||
IList result = null;
|
||||
RowDataContainer rowDataContainer = (RowDataContainer) associationData.get(row);
|
||||
|
||||
//The row data container will be null if the row did not result in an association match.//
|
||||
if(rowDataContainer != null) {
|
||||
CollectingMultiAssociation[] applicableAssociations = rowDataContainer.getApplicableAssociations();
|
||||
|
||||
result = new LiteList(applicableAssociations.length);
|
||||
|
||||
//Iterate over the applicable association data arrays to collect the result values.//
|
||||
for(int index = 0; index < applicableAssociations.length; index++) {
|
||||
CollectingMultiAssociation association = rowDataContainer.getLastNode(applicableAssociations[index]);
|
||||
|
||||
if(association.isTargetAssociation()) {
|
||||
result.add(association.getResult(rowDataContainer));
|
||||
}//if//
|
||||
else {
|
||||
//TODO: Shouldn't this not add anything?
|
||||
result.add(null);
|
||||
}//else//
|
||||
}//for//
|
||||
}//if//
|
||||
|
||||
return result;
|
||||
}//getValues()//
|
||||
/**
|
||||
* Initializes the container of associations.
|
||||
*/
|
||||
public void initialize() {
|
||||
for(int index = 0; index < associations.length; index++) {
|
||||
associations[index].initialize(this);
|
||||
maxDepth = Math.max(maxDepth, associations[index].initializeAssociation(index, 0));
|
||||
}//for//
|
||||
}//initialize()//
|
||||
/**
|
||||
* Releases the container of associations.
|
||||
*/
|
||||
public void release() {
|
||||
//Does nothing for now.//
|
||||
}//release()//
|
||||
}//CollectingMultiAssociationContainer//
|
||||
@@ -0,0 +1,293 @@
|
||||
/*
|
||||
* Copyright (c) 2005,2009 Declarative Engineering LLC.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Declarative Engineering LLC
|
||||
* verson 1 which accompanies this distribution, and is available at
|
||||
* http://declarativeengineering.com/legal/DE_Developer_License_v1.txt
|
||||
*/
|
||||
package com.foundation.view;
|
||||
|
||||
import com.common.util.IIterator;
|
||||
import com.common.util.IList;
|
||||
import com.common.util.LiteHashMap;
|
||||
import com.common.util.LiteList;
|
||||
|
||||
/*
|
||||
* This is similar to the multi association except that it gathers a collection of results instead of a single result per item in the association.
|
||||
* The resulting collection may be a collection of collections or a collection of values, or a mixed bag.
|
||||
*/
|
||||
public class CollectingMultiResourceAssociation extends ResourceAssociation implements ICollectingMultiResourceAssociation {
|
||||
/** The container of association metadata. */
|
||||
private CollectingMultiAssociationContainer associationContainer = null;
|
||||
/** The listener which receives change notifications. */
|
||||
private IMultiResourceAssociationChangeListener changeListener = null;
|
||||
/** A mapping of item data objects indexed by the item they are representing. */
|
||||
private LiteHashMap itemDataByItemMap = new LiteHashMap(256);
|
||||
/** A flag indicating that the association is in the process of refreshing and should ignore update events. Update events may occur due to the model loading attribute values on demand. */
|
||||
private boolean isRefreshing = false;
|
||||
|
||||
/**
|
||||
* Encapsulates the resource/value association data for a single item.
|
||||
*/
|
||||
private static class ItemData {
|
||||
/** The item that this item data describes. */
|
||||
private Object item = null;
|
||||
/** The item's data object which will be passed with events. This can be any user defined value. */
|
||||
private Object data = null;
|
||||
/** The number of times the item has been registered. */
|
||||
private int registrationCounter = 1;
|
||||
/** The resource's current values. */
|
||||
private IList currentValues = null;
|
||||
|
||||
public ItemData(Object item, Object data) {
|
||||
this.item = item;
|
||||
this.data = data;
|
||||
}//ItemData()//
|
||||
}//ItemData//
|
||||
/**
|
||||
* CollectingMultiResourceAssociation constructor.
|
||||
* @param component The component that is using the resource association. This is used for debugging and for searching for value holders.
|
||||
* @param changeListener The listener which receives change notifications. This is often the component that is using this association.
|
||||
* @param viewContext The view context under which this association is being created. This will be used to access the view's resources.
|
||||
* @param valueType The type of value that the association is holding. This must be one of the TYPE_XXX identifiers defined in this class.
|
||||
* @param canSetValue Whether an altered value should be propegated back to the underlying object via the attribute if available, or set method if available.
|
||||
*/
|
||||
public CollectingMultiResourceAssociation(IAbstractComponent component, IMultiResourceAssociationChangeListener changeListener, IViewContext viewContext, int valueType, boolean canSetValue) {
|
||||
super(component, viewContext, valueType, canSetValue, null);
|
||||
|
||||
this.changeListener = changeListener;
|
||||
}//CollectingMultiResourceAssociation()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.view.ResourceAssociation#initialize()
|
||||
*/
|
||||
public void initialize() {
|
||||
if(associationContainer != null) {
|
||||
associationContainer.initialize();
|
||||
}//if//
|
||||
|
||||
super.initialize();
|
||||
}//initialize()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.view.ResourceAssociation#release()
|
||||
*/
|
||||
public void release() {
|
||||
if(associationContainer != null) {
|
||||
associationContainer.release();
|
||||
}//if//
|
||||
|
||||
super.release();
|
||||
}//release()//
|
||||
/**
|
||||
* Sets the associations.
|
||||
* @param associationContainer The container of associations.
|
||||
*/
|
||||
public void setAssociations(CollectingMultiAssociationContainer associationContainer) {
|
||||
//Note: Added this code to prevent changing the associations after initialization because allowing this makes the code way too complex and prevents use of the VariableResourceAssociation.//
|
||||
//TODO: Allow this... It will require sending control notifications for each row I think.
|
||||
if(isInitialized()) {
|
||||
throw new RuntimeException("Cannot set associations after the resource association has been initialized.");
|
||||
}//if//
|
||||
|
||||
if(this.associationContainer != null) {
|
||||
//TODO: Unregister all rows...
|
||||
this.associationContainer.release();
|
||||
}//if//
|
||||
|
||||
this.associationContainer = associationContainer;
|
||||
associationContainer.setResourceAssociation(this);
|
||||
|
||||
if(isInitialized()) {
|
||||
associationContainer.initialize();
|
||||
}//if//
|
||||
}//setAssociations()//
|
||||
/**
|
||||
* Determines whether there are any associations possible.
|
||||
* @return Whether there is any association metadata defined.
|
||||
*/
|
||||
public boolean hasAssociations() {
|
||||
return associationContainer != null;
|
||||
}//hasAssociations()//
|
||||
/**
|
||||
* Registers an item with the association.
|
||||
* <p>Items must be registered prior to getting their association value. When an item is no longer used it must be unregistered to free up memory.</p>
|
||||
* <p>Note: The same item may be registered multiple times, but the unregister method must be called an equal number of times to avoid memory leaks.</p>
|
||||
* @param item The user's item to be registered.
|
||||
* @param data The item's data object which will be passed with events. This can be any user defined value.
|
||||
*/
|
||||
public void registerItem(Object item, Object data) {
|
||||
//Optimize the case where no associations were defined.//
|
||||
if(hasAssociations()) {
|
||||
ItemData itemData = (ItemData) itemDataByItemMap.get(item);
|
||||
|
||||
if(itemData == null) {
|
||||
//Setup a new item data which will be accessed when the user refreshes or gets the value for the item.//
|
||||
itemData = new ItemData(item, data);
|
||||
itemDataByItemMap.put(item, itemData);
|
||||
associationContainer.register(item, itemData);
|
||||
}//if//
|
||||
else {
|
||||
//Increment the counter so we know when to remove the data when unregistering.//
|
||||
itemData.registrationCounter++;
|
||||
}//else//
|
||||
}//if//
|
||||
}//registerItem()//
|
||||
/**
|
||||
* Registers an item with the association.
|
||||
* <p>Items must be registered prior to getting their association value. When an item is no longer used it must be unregistered to free up memory.</p>
|
||||
* <p>Note: The same item may be registered multiple times, but the unregister method must be called an equal number of times to avoid memory leaks.</p>
|
||||
* @param item The user's item to be registered.
|
||||
* @deprecated
|
||||
*/
|
||||
public void registerItem(Object item) {
|
||||
registerItem(item, null);
|
||||
}//registerItem()//
|
||||
/**
|
||||
* Unregisters an item from the association.
|
||||
* <p>The user must call this method once for every call to register an item.</p>
|
||||
* @param item The user's item to be unregistered.
|
||||
*/
|
||||
public void unregisterItem(Object item) {
|
||||
//Optimize the case where no associations were defined.//
|
||||
if(hasAssociations()) {
|
||||
ItemData itemData = (ItemData) itemDataByItemMap.get(item);
|
||||
|
||||
//Reduce the counter by one so we can track how many times an items is un/registered.//
|
||||
itemData.registrationCounter--;
|
||||
|
||||
if(itemData.registrationCounter == 0) {
|
||||
//Remove the item data so we can GC it.//
|
||||
itemDataByItemMap.remove(item);
|
||||
associationContainer.unregister(item);
|
||||
itemData.item = null;
|
||||
itemData.currentValues = null;
|
||||
}//if//
|
||||
}//if//
|
||||
}//unregisterItem()//
|
||||
/**
|
||||
* Unregisters all items from the association.
|
||||
*/
|
||||
public void unregisterAllItems() {
|
||||
IIterator iterator = itemDataByItemMap.valueIterator();
|
||||
|
||||
//Clean up all the items.//
|
||||
while(iterator.hasNext()) {
|
||||
ItemData itemData = (ItemData) iterator.next();
|
||||
|
||||
associationContainer.unregister(itemData.item);
|
||||
|
||||
if(itemData.currentValues != null) {
|
||||
itemData.currentValues.removeAll();
|
||||
}//if//
|
||||
|
||||
itemData.currentValues = null;
|
||||
itemData.item = null;
|
||||
}//while//
|
||||
|
||||
//Remove all the items.//
|
||||
itemDataByItemMap.removeAll();
|
||||
}//unregisterItem()//
|
||||
/**
|
||||
* Gets the listener that is receiving change notifications. This is normally the component using the resource association.
|
||||
* @return The listener which must be notified when the associated value is altered externally.
|
||||
*/
|
||||
protected IMultiResourceAssociationChangeListener getChangeListener() {
|
||||
return changeListener;
|
||||
}//getChangeListener()//
|
||||
/**
|
||||
* Gets the item data container for the given item.
|
||||
* @param item The item whose resource association data is required.
|
||||
* @return The item data for the item, or null if one could not be found.
|
||||
*/
|
||||
private ItemData getItemData(Object item) {
|
||||
return (ItemData) itemDataByItemMap.get(item);
|
||||
}//getItemData()//
|
||||
/**
|
||||
* Gets the values for the association.
|
||||
* @param item The item identifying which item data to use.
|
||||
* @param results The optional result collection, allowing the caller to reuse the collection.
|
||||
* @return The static or resource values (depending on whether the value was set via a valid resource URL, was converted from a string, or was a properly typed value). Some collection values could be null.
|
||||
*/
|
||||
public final IList getValues(Object item, IList results) {
|
||||
IList result = results;
|
||||
|
||||
if(hasAssociations()) {
|
||||
ItemData itemData = getItemData(item);
|
||||
|
||||
if(results != null) {
|
||||
results.addAll(itemData.currentValues);
|
||||
}//if//
|
||||
else {
|
||||
result = itemData.currentValues == null ? LiteList.EMPTY_LIST : itemData.currentValues;
|
||||
}//else//
|
||||
}//if//
|
||||
else if(result == null) {
|
||||
result = LiteList.EMPTY_LIST;
|
||||
}//else if//
|
||||
|
||||
return result;
|
||||
}//getValues()//
|
||||
/**
|
||||
* Determines whether there are any associations related to the given item.
|
||||
* If there are no associations then any call to refresh and getValues will never result in any data.
|
||||
* @param item The item to be checked.
|
||||
* @return Whether the given item will ever result in data.
|
||||
*/
|
||||
public boolean hasAssociations(Object item) {
|
||||
return associationContainer != null && associationContainer.hasAssociations(item);
|
||||
}//hasAssociations()//
|
||||
/**
|
||||
* Refreshes the value held by this association with the latest data from the model.
|
||||
* Calling getValues without first calling this method will result in data from the time of the last refresh, which may not be the most current data in the model.
|
||||
* @param item The item identifying which item data to use.
|
||||
* @return Whether the resource value was updated. If it was, then a call to getValue() will return the latest value.
|
||||
* @see #getValues(java.lang.Object, com.common.util.IList)
|
||||
*/
|
||||
public boolean refresh(Object item) {
|
||||
boolean result = false;
|
||||
|
||||
//Optimize the case where no associations were defined.//
|
||||
if(hasAssociations()) {
|
||||
ItemData itemData = getItemData(item);
|
||||
|
||||
//There is never a refresh necessary if the control's default value is always used.//
|
||||
if(itemData != null) {
|
||||
isRefreshing = true;
|
||||
|
||||
try {
|
||||
IList modelValues = associationContainer.getValues(item);
|
||||
|
||||
itemData.currentValues = modelValues;
|
||||
result = true;
|
||||
}//try//
|
||||
finally {
|
||||
isRefreshing = false;
|
||||
}//finally//
|
||||
}//if//
|
||||
}//if//
|
||||
|
||||
return result;
|
||||
}//refresh()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.view.ResourceAssociation#internalRelease()
|
||||
*/
|
||||
protected void internalRelease() {
|
||||
unregisterAllItems();
|
||||
}//internalRelease()//
|
||||
/**
|
||||
* Called by the associations when the model has been altered.
|
||||
* @param row The row that has changed.
|
||||
*/
|
||||
public void modelChanged(Object row, Object data) {
|
||||
if(!isRefreshing) {
|
||||
ItemData itemData = (ItemData) data;
|
||||
|
||||
getChangeListener().onValueChanged(this, row, itemData.data, false);
|
||||
}//if//
|
||||
}//modelChanged()//
|
||||
/* (non-Javadoc)
|
||||
* @see java.lang.Object#toString()
|
||||
*/
|
||||
public String toString() {
|
||||
return "CollectingMultiResourceAssociation";
|
||||
}//toString()//
|
||||
}//CollectingMultiResourceAssociation//
|
||||
@@ -0,0 +1,395 @@
|
||||
/*
|
||||
* Copyright (c) 2006,2008 Declarative Engineering LLC.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Declarative Engineering LLC
|
||||
* verson 1 which accompanies this distribution, and is available at
|
||||
* http://declarativeengineering.com/legal/DE_Developer_License_v1.txt
|
||||
*/
|
||||
package com.foundation.view;
|
||||
|
||||
import com.common.util.IHashSet;
|
||||
import com.common.util.IIterator;
|
||||
import com.common.util.LiteHashSet;
|
||||
import com.foundation.event.EventSupport;
|
||||
import com.foundation.event.IEventEmitter;
|
||||
import com.foundation.event.IEventHandler;
|
||||
import com.foundation.util.IManagedCollection;
|
||||
import com.foundation.view.EventAssociation;
|
||||
import com.foundation.view.IAssociationHandler;
|
||||
import com.foundation.view.IEventAssociation;
|
||||
import com.foundation.view.IEventAssociationChangeListener;
|
||||
import com.foundation.view.IValueHolderListener;
|
||||
|
||||
/**
|
||||
* Models an association that results in one value for each row value given to it.
|
||||
*/
|
||||
public class CollectingSingleAssociation extends Association implements IEventAssociationChangeListener, IValueHolderListener, IEventHandler {
|
||||
/** The child associations in search order. This will be null only if this is a target association (the result is the resource association's result). */
|
||||
private CollectingSingleAssociation[] children = null;
|
||||
/** The set of events that the association registers for when the input changes. If any of these events are triggered it indicates that this association's value has changed. */
|
||||
private EventAssociation[] events = null;
|
||||
/** The depth of this association in the tree. */
|
||||
private int depth = 0;
|
||||
/** The index into the row data container for the array of row data where this row data exists. */
|
||||
private int containerIndex = 0;
|
||||
/** The row that last generated the result. This may be null if the association is related to a value holder. */
|
||||
private Object row = null;
|
||||
/** The last known result for this association. */
|
||||
private Object result = null;
|
||||
/** The last used child association for the current result. This will always be null for target associations. */
|
||||
private CollectingSingleAssociation resultChild = null;
|
||||
/** The support object that tracks events registered via the AssociationNodes. This will only be non-null if association attribute and nodes were provided. */
|
||||
private EventSupport nodeEventSupport = null;
|
||||
/**
|
||||
* CollectingSingleAssociation constructor.
|
||||
* @param rowType The type of row the association applies to. In the case of a root association in a single association this is the type held by the value holder.
|
||||
* @param associationNumber The association number which will be passed to the handler when the assocation is invoked. This number allows fast indexing of the methods that the handler handles. This value may be -1 if the input value should be used as the output value for the association (short circuiting).
|
||||
* @param handler The object that handles the method invocations to get and set the value.
|
||||
* @param invertLogic Whether the result of the association will be inverted. This is ignored unless the result is a Boolean value.
|
||||
* @param attributes The attributes to listen to and whose values are passed to the nodes. This may be null.
|
||||
* @param nodes The nodes of the arbitrary tree of listeners for value change events for this association's result value. This may be null.
|
||||
* @param children The child associations in search order. The children will be searched for the first match for this association's result. This should always be null for target associations, and should never be null for non-target associations.
|
||||
* @param events The non-null array of events that will be used to listen to the supplied row object for changes in the result.
|
||||
* @param valueHolderName The name of the optional value holder whose value holds the getter and setter methods used by the association. If no value holder is defined then the row object passed to the association must define the getter and setter methods.
|
||||
*/
|
||||
public CollectingSingleAssociation(Class rowType, int associationNumber, IAssociationHandler handler, boolean invertLogic, AssociationAttribute[] attributes, AssociationNode[] nodes, CollectingSingleAssociation[] children, EventAssociation[] events, String valueHolderName) {
|
||||
super(rowType, associationNumber, handler, invertLogic, attributes, nodes, valueHolderName);
|
||||
|
||||
if(events == null) {
|
||||
throw new IllegalArgumentException("The events parameter may not be null.");
|
||||
}//if//
|
||||
|
||||
this.children = children;
|
||||
this.events = events;
|
||||
|
||||
if(children != null) {
|
||||
for(int index = 0; index < children.length; index++) {
|
||||
children[index].setParent(this);
|
||||
}//for//
|
||||
}//if//
|
||||
|
||||
for(int index = 0; index < events.length; index++) {
|
||||
events[index].setChangeListener(this);
|
||||
}//for//
|
||||
}//CollectingSingleAssociation()//
|
||||
/**
|
||||
* Determines whether this is a target association meaning that its result is the value assigned to the initial association input.
|
||||
* @return Whether this association is a target and the result is not placed as input to another association.
|
||||
*/
|
||||
public boolean isTargetAssociation() {
|
||||
return children == null;
|
||||
}//isTargetAssociation()//
|
||||
/**
|
||||
* Initializes the container index and depth for this multi association.
|
||||
* @param containerIndex The index into the row data container for the array of row data where this row data exists.
|
||||
* @param depth The depth of the association in the tree.
|
||||
* @return The deepest this branch of the tree got.
|
||||
*/
|
||||
public int initializeAssociation(int containerIndex, int depth) {
|
||||
this.depth = depth;
|
||||
this.containerIndex = containerIndex;
|
||||
|
||||
if(children != null) {
|
||||
for(int index = 0; index < children.length; index++) {
|
||||
depth = Math.max(depth, children[index].initializeAssociation(containerIndex, this.depth + 1));
|
||||
}//for//
|
||||
}//if//
|
||||
|
||||
return depth;
|
||||
}//initializeAssociation()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.view.Association#release()
|
||||
*/
|
||||
public void release() {
|
||||
super.release();
|
||||
}//release()//
|
||||
/**
|
||||
* Gets the index into the row data container for the array of row data where this row data exists.
|
||||
* @return The first dimension's index in the row data array in the container.
|
||||
*/
|
||||
public int getContainerIndex() {
|
||||
return containerIndex;
|
||||
}//getContainerIndex()//
|
||||
/**
|
||||
* Gets the latest result for the association.
|
||||
* @return The association's current result.
|
||||
*/
|
||||
public Object getResult() {
|
||||
return result;
|
||||
}//getResult()//
|
||||
/**
|
||||
* Gets the current result of the association from the model.
|
||||
* @param row The row object that is the input to the association.
|
||||
* @return The result of the association.
|
||||
*/
|
||||
protected Object internalGetResult() {
|
||||
Object result;
|
||||
|
||||
if(getIsValueHolderAssociated()) {
|
||||
if(getAssociationNumber() != -1) {
|
||||
result = getHandler().invokeMethod(getAssociationNumber(), getValueHolder().getValue(), null, IAssociationHandler.INVOKE_GETTER_METHOD_FLAG);
|
||||
}//if//
|
||||
else {
|
||||
result = getValueHolder().getValue();
|
||||
}//else//
|
||||
}//if//
|
||||
else {
|
||||
if(getAssociationNumber() != -1) {
|
||||
result = getHandler().invokeMethod(getAssociationNumber(), row, null, IAssociationHandler.INVOKE_GETTER_METHOD_FLAG);
|
||||
}//if//
|
||||
else {
|
||||
result = row;
|
||||
}//else//
|
||||
}//else//
|
||||
|
||||
result = convertValueToControl(result);
|
||||
|
||||
return result;
|
||||
}//getResult()//
|
||||
/**
|
||||
* Sets the new result of the association into the model.
|
||||
* @param row The row object that is the input to the association.
|
||||
* @param result The new result which will be placed in the model.
|
||||
*/
|
||||
protected void setResult(Object result) {
|
||||
result = convertValueToModel(result);
|
||||
|
||||
if(getIsValueHolderAssociated()) {
|
||||
if(getAssociationNumber() != -1) {
|
||||
if(oneArray == null) {
|
||||
oneArray = new Object[1];
|
||||
}//if//
|
||||
|
||||
oneArray[0] = result;
|
||||
getHandler().invokeMethod(getAssociationNumber(), getValueHolder().getValue(), oneArray, IAssociationHandler.INVOKE_SETTER_METHOD_FLAG);
|
||||
oneArray[0] = null;
|
||||
}//if//
|
||||
}//if//
|
||||
else {
|
||||
if(getAssociationNumber() != -1) {
|
||||
if(oneArray == null) {
|
||||
oneArray = new Object[1];
|
||||
}//if//
|
||||
|
||||
oneArray[0] = result;
|
||||
getHandler().invokeMethod(getAssociationNumber(), row, oneArray, IAssociationHandler.INVOKE_SETTER_METHOD_FLAG);
|
||||
oneArray[0] = null;
|
||||
}//if//
|
||||
}//else//
|
||||
}//setResult()//
|
||||
/**
|
||||
* Called by the event handlers to update the association state when the result may have altered.
|
||||
*/
|
||||
protected void updateAssociation() {
|
||||
Object newResult = internalGetResult();
|
||||
|
||||
if(newResult != result) {
|
||||
unregisterChildren();
|
||||
unregisterListeners(row);
|
||||
result = newResult;
|
||||
registerChildren(newResult);
|
||||
registerListeners(row);
|
||||
//Notify the ResourceAssociation.//
|
||||
((CollectingSingleResourceAssociation) getAssociationContainer().getResourceAssociation()).modelChanged();
|
||||
}//if//
|
||||
else {
|
||||
//Always re-register all the extended listeners since they cannot handle attribute changes on their own.//
|
||||
unregisterListeners(result);
|
||||
registerListeners(result);
|
||||
}//else//
|
||||
}//updateAssociation()//
|
||||
/**
|
||||
* Gets the last association node in the chain.
|
||||
* @return The final association which holds the actual result.
|
||||
*/
|
||||
public CollectingSingleAssociation getLastNode() {
|
||||
return (resultChild == null) ? this : resultChild.getLastNode();
|
||||
}//getLastNode()//
|
||||
/**
|
||||
* Registers a value with the association for use by a single association.
|
||||
* @param row The next value in the chain of associations for the single association.
|
||||
* @param rowData The array of row data used by the associations to store depth specific data. This association may only access the index at its depth.
|
||||
*/
|
||||
public void register() {
|
||||
register(internalGetResult());
|
||||
}//register()//
|
||||
/**
|
||||
* Registers a value with the association for use by a single association.
|
||||
* @param result The result for the row.
|
||||
*/
|
||||
protected void register(Object result) {
|
||||
//Store the result.//
|
||||
this.result = result;
|
||||
this.resultChild = null;
|
||||
|
||||
//Register all events.//
|
||||
for(int index = 0; index < events.length; index++) {
|
||||
EventAssociation eventAssociation = (EventAssociation) events[index];
|
||||
|
||||
if(eventAssociation.getIsValueHolderAssociated()) {
|
||||
//Register for the value holder's value changing and for the event on the held value (uses the value holder's event support).//
|
||||
eventAssociation.register();
|
||||
}//if//
|
||||
else if(row instanceof IEventEmitter) {
|
||||
//Register with the event on the row object using the resource association's event support.//
|
||||
((CollectingSingleAssociationContainer) getAssociationContainer()).getEventSupport().register((IEventEmitter) row, eventAssociation.getEventNumber(), this, true);
|
||||
}//else//
|
||||
}//for//
|
||||
|
||||
//Register the listener with the value holder.//
|
||||
if(getIsValueHolderAssociated()) {
|
||||
getValueHolder().registerListener(this);
|
||||
}//if//
|
||||
|
||||
registerChildren(result);
|
||||
registerListeners(row);
|
||||
}//register()//
|
||||
/**
|
||||
* Registers the result of this association with the child associations.
|
||||
* @param result The result of this association which will become the row input for the child association(s).
|
||||
* @param rowDataContainer The container of row data used by the associations to store data.
|
||||
* @param rowData The data for the row that is registering.
|
||||
*/
|
||||
protected void registerChildren(Object result) {
|
||||
//Register with the first child association, if one exists and matches the input type.//
|
||||
if(children != null) {
|
||||
//Iterate over the children looking for the first match.//
|
||||
for(int index = 0; (resultChild == null) && (index < children.length); index++) {
|
||||
if(children[index].isApplicable(result)) {
|
||||
resultChild = children[index];
|
||||
resultChild.register(result);
|
||||
}//if//
|
||||
}//for//
|
||||
}//if//
|
||||
}//registerChildren()//
|
||||
/**
|
||||
* Registers listeners with the result.
|
||||
* @param row The association's row to register all extended listeners with.
|
||||
*/
|
||||
protected void registerListeners(Object row) {
|
||||
AssociationAttribute[] attributes = getAttributes();
|
||||
|
||||
if(attributes != null) {
|
||||
IHashSet trackedValues = new LiteHashSet(100);
|
||||
|
||||
if(nodeEventSupport == null) {
|
||||
nodeEventSupport = new EventSupport(null);
|
||||
}//if//
|
||||
|
||||
//TODO: It would be more efficient in some cases if we detected duplicate results for different row datas and had the events go to all affected row data's while only registering once.
|
||||
//This idea could turn out to be less efficient than the current design since it would require a more complex data structure.
|
||||
|
||||
for(int index = 0; index < attributes.length; index++) {
|
||||
Object attributeValue = attributes[index].getValue(row);
|
||||
|
||||
if(!trackedValues.containsValue(attributeValue)) {
|
||||
trackedValues.add(attributeValue);
|
||||
|
||||
//TODO: Should we support other collections? If so we would need to be notified when there are changes...
|
||||
if(attributeValue instanceof IManagedCollection) {
|
||||
IIterator iterator = ((IManagedCollection) attributeValue).iterator();
|
||||
|
||||
//Register for changes in the collected values.//
|
||||
while(iterator.hasNext()) {
|
||||
registerListeners(nodeEventSupport, trackedValues, this, iterator.next());
|
||||
}//while//
|
||||
|
||||
//Register for changes in the collection.//
|
||||
nodeEventSupport.register((IManagedCollection) attributeValue, IManagedCollection.EVENT, this, true);
|
||||
}//if//
|
||||
else {
|
||||
//Register for changes in the attribute value.//
|
||||
registerListeners(nodeEventSupport, trackedValues, this, attributeValue);
|
||||
}//else//
|
||||
}//if//
|
||||
}//for//
|
||||
}//if//
|
||||
}//registerListeners()//
|
||||
/**
|
||||
* Unregisters the previous registration.
|
||||
* @param row The optional row value. This should be null if this is the root association.
|
||||
*/
|
||||
public void unregister(Object row) {
|
||||
unregisterChildren();
|
||||
|
||||
//Unregister all events.//
|
||||
for(int index = 0; index < events.length; index++) {
|
||||
EventAssociation eventAssociation = (EventAssociation) events[index];
|
||||
|
||||
if(eventAssociation.getIsValueHolderAssociated()) {
|
||||
//Register for the value holder's value changing and for the event on the held value (uses the value holder's event support).//
|
||||
eventAssociation.unregister();
|
||||
}//if//
|
||||
else if(row instanceof IEventEmitter) {
|
||||
//Register with the event on the row object using the resource association's event support.//
|
||||
((CollectingSingleAssociationContainer) getAssociationContainer()).getEventSupport().unregister((IEventEmitter) row, eventAssociation.getEventNumber(), this);
|
||||
}//else//
|
||||
}//for//
|
||||
|
||||
//Unregister the listener with the value holder.//
|
||||
if(getIsValueHolderAssociated()) {
|
||||
getValueHolder().unregisterListener(this);
|
||||
}//if//
|
||||
|
||||
unregisterListeners(row);
|
||||
}//unregister()//
|
||||
/**
|
||||
* Unregisters the result of this association with the child associations.
|
||||
*/
|
||||
protected void unregisterChildren() {
|
||||
if(resultChild != null) {
|
||||
resultChild.unregister(result);
|
||||
resultChild = null;
|
||||
}//if//
|
||||
}//unregisterChildren()//
|
||||
/**
|
||||
* Unregisters listeners with the result.
|
||||
* @param row The association's row to unregister all extended listeners with.
|
||||
*/
|
||||
protected void unregisterListeners(Object row) {
|
||||
AssociationAttribute[] attributes = getAttributes();
|
||||
|
||||
if(attributes != null) {
|
||||
nodeEventSupport.unregisterAll();
|
||||
}//if//
|
||||
}//unregisterListeners()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.view.IEventAssociationChangeListener#onEventFired(com.foundation.view.IEventAssociation, java.lang.Object[])
|
||||
*/
|
||||
public void onEventFired(IEventAssociation eventAssociation, Object[] eventArguments) {
|
||||
//Called when an event is fired from a value holder. In this case all rows are affected by the event.//
|
||||
updateAssociation();
|
||||
}//onEventFired()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.view.IValueHolderListener#heldValueChanged()
|
||||
*/
|
||||
public void heldValueChanged() {
|
||||
//Called when a value holder, that returned the result given the row value, has changed its value.//
|
||||
updateAssociation();
|
||||
}//heldValueChanged()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.view.Association#initialize(com.foundation.view.AssociationContainer)
|
||||
*/
|
||||
public void initialize(AssociationContainer associationContainer) {
|
||||
super.initialize(associationContainer);
|
||||
|
||||
if(children != null) {
|
||||
for(int index = 0; index < children.length; index++) {
|
||||
children[index].initialize(associationContainer);
|
||||
}//for//
|
||||
}//if//
|
||||
}//initialize()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.event.IEventHandler#evaluate(com.foundation.event.IEventEmitter, int, java.lang.Object[], int)
|
||||
*/
|
||||
public void evaluate(IEventEmitter eventEmitter, int eventNumber, Object[] eventParameters, int flags) {
|
||||
//Called when an event, registered on the row object that generated the result, is fired.//
|
||||
updateAssociation();
|
||||
}//evaluate()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.event.IHandler#evaluate(int, java.lang.Object[], int)
|
||||
*/
|
||||
public void evaluate(int eventNumber, Object[] eventParameters, int flags) {
|
||||
//Never called.//
|
||||
}//evaluate()//
|
||||
}//CollectingSingleAssociation//
|
||||
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
* Copyright (c) 2006,2007 Declarative Engineering LLC.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Declarative Engineering LLC
|
||||
* verson 1 which accompanies this distribution, and is available at
|
||||
* http://declarativeengineering.com/legal/DE_Developer_License_v1.txt
|
||||
*/
|
||||
package com.foundation.view;
|
||||
|
||||
import com.common.util.IList;
|
||||
import com.common.util.LiteList;
|
||||
import com.foundation.event.EventSupport;
|
||||
|
||||
/**
|
||||
* Collects multiple results, one for each root association that matches the row type.
|
||||
* Note that due to efficiency and processing time requirements, only each root association results in a value or collection of values.
|
||||
* This may be changed in the future.
|
||||
*/
|
||||
public class CollectingSingleAssociationContainer extends AssociationContainer {
|
||||
/** The associations that make up the root of the tree. */
|
||||
private CollectingSingleAssociation[] associations = null;
|
||||
/** The support object that tracks events registered by the association. */
|
||||
private EventSupport eventSupport = new EventSupport(null);
|
||||
/**
|
||||
* CollectingSingleAssociationContainer constructor.
|
||||
*/
|
||||
public CollectingSingleAssociationContainer(CollectingSingleAssociation[] associations) {
|
||||
this.associations = associations;
|
||||
}//CollectingSingleAssociationContainer()//
|
||||
/**
|
||||
* Registers a row with the associations.
|
||||
*/
|
||||
public void register() {
|
||||
//If applicable associations were found then register the row with them.//
|
||||
if((associations != null) && (associations.length > 0)) {
|
||||
for(int index = 0; index < associations.length; index++) {
|
||||
associations[index].register();
|
||||
}//for//
|
||||
}//if//
|
||||
}//register()//
|
||||
/**
|
||||
* Unregisters a row with the associations.
|
||||
*/
|
||||
public void unregister() {
|
||||
if((associations != null) && (associations.length > 0)) {
|
||||
for(int index = 0; index < associations.length; index++) {
|
||||
associations[index].unregister(null);
|
||||
}//for//
|
||||
}//if//
|
||||
}//unregister()//
|
||||
/**
|
||||
* Gets the event support.
|
||||
* @return The support object usable by the association to register for events.
|
||||
*/
|
||||
public EventSupport getEventSupport() {
|
||||
return eventSupport;
|
||||
}//getEventSupport()//
|
||||
/**
|
||||
* Gets the values in the model.
|
||||
* @return The collection of values the row resolves to. These values may not be in a perticular order, nor may the order match previous calls to getValues(Object).
|
||||
*/
|
||||
public IList getValues() {
|
||||
IList result = new LiteList(associations.length);
|
||||
|
||||
//Iterate over the applicable association data arrays to collect the result values.//
|
||||
for(int index = 0; index < associations.length; index++) {
|
||||
CollectingSingleAssociation association = associations[index].getLastNode();
|
||||
|
||||
if(association.isTargetAssociation()) {
|
||||
result.add(association.getResult());
|
||||
}//if//
|
||||
else {
|
||||
result.add(null);
|
||||
}//else//
|
||||
}//for//
|
||||
|
||||
return result;
|
||||
}//getValues()//
|
||||
/**
|
||||
* Initializes the container of associations.
|
||||
*/
|
||||
public void initialize() {
|
||||
for(int index = 0; index < associations.length; index++) {
|
||||
associations[index].initialize(this);
|
||||
}//for//
|
||||
}//initialize()//
|
||||
/**
|
||||
* Releases the container of associations.
|
||||
*/
|
||||
public void release() {
|
||||
//Does nothing for now.//
|
||||
}//release()//
|
||||
}//CollectingSingleAssociationContainer//
|
||||
@@ -0,0 +1,202 @@
|
||||
/*
|
||||
* Copyright (c) 2005,2009 Declarative Engineering LLC.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Declarative Engineering LLC
|
||||
* verson 1 which accompanies this distribution, and is available at
|
||||
* http://declarativeengineering.com/legal/DE_Developer_License_v1.txt
|
||||
*/
|
||||
package com.foundation.view;
|
||||
|
||||
import com.common.util.IList;
|
||||
import com.common.util.LiteList;
|
||||
|
||||
/**
|
||||
* <p>Warning (TODO): This code has never been used or tested.</p>
|
||||
* <p>TODO: The getValues() should internalize the flattening of the collections into one managed list. Currently it returns a list of lists, arrays, and objects. It is currently left to the control to flatten this into a single list.</p>
|
||||
* This is similar to the single association except that it gathers a collection of results instead of a single result in the association.
|
||||
* The resulting collection may be a collection of collections or a collection of values, or a mixed bag.
|
||||
*/
|
||||
public class CollectingSingleResourceAssociation extends ResourceAssociation implements ICollectingSingleResourceAssociation {
|
||||
/** The container of association metadata. */
|
||||
private CollectingSingleAssociationContainer associationContainer = null;
|
||||
/** The listener which receives change notifications. */
|
||||
private ISingleResourceAssociationChangeListener changeListener = null;
|
||||
/** A flag indicating that the association is in the process of refreshing and should ignore update events. Update events may occur due to the model loading attribute values on demand. */
|
||||
private boolean isRefreshing = false;
|
||||
/** The resource's current values. */
|
||||
private IList currentValues = null;
|
||||
/**
|
||||
* CollectingSingleResourceAssociation constructor.
|
||||
* @param component The component that is using the resource association. This is used for debugging and for searching for value holders.
|
||||
* @param changeListener The listener which receives change notifications. This is often the component that is using this association.
|
||||
* @param viewContext The view context under which this association is being created. This will be used to access the view's resources.
|
||||
* @param valueType The type of value that the association is holding. This must be one of the TYPE_XXX identifiers defined in this class.
|
||||
* @param canSetValue Whether an altered value should be propegated back to the underlying object via the attribute if available, or set method if available.
|
||||
*/
|
||||
public CollectingSingleResourceAssociation(IAbstractComponent component, ISingleResourceAssociationChangeListener changeListener, IViewContext viewContext, int valueType, boolean canSetValue) {
|
||||
super(component, viewContext, valueType, canSetValue, null);
|
||||
|
||||
this.changeListener = changeListener;
|
||||
}//CollectingSingleResourceAssociation()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.view.ResourceAssociation#initialize()
|
||||
*/
|
||||
public void initialize() {
|
||||
if(associationContainer != null) {
|
||||
associationContainer.initialize();
|
||||
}//if//
|
||||
|
||||
super.initialize();
|
||||
}//initialize()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.view.ResourceAssociation#release()
|
||||
*/
|
||||
public void release() {
|
||||
if(associationContainer != null) {
|
||||
associationContainer.release();
|
||||
}//if//
|
||||
|
||||
super.release();
|
||||
}//release()//
|
||||
/**
|
||||
* Sets the associations.
|
||||
* @param associationContainer The container of associations.
|
||||
*/
|
||||
public void setAssociations(CollectingSingleAssociationContainer associationContainer) {
|
||||
//Note: Added this code to prevent changing the associations after initialization because allowing this makes the code way too complex and prevents use of the VariableResourceAssociation.//
|
||||
//TODO: Allow this... It will require sending control notifications for each row I think.
|
||||
if(isInitialized()) {
|
||||
throw new RuntimeException("Cannot set associations after the resource association has been initialized.");
|
||||
}//if//
|
||||
|
||||
if(this.associationContainer != null) {
|
||||
this.associationContainer.release();
|
||||
}//if//
|
||||
|
||||
this.associationContainer = associationContainer;
|
||||
associationContainer.setResourceAssociation(this);
|
||||
|
||||
if(isInitialized()) {
|
||||
associationContainer.initialize();
|
||||
}//if//
|
||||
}//setAssociations()//
|
||||
/**
|
||||
* Determines whether there are any associations possible.
|
||||
* @return Whether there is any association metadata defined.
|
||||
*/
|
||||
public boolean hasAssociations() {
|
||||
return associationContainer != null;
|
||||
}//hasAssociations()//
|
||||
/**
|
||||
* Registers an item with the association.
|
||||
* <p>Items must be registered prior to getting their association value. When an item is no longer used it must be unregistered to free up memory.</p>
|
||||
* <p>Note: The same item may be registered multiple times, but the unregister method must be called an equal number of times to avoid memory leaks.</p>
|
||||
* @param item The user's item to be registered.
|
||||
*/
|
||||
public void register() {
|
||||
//Optimize the case where no associations were defined.//
|
||||
if(hasAssociations()) {
|
||||
associationContainer.register();
|
||||
}//if//
|
||||
}//register()//
|
||||
/**
|
||||
* Unregisters an item from the association.
|
||||
* <p>The user must call this method once for every call to register an item.</p>
|
||||
* @param item The user's item to be unregistered.
|
||||
*/
|
||||
public void unregister() {
|
||||
//Optimize the case where no associations were defined.//
|
||||
if(hasAssociations()) {
|
||||
associationContainer.unregister();
|
||||
}//if//
|
||||
}//unregister()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.view.ResourceAssociation#rebuildApplicableAssociations()
|
||||
*/
|
||||
protected void rebuildApplicableAssociations() {
|
||||
if(isInitialized()) {
|
||||
unregister();
|
||||
register();
|
||||
//Notify the owner so it can refresh the display.//
|
||||
getChangeListener().onValueChanged(this, 0);
|
||||
}//if//
|
||||
}//rebuildApplicableAssociations()//
|
||||
/**
|
||||
* Gets the listener that is receiving change notifications. This is normally the component using the resource association.
|
||||
* @return The listener which must be notified when the associated value is altered externally.
|
||||
*/
|
||||
protected ISingleResourceAssociationChangeListener getChangeListener() {
|
||||
return changeListener;
|
||||
}//getChangeListener()//
|
||||
/**
|
||||
* Gets the values for the association.
|
||||
* @param item The item identifying which item data to use.
|
||||
* @param results The optional result collection, allowing the caller to reuse the collection.
|
||||
* @return The static or resource values (depending on whether the value was set via a valid resource URL, was converted from a string, or was a properly typed value). Some collection values could be null.
|
||||
*/
|
||||
public final IList getValues(IList results) {
|
||||
IList result = results;
|
||||
|
||||
if(hasAssociations()) {
|
||||
if(results != null) {
|
||||
results.addAll(currentValues);
|
||||
}//if//
|
||||
else {
|
||||
result = currentValues;
|
||||
}//else//
|
||||
}//if//
|
||||
else if(result == null) {
|
||||
result = LiteList.EMPTY_LIST;
|
||||
}//else if//
|
||||
|
||||
return result;
|
||||
}//getValues()//
|
||||
/**
|
||||
* Refreshes the value held by this association with the latest data from the model.
|
||||
* Calling getValues without first calling this method will result in data from the time of the last refresh, which may not be the most current data in the model.
|
||||
* @param item The item identifying which item data to use.
|
||||
* @return Whether the resource value was updated. If it was, then a call to getValue() will return the latest value.
|
||||
* @see #getValues(java.lang.Object, com.common.util.IList)
|
||||
*/
|
||||
public boolean refresh() {
|
||||
boolean result = false;
|
||||
|
||||
//Optimize the case where no associations were defined.//
|
||||
if(hasAssociations()) {
|
||||
isRefreshing = true;
|
||||
|
||||
try {
|
||||
IList modelValues = associationContainer.getValues();
|
||||
|
||||
currentValues = modelValues;
|
||||
result = true;
|
||||
}//try//
|
||||
finally {
|
||||
isRefreshing = false;
|
||||
}//finally//
|
||||
}//if//
|
||||
|
||||
return result;
|
||||
}//refresh()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.view.ResourceAssociation#internalRelease()
|
||||
*/
|
||||
protected void internalRelease() {
|
||||
unregister();
|
||||
}//internalRelease()//
|
||||
/**
|
||||
* Called by the associations when the model has been altered.
|
||||
* @param row The row that has changed.
|
||||
*/
|
||||
public void modelChanged() {
|
||||
if(!isRefreshing) {
|
||||
getChangeListener().onValueChanged(this, 0);
|
||||
}//if//
|
||||
}//modelChanged()//
|
||||
/* (non-Javadoc)
|
||||
* @see java.lang.Object#toString()
|
||||
*/
|
||||
public String toString() {
|
||||
return "CollectingSingleResourceAssociation";
|
||||
}//toString()//
|
||||
}//CollectingSingleResourceAssociation//
|
||||
@@ -0,0 +1,209 @@
|
||||
/*
|
||||
* Copyright (c) 2006,2009 Declarative Engineering LLC.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Declarative Engineering LLC
|
||||
* verson 1 which accompanies this distribution, and is available at
|
||||
* http://declarativeengineering.com/legal/DE_Developer_License_v1.txt
|
||||
*/
|
||||
package com.foundation.view;
|
||||
|
||||
import com.common.util.*;
|
||||
import com.foundation.util.*;
|
||||
import com.foundation.event.*;
|
||||
|
||||
/*
|
||||
* This adapter is meant to enhance the AttributeAssociation such that all the complex linkage between a collection attribute and the user control are simplified into one interface (ICollectionChangeListener).
|
||||
* The user will create a new CollectionAttributeAssociationAdapter providing an implementation of ICollectionChangeListener, then when the related attribute association is changed the user will call the setAttributeAssociation method which will update all the bindings and if necessary call the listener.
|
||||
* The listener will be notified of all collection changes and should take appropriate action for the specific control.
|
||||
* The control can also add item events to this adapter which will cause the adapter to listen on the collection items for those events.
|
||||
* <p>Note: The user control must add the item events prior to setting the first attribute association so that all collection items are properly listened to.</p>
|
||||
*/
|
||||
public class CollectionAttributeAssociationAdapter implements IAttributeAssociationChangeListener, IEventHandler {
|
||||
/** The set of events to be registered with each item in the collection. */
|
||||
private EventAssociationSet itemEvents = null;
|
||||
/** The related attribute association whose value is a collection. */
|
||||
private IAttributeAssociation attributeAssociation = null;
|
||||
/** The current value of the attribute related to this adapter. */
|
||||
private Object currentValue = null;
|
||||
/** A support for receiving collection change events. */
|
||||
private EventSupport eventSupport = new EventSupport(null);
|
||||
/** The listener for collection change events. */
|
||||
private ICollectionChangeListener collectionListener = null;
|
||||
|
||||
/**
|
||||
* Defines the methods called by this adapter when the collection is altered.
|
||||
*/
|
||||
public interface ICollectionChangeListener {
|
||||
/**
|
||||
* Called when one of the collection items fires an event that was being listened to.
|
||||
* @param eventName The name of the event which was fired.
|
||||
* @param item The collection item which fired the event.
|
||||
* @param eventParameters The set of event specific parameters which may be null or empty.
|
||||
*/
|
||||
public void collectionItemChanged(String eventName, Object item, Object[] eventParameters);
|
||||
/**
|
||||
* Called when the whole collection is replaced.
|
||||
* @param oldValues The collection of values in the old collection, or possibly null if there wasn't an old collection.
|
||||
* @param newValues The collection of values in the new collection, or possibly null if there isn't a new collection.
|
||||
*/
|
||||
public void collectionChanged(ICollection oldValues, ICollection newValues);
|
||||
/**
|
||||
* Called when an item is added to the current collection.
|
||||
* @param item The item that was added.
|
||||
*/
|
||||
public void collectionItemAdded(Object item);
|
||||
/**
|
||||
* Called when an item is removed from the current collection.
|
||||
* @param item The item that was removed.
|
||||
*/
|
||||
public void collectionItemRemoved(Object item);
|
||||
/**
|
||||
* Called when items are added to the current collection.
|
||||
* @param items The items that were added.
|
||||
*/
|
||||
public void collectionItemsAdded(ICollection items);
|
||||
/**
|
||||
* Called when items are removed from the current collection.
|
||||
* @param items The items that were removed.
|
||||
*/
|
||||
public void collectionItemsRemoved(ICollection items);
|
||||
}//ICollectionChangeListener//
|
||||
/**
|
||||
* CollectionAttributeAssociationAdapter constructor.
|
||||
* @param collectionListener The handler to be called when a collection events occur.
|
||||
*/
|
||||
public CollectionAttributeAssociationAdapter(ICollectionChangeListener collectionListener) {
|
||||
super();
|
||||
this.collectionListener = collectionListener;
|
||||
this.itemEvents = new EventAssociationSet(this);
|
||||
}//CollectionAttributeAssociationAdapter()//
|
||||
/**
|
||||
* Gets the listener for collection changes.
|
||||
* @return The listener which will be notified when the collection is altered.
|
||||
*/
|
||||
private ICollectionChangeListener getCollectionListener() {
|
||||
return collectionListener;
|
||||
}//getCollectionListener()//
|
||||
/**
|
||||
* Sets the attribute association related to this adapter.
|
||||
* @param attributeAssociation The related attribute association whose value is a collection.
|
||||
*/
|
||||
public void setAttributeAssociation(IAttributeAssociation attributeAssociation) {
|
||||
if(this.attributeAssociation != attributeAssociation) {
|
||||
if(this.attributeAssociation != null) {
|
||||
this.attributeAssociation.setChangeListener((IEventAssociationChangeListener) null);
|
||||
}//if//
|
||||
|
||||
this.attributeAssociation = attributeAssociation;
|
||||
|
||||
if(attributeAssociation != null) {
|
||||
attributeAssociation.setChangeListener(this);
|
||||
}//if//
|
||||
|
||||
replaceCollectionValue();
|
||||
}//if//
|
||||
}//setAttributeAssociation()//
|
||||
/**
|
||||
* Adds an event to the set of events listened to on the collection values.
|
||||
* @param eventAssociation The event association to be added.
|
||||
*/
|
||||
public void addEventAssociation(IEventAssociation eventAssociation) {
|
||||
itemEvents.addEventAssociation(eventAssociation);
|
||||
}//addEventAssociation()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.view.IAttributeAssociationChangeListener#onValueChanged(com.foundation.view.IAttributeAssociation)
|
||||
*/
|
||||
public void onValueChanged(IAttributeAssociation attributeAssociation) {
|
||||
//This method is called when the current collection changes.//
|
||||
if(this.attributeAssociation == attributeAssociation) {
|
||||
replaceCollectionValue();
|
||||
}//if//
|
||||
}//onValueChanged()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.event.IHandler#evaluate(int, java.lang.Object[], int)
|
||||
*/
|
||||
public void evaluate(int eventNumber, Object[] eventParameters, int flags) {
|
||||
//This method is never called.//
|
||||
}//evaluate()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.event.IEventHandler#evaluate(com.foundation.event.IEventEmitter, int, java.lang.Object[], int)
|
||||
*/
|
||||
public void evaluate(IEventEmitter eventEmitter, int eventNumber, Object[] eventParameters, int flags) {
|
||||
if(EventSupport.isStandardEvent(flags)) {
|
||||
//This method is called when events occur in the collection items.//
|
||||
getCollectionListener().collectionItemChanged(com.foundation.metadata.MetadataService.getSingleton().getEventName(eventEmitter.getClass(), eventNumber), eventEmitter, eventParameters);
|
||||
}//if//
|
||||
}//evaluate()//
|
||||
/**
|
||||
* Replaces the old collection value with a new one.
|
||||
*/
|
||||
private void replaceCollectionValue() {
|
||||
Object oldValue = currentValue;
|
||||
Object newValue = attributeAssociation != null ? attributeAssociation.getAttributeValue() : null;
|
||||
|
||||
//Unregister the old collection.//
|
||||
if(oldValue instanceof IManagedCollection) {
|
||||
//Unregister the old event hooks.//
|
||||
eventSupport.unregisterAll((IManagedCollection) oldValue);
|
||||
}//if//
|
||||
|
||||
//Unregister all the events with all the objects in the old collection.//
|
||||
itemEvents.unregisterEvents();
|
||||
|
||||
//Register the new collection events.//
|
||||
if(newValue instanceof IManagedCollection) {
|
||||
//Register the new event hooks.//
|
||||
eventSupport.register((IManagedCollection) newValue, IManagedCollection.EVENT.getNumber(), new IHandler() {
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.event.IHandler#evaluate(int, java.lang.Object[], int)
|
||||
*/
|
||||
public void evaluate(int eventNumber, Object[] eventParameters, int flags) {
|
||||
if(EventSupport.isStandardEvent(flags)) {
|
||||
//This method is called when items are added or removed from the current collection.//
|
||||
CollectionChangeEvent event = (CollectionChangeEvent) eventParameters[0];
|
||||
|
||||
if(event.hasRemoves()) {
|
||||
if(event.getRemovedObjects() != null) {
|
||||
getCollectionListener().collectionItemsRemoved(event.getRemovedObjects());
|
||||
}//if//
|
||||
else {
|
||||
getCollectionListener().collectionItemRemoved(event.getRemovedObject());
|
||||
}//else//
|
||||
}//if//
|
||||
|
||||
if(event.hasAdds()) {
|
||||
if(event.getAddedObjects() != null) {
|
||||
getCollectionListener().collectionItemsAdded(event.getAddedObjects());
|
||||
}//if//
|
||||
else {
|
||||
getCollectionListener().collectionItemAdded(event.getAddedObject());
|
||||
}//else//
|
||||
}//if//
|
||||
}//if//
|
||||
}//evaluate()//
|
||||
}, true);
|
||||
|
||||
if(itemEvents.getEventCount() > 0) {
|
||||
if(newValue instanceof IManagedIndexedCollection) {
|
||||
IManagedIndexedCollection collection = (IManagedIndexedCollection) newValue;
|
||||
|
||||
for(int index = collection.getSize(); index >= 0; index--) {
|
||||
itemEvents.registerEvents(collection.get(index));
|
||||
}//for//
|
||||
}//if//
|
||||
else {
|
||||
IIterator iterator = ((IManagedCollection) newValue).iterator();
|
||||
|
||||
while(iterator.hasNext()) {
|
||||
itemEvents.registerEvents(iterator.next());
|
||||
}//while//
|
||||
}//else//
|
||||
}//if//
|
||||
}//if//
|
||||
|
||||
//Save a reference to the current collection.//
|
||||
currentValue = newValue;
|
||||
//Notify the listener that the whole collection changed.//
|
||||
getCollectionListener().collectionChanged(oldValue instanceof IManagedCollection ? (IManagedCollection) oldValue : null, newValue instanceof IManagedCollection ? (IManagedCollection) newValue : null);
|
||||
}//replaceCollectionValue()//
|
||||
}//CollectionAttributeAssociationAdapter//
|
||||
140
Foundation/src/com/foundation/view/ControlDecoration.java
Normal file
140
Foundation/src/com/foundation/view/ControlDecoration.java
Normal file
@@ -0,0 +1,140 @@
|
||||
/*
|
||||
* Copyright (c) 2007,2009 Declarative Engineering LLC.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Declarative Engineering LLC
|
||||
* verson 1 which accompanies this distribution, and is available at
|
||||
* http://declarativeengineering.com/legal/DE_Developer_License_v1.txt
|
||||
*/
|
||||
package com.foundation.view;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import com.common.io.IObjectInputStream;
|
||||
import com.common.io.IObjectOutputStream;
|
||||
import com.foundation.view.resource.ResourceReference;
|
||||
|
||||
/*
|
||||
* The base class for displaying an image with a tool tip as a control on top of another control.
|
||||
*/
|
||||
public class ControlDecoration extends AbstractDecoration {
|
||||
private Object imageReference = null;
|
||||
private Object toolTip = null;
|
||||
private int position = POSITION_UPPER_LEFT;
|
||||
/**
|
||||
* ControlDecoration constructor.
|
||||
* <p>Warning: This constructor is to allow serialization only.</p>
|
||||
*/
|
||||
public ControlDecoration() {
|
||||
super();
|
||||
}//ControlDecoration()//
|
||||
/**
|
||||
* ControlDecoration constructor.
|
||||
* @param imageReference The image resource reference.
|
||||
* @param toolTipReference The tooltip resource reference.
|
||||
*/
|
||||
public ControlDecoration(ResourceReference imageReference, ResourceReference toolTipReference) {
|
||||
this((Object) imageReference, (Object) toolTipReference, POSITION_UPPER_LEFT);
|
||||
}//ControlDecoration()//
|
||||
/**
|
||||
* ControlDecoration constructor.
|
||||
* @param imageReference The image resource reference.
|
||||
* @param toolTip The tooltip or the tooltip resource reference.
|
||||
*/
|
||||
public ControlDecoration(ResourceReference imageReference, String toolTip) {
|
||||
this((Object) imageReference, (Object) toolTip, POSITION_UPPER_LEFT);
|
||||
}//ControlDecoration()//
|
||||
/**
|
||||
* ControlDecoration constructor.
|
||||
* @param imageReference The image resource reference.
|
||||
* @param toolTipReference The tooltip resource reference.
|
||||
*/
|
||||
public ControlDecoration(String imageReference, ResourceReference toolTipReference) {
|
||||
this((Object) imageReference, (Object) toolTipReference, POSITION_UPPER_LEFT);
|
||||
}//ControlDecoration()//
|
||||
/**
|
||||
* ControlDecoration constructor.
|
||||
* @param image The image resource. Calling this method with this parameter is not recommended.
|
||||
* @param toolTip The tooltip or the tooltip resource reference.
|
||||
*/
|
||||
public ControlDecoration(JefImage image, String toolTip) {
|
||||
this((Object) image, (Object) toolTip, POSITION_UPPER_LEFT);
|
||||
}//ControlDecoration()//
|
||||
/**
|
||||
* ControlDecoration constructor.
|
||||
* @param imageReference The image resource reference.
|
||||
* @param toolTip The tooltip or the tooltip resource reference.
|
||||
*/
|
||||
public ControlDecoration(String imageReference, String toolTip) {
|
||||
this((Object) imageReference, (Object) toolTip, POSITION_UPPER_LEFT);
|
||||
}//ControlDecoration()//
|
||||
/**
|
||||
* ControlDecoration constructor.
|
||||
* @param imageReference The image resource reference.
|
||||
* @param toolTip The tooltip or the tooltip resource reference.
|
||||
* @param position One of the POSITION_*** identifiers defined by this class.
|
||||
*/
|
||||
public ControlDecoration(String imageReference, String toolTip, int position) {
|
||||
this((Object) imageReference, (Object) toolTip, position);
|
||||
}//ControlDecoration()//
|
||||
/**
|
||||
* ControlDecoration constructor.
|
||||
* @param image The image resource reference.
|
||||
* @param toolTip The tooltip or the tooltip resource reference.
|
||||
*/
|
||||
public ControlDecoration(Object image, Object toolTip) {
|
||||
this(image, toolTip, POSITION_UPPER_LEFT);
|
||||
}//ControlDecoration()//
|
||||
/**
|
||||
* ControlDecoration constructor.
|
||||
* @param image The image resource reference.
|
||||
* @param toolTip The tooltip or the tooltip resource reference.
|
||||
* @param position One of the POSITION_*** identifiers defined by this class.
|
||||
*/
|
||||
public ControlDecoration(Object image, Object toolTip, int position) {
|
||||
super();
|
||||
this.imageReference = image instanceof JefImage ? image : image instanceof ResourceReference ? (ResourceReference) image : new ResourceReference((String) image);
|
||||
this.toolTip = toolTip instanceof ResourceReference ? (ResourceReference) toolTip : ResourceReference.isResourceUrl((String) toolTip) ? (Object) new ResourceReference((String) toolTip) : (String) toolTip;
|
||||
this.position = position;
|
||||
}//ControlDecoration()//
|
||||
/**
|
||||
* Gets the position relative to the control being decorated.
|
||||
* @return The position identifier.
|
||||
*/
|
||||
public int getPosition() {
|
||||
return position;
|
||||
}//getPosition()//
|
||||
/**
|
||||
* Gets the resource reference or image for the control's image.
|
||||
* @return The image's ResourceReference or JefImage.
|
||||
*/
|
||||
public Object getImage() {
|
||||
return imageReference;
|
||||
}//getImage()//
|
||||
/**
|
||||
* Gets the tool tip.
|
||||
* @return The tool tip String or ResourceReference.
|
||||
*/
|
||||
public Object getToolTip() {
|
||||
return toolTip;
|
||||
}//getToolTip()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.common.io.IExternalizable#readExternal(com.common.io.IObjectInputStream)
|
||||
*/
|
||||
public Object readExternal(IObjectInputStream in) throws IOException, ClassNotFoundException {
|
||||
in.readByte();
|
||||
imageReference = in.readObject();
|
||||
toolTip = in.readObject();
|
||||
position = in.readInt();
|
||||
|
||||
return null;
|
||||
}//readExternal()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.common.io.IExternalizable#writeExternal(com.common.io.IObjectOutputStream)
|
||||
*/
|
||||
public void writeExternal(IObjectOutputStream out) throws IOException {
|
||||
out.writeByte(0);
|
||||
out.writeObject(imageReference);
|
||||
out.writeObject(toolTip);
|
||||
out.writeInt(position);
|
||||
}//writeExternal()//
|
||||
}//ControlDecoration//
|
||||
96
Foundation/src/com/foundation/view/DisplayMetadata.java
Normal file
96
Foundation/src/com/foundation/view/DisplayMetadata.java
Normal file
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
* Copyright (c) 2005,2009 Declarative Engineering LLC.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Declarative Engineering LLC
|
||||
* verson 1 which accompanies this distribution, and is available at
|
||||
* http://declarativeengineering.com/legal/DE_Developer_License_v1.txt
|
||||
*/
|
||||
package com.foundation.view;
|
||||
|
||||
import java.io.Externalizable;
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectInput;
|
||||
import java.io.ObjectOutput;
|
||||
|
||||
public class DisplayMetadata implements Externalizable {
|
||||
/** The x dimension. */
|
||||
public static final int X = 0;
|
||||
/** The y dimension. */
|
||||
public static final int Y = 1;
|
||||
/** The width dimension. */
|
||||
public static final int WIDTH = 2;
|
||||
/** The height dimension. */
|
||||
public static final int HEIGHT = 3;
|
||||
|
||||
/** The bounds for the display. A primary display will have bounds of (0, 0, width, height). */
|
||||
private int[] boundDimensions = null;
|
||||
/** The client area for the display. This is the display coordinates which can display data (x, y, width, height). */
|
||||
private int[] clientDimensions = null;
|
||||
/**
|
||||
* DisplayMetadata constructor.
|
||||
*/
|
||||
public DisplayMetadata() {
|
||||
super();
|
||||
}//DisplayMetadata()//
|
||||
/**
|
||||
* DisplayMetadata constructor.
|
||||
* @param The display bounds (x, y, width, height).
|
||||
* @param The display client area (x, y, width, height).
|
||||
*/
|
||||
public DisplayMetadata(int[] boundDimensions, int[] clientDimensions) {
|
||||
super();
|
||||
|
||||
this.boundDimensions = boundDimensions;
|
||||
this.clientDimensions = clientDimensions;
|
||||
}//DisplayMetadata()//
|
||||
/**
|
||||
* Gets the display device bounds dimension.
|
||||
* @param side The dimension indicator identifying which dimension to be retreived (X, Y, WIDTH, HEIGHT).
|
||||
* @return The dimension of the display bounds.
|
||||
* @see #X
|
||||
* @see #Y
|
||||
* @see #WIDTH
|
||||
* @see #HEIGHT
|
||||
*/
|
||||
public int getBoundDimension(int side) {
|
||||
return boundDimensions[side];
|
||||
}//getBoundDimension()//
|
||||
/**
|
||||
* Gets the display client area bounds dimension.
|
||||
* @param side The dimension indicator identifying which dimension to be retreived (X, Y, WIDTH, HEIGHT).
|
||||
* @return The dimension of the client area.
|
||||
* @see #X
|
||||
* @see #Y
|
||||
* @see #WIDTH
|
||||
* @see #HEIGHT
|
||||
*/
|
||||
public int getClientDimension(int side) {
|
||||
return clientDimensions[side];
|
||||
}//getClientDimension()//
|
||||
/* (non-Javadoc)
|
||||
* @see java.io.Externalizable#readExternal(java.io.ObjectInput)
|
||||
*/
|
||||
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
|
||||
boundDimensions[X] = in.readInt();
|
||||
boundDimensions[Y] = in.readInt();
|
||||
boundDimensions[WIDTH] = in.readInt();
|
||||
boundDimensions[HEIGHT] = in.readInt();
|
||||
clientDimensions[X] = in.readInt();
|
||||
clientDimensions[Y] = in.readInt();
|
||||
clientDimensions[WIDTH] = in.readInt();
|
||||
clientDimensions[HEIGHT] = in.readInt();
|
||||
}//readExternal()//
|
||||
/* (non-Javadoc)
|
||||
* @see java.io.Externalizable#writeExternal(java.io.ObjectOutput)
|
||||
*/
|
||||
public void writeExternal(ObjectOutput out) throws IOException {
|
||||
out.writeInt(boundDimensions[X]);
|
||||
out.writeInt(boundDimensions[Y]);
|
||||
out.writeInt(boundDimensions[WIDTH]);
|
||||
out.writeInt(boundDimensions[HEIGHT]);
|
||||
out.writeInt(clientDimensions[X]);
|
||||
out.writeInt(clientDimensions[Y]);
|
||||
out.writeInt(clientDimensions[WIDTH]);
|
||||
out.writeInt(clientDimensions[HEIGHT]);
|
||||
}//writeExternal()//
|
||||
}//DisplayMetadata//
|
||||
108
Foundation/src/com/foundation/view/EventAssociation.java
Normal file
108
Foundation/src/com/foundation/view/EventAssociation.java
Normal file
@@ -0,0 +1,108 @@
|
||||
/*
|
||||
* Copyright (c) 2003,2009 Declarative Engineering LLC.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Declarative Engineering LLC
|
||||
* verson 1 which accompanies this distribution, and is available at
|
||||
* http://declarativeengineering.com/legal/DE_Developer_License_v1.txt
|
||||
*/
|
||||
package com.foundation.view;
|
||||
|
||||
import com.foundation.event.EventSupport;
|
||||
import com.foundation.metadata.Event;
|
||||
|
||||
/*
|
||||
* Models a view association with an event declared by an event emitter.
|
||||
*/
|
||||
public class EventAssociation extends ValueHolderAssociation implements IEventAssociation {
|
||||
/** The number of the event being listened to. */
|
||||
private int eventNumber = -1;
|
||||
/** The listener for event notifications. */
|
||||
private IEventAssociationChangeListener listener = null;
|
||||
/** A counter to allow multiple calls to register and unregister the event without actually registering multiple times. */
|
||||
private int registerCounter = 0;
|
||||
/**
|
||||
* EventAssociation constructor.
|
||||
* @param component The component the association is connected to.
|
||||
* @param valueHolderName The optional name of the value holder for the association.
|
||||
* @param heldType The optional class which is a subclass of the value holder's held type and defines the association.
|
||||
* @param rowType The optional class which defines the association if a held type or value holder is not provided, and it determines whether this association is used based on whether it matches the control's row type.
|
||||
* @param event The unique (within the context of the value holder's held value type hierarchy) identifier for the event to register.
|
||||
*/
|
||||
public EventAssociation(IAbstractComponent component, String valueHolderName, Class heldType, Class rowType, Event event) {
|
||||
super(component, valueHolderName, heldType, rowType);
|
||||
this.eventNumber = event.getNumber();
|
||||
this.listener = component;
|
||||
}//EventAssociation()//
|
||||
/**
|
||||
* EventAssociation constructor.
|
||||
* @param component The component the association is connected to.
|
||||
* @param valueHolderName The optional name of the value holder for the association.
|
||||
* @param heldType The optional class which is a subclass of the value holder's held type and defines the association.
|
||||
* @param rowType The optional class which defines the association if a held type or value holder is not provided, and it determines whether this association is used based on whether it matches the control's row type.
|
||||
* @param eventName The unique (within the context of the value holder's held value type hierarchy) name for the event to register.
|
||||
*/
|
||||
public EventAssociation(IAbstractComponent component, String valueHolderName, Class heldType, Class rowType, String eventName) {
|
||||
super(component, valueHolderName, heldType, rowType);
|
||||
this.eventNumber = com.foundation.event.EventSupport.getEventNumber(getIsValueHolderAssociated() ? heldType != null ? heldType : getValueHolder().getHeldType() : rowType, eventName);
|
||||
this.listener = component;
|
||||
|
||||
if(eventNumber == -1) {
|
||||
throw new RuntimeException("Could not find the event with the name \"" + eventName + "\" in the type \"" + (getIsValueHolderAssociated() ? heldType != null ? heldType : getValueHolder().getHeldType() : rowType) + "\".");
|
||||
}//if//
|
||||
}//EventAssociation()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.view.swt.IEventAssociation#register()
|
||||
*/
|
||||
public void register() {
|
||||
if(registerCounter++ == 0) {
|
||||
getValueHolder().registerListener(this);
|
||||
}//if//
|
||||
}//register()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.view.swt.IEventAssociation#unregister()
|
||||
*/
|
||||
public void unregister() {
|
||||
if(--registerCounter == 0) {
|
||||
getValueHolder().unregisterListener(this);
|
||||
}//if//
|
||||
}//unregister()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.view.IEventAssociation#getEventNumber()
|
||||
*/
|
||||
public int getEventNumber() {
|
||||
return eventNumber;
|
||||
}//getEventNumber()//
|
||||
/**
|
||||
* Gets the change listener for this association.
|
||||
* @return The listener which needs to be updated when something occurs.
|
||||
*/
|
||||
private IEventAssociationChangeListener getChangeListener() {
|
||||
return listener;
|
||||
}//getChangeListener()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.view.IEventAssociation#setChangeListener(com.foundation.view.IEventAssociationChangeListener)
|
||||
*/
|
||||
public void setChangeListener(IEventAssociationChangeListener listener) {
|
||||
if(listener == null) {
|
||||
listener = getComponent();
|
||||
}//if//
|
||||
|
||||
this.listener = listener;
|
||||
}//setChangeListener()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.common.event.IHandler#evaluate(int, java.lang.Object[], int)
|
||||
*/
|
||||
public void evaluate(int eventNumber, Object[] args, int flags) {
|
||||
//Note: I commented out the isTrustedChangeEvent because we don't want to listen to lazy load and reflection load events which are marked as trusted.//
|
||||
//If we did listen to them we would be performing updates before we have finished initializing during an initialization where more than one association is connected to a lazy or reflection loaded attribute.//
|
||||
//We do care about reflection updates which are now marked as trusted when setting the attribute, but are not marked as trusted when firing the events.//
|
||||
//I don't think there is anything else marked as trusted that we care about here - thus the code is commented out.//
|
||||
|
||||
//Note #2: I added the code to allow trusted change events if they are reflection updates and are not modifying the original value or clearing it.//
|
||||
//The reason is that a value that wasn't modified locally and is updated via a reflection update should cause an update in the view.//
|
||||
if(EventSupport.isStandardEvent(flags) || (EventSupport.isTrustedChangeEvent(flags) && EventSupport.isReflectionUpdateChangeEvent(flags) && !EventSupport.isOriginalValueChanged(flags) && !EventSupport.isOriginalValueCleared(flags))) {
|
||||
//Notify the component of the event.//
|
||||
getChangeListener().onEventFired(this, args == null ? EMPTY_ARGS : args);
|
||||
}//if//
|
||||
}//evaluate()//
|
||||
}//EventAssociation//
|
||||
115
Foundation/src/com/foundation/view/EventAssociationSet.java
Normal file
115
Foundation/src/com/foundation/view/EventAssociationSet.java
Normal file
@@ -0,0 +1,115 @@
|
||||
/*
|
||||
* Copyright (c) 2005,2009 Declarative Engineering LLC.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Declarative Engineering LLC
|
||||
* verson 1 which accompanies this distribution, and is available at
|
||||
* http://declarativeengineering.com/legal/DE_Developer_License_v1.txt
|
||||
*/
|
||||
package com.foundation.view;
|
||||
|
||||
import com.foundation.event.EventSupport;
|
||||
import com.foundation.event.IHandler;
|
||||
import com.foundation.event.IEventEmitter;
|
||||
import com.common.util.*;
|
||||
|
||||
/**
|
||||
* The event association set is a utility class that encapsulates having multiple events to be registered with a set of objects.
|
||||
* The objects may be of different types and the events may not all be declared by all the objects being registered.
|
||||
* <p>Warning: This is not a thread safe class as it was designed to work within a single threaded view environment.</p>
|
||||
*/
|
||||
public class EventAssociationSet {
|
||||
/** The collection of IEventAssociation instances. */
|
||||
private IList events = new LiteList(6);
|
||||
/** The support object that will be used to register and unregister event handlers. */
|
||||
private EventSupport eventSupport = null;
|
||||
/** The event handler to be used when registering or unregistering events. */
|
||||
private IHandler eventHandler = null;
|
||||
/**
|
||||
* EventAssociationSet constructor.
|
||||
* @param The object which will be notified when a registered event is fired.
|
||||
*/
|
||||
public EventAssociationSet(IHandler eventHandler) {
|
||||
super();
|
||||
this.eventHandler = eventHandler;
|
||||
}//EventAssociationSet()//
|
||||
/**
|
||||
* Adds a new event assocation to the set.
|
||||
* @param eventAssociation The event association to be added.
|
||||
*/
|
||||
public void addEventAssociation(IEventAssociation eventAssociation) {
|
||||
events.add(eventAssociation);
|
||||
}//addEventAssociation()//
|
||||
/**
|
||||
* Removes an event assocation from the set.
|
||||
* <p>Warning: The caller must be careful to unregister events prior (or post since unregisterEvents(..) removes all registered events regardless of what is available at the time for registration) to removing otherwise events will still be received.</p>
|
||||
* @param eventAssociation The event association to be removed.
|
||||
*/
|
||||
public void removeEventAssociation(IEventAssociation eventAssociation) {
|
||||
events.add(eventAssociation);
|
||||
}//addEventAssociation()//
|
||||
/**
|
||||
* Removes all event associations.
|
||||
* <p><b>Warning:</b> The caller must first unregister all events or risk memory leaks.</p>
|
||||
* @see #unregisterEvents()
|
||||
*/
|
||||
public void removeEventAssociations() {
|
||||
events.removeAll();
|
||||
}//removeEventAssociations()//
|
||||
/**
|
||||
* Registers an object using all applicable events.
|
||||
* <p>Warning: Adding event associations after registering objects will not update the registrations of the objects already registered, but will affect future object registrations.</p>
|
||||
* @param object The object to register.
|
||||
*/
|
||||
public void registerEvents(Object object) {
|
||||
if(events != null) {
|
||||
//Lazy load the event support.//
|
||||
if((events.getSize() > 0) && (eventSupport == null)) {
|
||||
eventSupport = new EventSupport(null);
|
||||
}//if//
|
||||
|
||||
//Iterate over the event associations and register the applicable ones with this object.//
|
||||
for(int eventIndex = 0; eventIndex < events.getSize(); eventIndex++) {
|
||||
IEventAssociation event = (IEventAssociation) events.get(eventIndex);
|
||||
|
||||
//Check the types to see if the event is applicable to the object.//
|
||||
if(event.isApplicable(object)) {
|
||||
eventSupport.register((IEventEmitter) object, event.getEventNumber(), eventHandler, true);
|
||||
}//if//
|
||||
}//for//
|
||||
}//if//
|
||||
}//registerEvents()//
|
||||
/**
|
||||
* Unregisters an object using all applicable events.
|
||||
* <p>Warning: Removing event associations before unregistering objects will cause memory leaks since the event will never be unregistered.</p>
|
||||
* @param object The object to unregister.
|
||||
*/
|
||||
public void unregisterEvents(Object object) {
|
||||
if((events != null) && (eventSupport != null)) {
|
||||
//Iterate over the event associations and unregister the applicable ones from this object.//
|
||||
for(int eventIndex = 0; eventIndex < events.getSize(); eventIndex++) {
|
||||
IEventAssociation event = (IEventAssociation) events.get(eventIndex);
|
||||
|
||||
//Check the types to see if the event is applicable to the object.//
|
||||
if(event.isApplicable(object)) {
|
||||
eventSupport.unregister((IEventEmitter) object, event.getEventNumber(), eventHandler);
|
||||
}//if//
|
||||
}//for//
|
||||
}//if//
|
||||
}//unregisterEvents()//
|
||||
/**
|
||||
* Unregisters all objects using all applicable events.
|
||||
* <p>Warning: Removing event associations before unregistering objects will cause memory leaks since the event will never be unregistered.</p>
|
||||
*/
|
||||
public void unregisterEvents() {
|
||||
if(eventSupport != null) {
|
||||
eventSupport.unregisterAll();
|
||||
}//if//
|
||||
}//unregisterEvents()//
|
||||
/**
|
||||
* Gets the number of events added to the set.
|
||||
* @return The event set count.
|
||||
*/
|
||||
public int getEventCount() {
|
||||
return events.getSize();
|
||||
}//getEventCount()//
|
||||
}//EventAssociationSet//
|
||||
299
Foundation/src/com/foundation/view/FormatDate.java
Normal file
299
Foundation/src/com/foundation/view/FormatDate.java
Normal file
@@ -0,0 +1,299 @@
|
||||
package com.foundation.view;
|
||||
|
||||
import java.io.Externalizable;
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectInput;
|
||||
import java.io.ObjectOutput;
|
||||
import java.text.DateFormat;
|
||||
import java.text.DecimalFormat;
|
||||
import java.text.Format;
|
||||
import java.text.NumberFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.TimeZone;
|
||||
|
||||
import com.common.comparison.Comparator;
|
||||
|
||||
/**
|
||||
* Copyright Declarative Engineering LLC 2009<p>
|
||||
* Encapsulates the information necessary to setup a date formatter.
|
||||
*/
|
||||
public class FormatDate extends AbstractFormat {
|
||||
public static final int TYPE_DATE = 0;
|
||||
public static final int TYPE_TIME = 1;
|
||||
public static final int TYPE_DATETIME = 2;
|
||||
|
||||
public static final int TYPE_FORMAT_SHORT = 0;
|
||||
public static final int TYPE_FORMAT_MEDIUM = 1;
|
||||
public static final int TYPE_FORMAT_LONG = 2;
|
||||
public static final int TYPE_FORMAT_FULL = 3;
|
||||
|
||||
private String pattern = null;
|
||||
/** The type of date to display (date, time, datetime). */
|
||||
private int type = 0;
|
||||
/** The format for the date type (short, medium, long, full). */
|
||||
private int format1 = -1;
|
||||
/** The secondary format for the date type (short, medium, long, full), used only if the type is datetime (refers to the time format). */
|
||||
private int format2 = -1;
|
||||
/** The optional time zone identifier. */
|
||||
private String timeZoneId = null;
|
||||
/**
|
||||
* FormatDate constructor.
|
||||
*/
|
||||
public FormatDate() {
|
||||
}//FormatDate()//
|
||||
/**
|
||||
* FormatDate constructor.
|
||||
* @param pattern The pattern to use for formatting the date.
|
||||
* @param timeZoneId The identifier for the time zone to be forced upon the format, otherwise the localized timezone will be used.
|
||||
*/
|
||||
public FormatDate(String pattern, String timeZoneId) {
|
||||
if(pattern == null || pattern.length() == 0) {
|
||||
throw new IllegalArgumentException("Bad number pattern.");
|
||||
}//if//
|
||||
|
||||
this.pattern = pattern;
|
||||
}//FormatDate()//
|
||||
/**
|
||||
* FormatDate constructor.
|
||||
* @param type The pre-defined date pattern type.
|
||||
* @param format1 The format type for the date.
|
||||
* @param timeZoneId The identifier for the time zone to be forced upon the format, otherwise the localized timezone will be used.
|
||||
*/
|
||||
public FormatDate(int type, int format1, String timeZoneId) {
|
||||
if(type < 0 || type > TYPE_DATETIME) {
|
||||
throw new IllegalArgumentException("Bad date type.");
|
||||
}//if//
|
||||
|
||||
if(format1 < 0 || format1 > TYPE_FORMAT_FULL) {
|
||||
throw new IllegalArgumentException("Bad date type format.");
|
||||
}//if//
|
||||
|
||||
this.type = type;
|
||||
this.format1 = format1;
|
||||
|
||||
if(type == TYPE_DATETIME) {
|
||||
this.format2 = format1;
|
||||
}//if//
|
||||
}//FormatDate()//
|
||||
/**
|
||||
* FormatDate constructor.
|
||||
* @param type The pre-defined date pattern type.
|
||||
* @param format1 The format type for the date.
|
||||
* @param format2 The format type used for the time portion of the date (only utilized if type is date-time).
|
||||
* @param timeZoneId The identifier for the time zone to be forced upon the format, otherwise the localized timezone will be used.
|
||||
*/
|
||||
public FormatDate(int type, int format1, int format2, String timeZoneId) {
|
||||
if(type < 0 || type > TYPE_DATETIME) {
|
||||
throw new IllegalArgumentException("Bad date type.");
|
||||
}//if//
|
||||
|
||||
if(format1 < 0 || format1 > TYPE_FORMAT_FULL) {
|
||||
throw new IllegalArgumentException("Bad date type format.");
|
||||
}//if//
|
||||
|
||||
if(format2 < 0 || format2 > TYPE_FORMAT_FULL) {
|
||||
throw new IllegalArgumentException("Bad date type format.");
|
||||
}//if//
|
||||
|
||||
this.type = type;
|
||||
this.format1 = format1;
|
||||
this.format2 = format2;
|
||||
}//FormatDate()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.view.IJefFormat#createFormat()
|
||||
*/
|
||||
public Format createFormat() {
|
||||
DateFormat result;
|
||||
|
||||
if(pattern != null) {
|
||||
result = new SimpleDateFormat(pattern);
|
||||
}//if//
|
||||
else {
|
||||
switch(type) {
|
||||
case TYPE_DATE: {
|
||||
result = format1 == -1 ? DateFormat.getDateInstance() : DateFormat.getDateInstance(getFormatStyle(format1));
|
||||
break;
|
||||
}//case//
|
||||
case TYPE_TIME: {
|
||||
result = format1 == -1 ? DateFormat.getTimeInstance() : DateFormat.getTimeInstance(getFormatStyle(format1));
|
||||
break;
|
||||
}//case//
|
||||
case TYPE_DATETIME: {
|
||||
result = format1 == -1 ? DateFormat.getDateTimeInstance() : DateFormat.getDateTimeInstance(getFormatStyle(format1), format2 == -1 ? getFormatStyle(format1) : getFormatStyle(format2));
|
||||
break;
|
||||
}//case//
|
||||
default: {
|
||||
result = DateFormat.getInstance();
|
||||
break;
|
||||
}//default//
|
||||
}//switch//
|
||||
}//else//
|
||||
|
||||
if(timeZoneId != null) {
|
||||
result.setTimeZone(TimeZone.getTimeZone(timeZoneId));
|
||||
}//if//
|
||||
|
||||
return result;
|
||||
}//createFormat()//
|
||||
private int getFormatStyle(int format) {
|
||||
int result = 0;
|
||||
|
||||
switch(format) {
|
||||
case TYPE_FORMAT_SHORT: {
|
||||
result = DateFormat.SHORT;
|
||||
break;
|
||||
}//case//
|
||||
case TYPE_FORMAT_MEDIUM: {
|
||||
result = DateFormat.MEDIUM;
|
||||
break;
|
||||
}//case//
|
||||
case TYPE_FORMAT_LONG: {
|
||||
result = DateFormat.LONG;
|
||||
break;
|
||||
}//case//
|
||||
case TYPE_FORMAT_FULL: {
|
||||
result = DateFormat.FULL;
|
||||
break;
|
||||
}//case//
|
||||
default: {
|
||||
result = DateFormat.SHORT;
|
||||
break;
|
||||
}//default//
|
||||
}//switch//
|
||||
|
||||
return result;
|
||||
}//getFormatStyle()//
|
||||
/* (non-Javadoc)
|
||||
* @see java.io.Externalizable#readExternal(java.io.ObjectInput)
|
||||
*/
|
||||
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
|
||||
pattern = in.readUTF();
|
||||
type = in.readInt();
|
||||
format1 = in.readInt();
|
||||
format2 = in.readInt();
|
||||
timeZoneId = in.readUTF();
|
||||
}//readExternal()//
|
||||
/* (non-Javadoc)
|
||||
* @see java.io.Externalizable#writeExternal(java.io.ObjectOutput)
|
||||
*/
|
||||
public void writeExternal(ObjectOutput out) throws IOException {
|
||||
out.writeUTF(pattern);
|
||||
out.writeInt(type);
|
||||
out.writeInt(format1);
|
||||
out.writeInt(format2);
|
||||
out.writeUTF(timeZoneId);
|
||||
}//writeExternal()//
|
||||
/* (non-Javadoc)
|
||||
* @see java.lang.Object#clone()
|
||||
*/
|
||||
public Object clone() throws CloneNotSupportedException {
|
||||
FormatDate result = new FormatDate();
|
||||
|
||||
result.pattern = pattern;
|
||||
result.type = type;
|
||||
result.format1 = format1;
|
||||
result.format2 = format2;
|
||||
result.timeZoneId = timeZoneId;
|
||||
|
||||
return result;
|
||||
}//clone()//
|
||||
/* (non-Javadoc)
|
||||
* @see java.lang.Object#equals(java.lang.Object)
|
||||
*/
|
||||
public boolean equals(Object object) {
|
||||
return (object instanceof FormatDate) && (Comparator.equals(pattern, ((FormatDate) object).pattern)) && (type == ((FormatDate) object).type) && (format1 == ((FormatDate) object).format1) && (format2 == ((FormatDate) object).format2) && (Comparator.equals(timeZoneId, ((FormatDate) object).timeZoneId));
|
||||
}//equals()//
|
||||
/* (non-Javadoc)
|
||||
* @see java.lang.Object#hashCode()
|
||||
*/
|
||||
public int hashCode() {
|
||||
return (pattern == null ? type ^ format1 ^ format2 : pattern.hashCode()) ^ (timeZoneId == null ? 0 : timeZoneId.hashCode());
|
||||
}//hashCode()//
|
||||
/* (non-Javadoc)
|
||||
* @see java.lang.Object#toString()
|
||||
*/
|
||||
public String toString() {
|
||||
String result = "date ";
|
||||
|
||||
if(timeZoneId != null) {
|
||||
result += timeZoneId + " ";
|
||||
}//if//
|
||||
else {
|
||||
result += "local ";
|
||||
}//else//
|
||||
|
||||
if(pattern != null) {
|
||||
result += pattern;
|
||||
}//if//
|
||||
else {
|
||||
switch(type) {
|
||||
case TYPE_DATE: {
|
||||
result += "date";
|
||||
break;
|
||||
}//case//
|
||||
case TYPE_TIME: {
|
||||
result += "time";
|
||||
break;
|
||||
}//case//
|
||||
case TYPE_DATETIME: {
|
||||
result += "datetime";
|
||||
break;
|
||||
}//case//
|
||||
default: {
|
||||
result += "date";
|
||||
break;
|
||||
}//default//
|
||||
}//switch//
|
||||
|
||||
switch(format1) {
|
||||
case TYPE_FORMAT_SHORT: {
|
||||
result += "-short";
|
||||
break;
|
||||
}//case//
|
||||
case TYPE_FORMAT_MEDIUM: {
|
||||
result += "-medium";
|
||||
break;
|
||||
}//case//
|
||||
case TYPE_FORMAT_LONG: {
|
||||
result += "-long";
|
||||
break;
|
||||
}//case//
|
||||
case TYPE_FORMAT_FULL: {
|
||||
result += "-full";
|
||||
break;
|
||||
}//case//
|
||||
default: {
|
||||
result += "-short";
|
||||
break;
|
||||
}//default//
|
||||
}//switch//
|
||||
|
||||
if(type == TYPE_DATETIME && format2 != -1) {
|
||||
switch(format2) {
|
||||
case TYPE_FORMAT_SHORT: {
|
||||
result += "-short";
|
||||
break;
|
||||
}//case//
|
||||
case TYPE_FORMAT_MEDIUM: {
|
||||
result += "-medium";
|
||||
break;
|
||||
}//case//
|
||||
case TYPE_FORMAT_LONG: {
|
||||
result += "-long";
|
||||
break;
|
||||
}//case//
|
||||
case TYPE_FORMAT_FULL: {
|
||||
result += "-full";
|
||||
break;
|
||||
}//case//
|
||||
default: {
|
||||
result += "-short";
|
||||
break;
|
||||
}//default//
|
||||
}//switch//
|
||||
}//if//
|
||||
}//else//
|
||||
|
||||
return result;
|
||||
}//toString()//
|
||||
}//FormatDate//
|
||||
160
Foundation/src/com/foundation/view/FormatNumber.java
Normal file
160
Foundation/src/com/foundation/view/FormatNumber.java
Normal file
@@ -0,0 +1,160 @@
|
||||
package com.foundation.view;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectInput;
|
||||
import java.io.ObjectOutput;
|
||||
import java.text.DecimalFormat;
|
||||
import java.text.Format;
|
||||
import java.text.NumberFormat;
|
||||
|
||||
import com.common.comparison.Comparator;
|
||||
|
||||
/**
|
||||
* Copyright Declarative Engineering LLC 2009<p>
|
||||
* Encapsulates the information necessary to setup a number formatter.
|
||||
*/
|
||||
public class FormatNumber extends AbstractFormat {
|
||||
public static final int TYPE_NUMBER = 0;
|
||||
public static final int TYPE_INTEGER = 1;
|
||||
public static final int TYPE_PERCENT = 2;
|
||||
public static final int TYPE_CURRENCY = 3;
|
||||
|
||||
private String pattern = null;
|
||||
private int type = 0;
|
||||
/**
|
||||
* FormatNumber constructor.
|
||||
*/
|
||||
public FormatNumber() {
|
||||
}//FormatNumber()//
|
||||
/**
|
||||
* FormatNumber constructor.
|
||||
* @param pattern The pattern to use for formatting the number.
|
||||
*/
|
||||
public FormatNumber(String pattern) {
|
||||
if(pattern == null || pattern.length() == 0) {
|
||||
throw new IllegalArgumentException("Bad number pattern.");
|
||||
}//if//
|
||||
|
||||
this.pattern = pattern;
|
||||
}//FormatNumber()//
|
||||
/**
|
||||
* FormatNumber constructor.
|
||||
* @param type The pre-defined number pattern type.
|
||||
*/
|
||||
public FormatNumber(int type) {
|
||||
if(type < 0 || type > TYPE_CURRENCY) {
|
||||
throw new IllegalArgumentException("Bad number type.");
|
||||
}//if//
|
||||
|
||||
this.type = type;
|
||||
}//FormatNumber()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.view.IJefFormat#createFormat()
|
||||
*/
|
||||
public Format createFormat() {
|
||||
Format result;
|
||||
|
||||
if(pattern != null) {
|
||||
result = new DecimalFormat(pattern);
|
||||
}//if//
|
||||
else {
|
||||
switch(type) {
|
||||
case TYPE_NUMBER: {
|
||||
result = NumberFormat.getNumberInstance();
|
||||
break;
|
||||
}//case//
|
||||
case TYPE_INTEGER: {
|
||||
result = NumberFormat.getIntegerInstance();
|
||||
break;
|
||||
}//case//
|
||||
case TYPE_CURRENCY: {
|
||||
result = NumberFormat.getCurrencyInstance();
|
||||
break;
|
||||
}//case//
|
||||
case TYPE_PERCENT: {
|
||||
result = NumberFormat.getPercentInstance();
|
||||
break;
|
||||
}//case//
|
||||
default: {
|
||||
result = NumberFormat.getInstance();
|
||||
break;
|
||||
}//default//
|
||||
}//switch//
|
||||
}//else//
|
||||
|
||||
return result;
|
||||
}//createFormat()//
|
||||
/* (non-Javadoc)
|
||||
* @see java.io.Externalizable#readExternal(java.io.ObjectInput)
|
||||
*/
|
||||
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
|
||||
pattern = in.readUTF();
|
||||
type = in.readInt();
|
||||
}//readExternal()//
|
||||
/* (non-Javadoc)
|
||||
* @see java.io.Externalizable#writeExternal(java.io.ObjectOutput)
|
||||
*/
|
||||
public void writeExternal(ObjectOutput out) throws IOException {
|
||||
out.writeUTF(pattern);
|
||||
out.writeInt(type);
|
||||
}//writeExternal()//
|
||||
/* (non-Javadoc)
|
||||
* @see java.lang.Object#clone()
|
||||
*/
|
||||
public Object clone() throws CloneNotSupportedException {
|
||||
FormatNumber result = new FormatNumber();
|
||||
|
||||
result.pattern = pattern;
|
||||
result.type = type;
|
||||
|
||||
return result;
|
||||
}//clone()//
|
||||
/* (non-Javadoc)
|
||||
* @see java.lang.Object#equals(java.lang.Object)
|
||||
*/
|
||||
public boolean equals(Object object) {
|
||||
return (object instanceof FormatNumber) && (Comparator.equals(pattern, ((FormatNumber) object).pattern)) && (type == ((FormatNumber) object).type);
|
||||
}//equals()//
|
||||
/* (non-Javadoc)
|
||||
* @see java.lang.Object#hashCode()
|
||||
*/
|
||||
public int hashCode() {
|
||||
return pattern == null ? type : pattern.hashCode();
|
||||
}//hashCode()//
|
||||
/* (non-Javadoc)
|
||||
* @see java.lang.Object#toString()
|
||||
*/
|
||||
public String toString() {
|
||||
String result = "number ";
|
||||
|
||||
if(pattern != null) {
|
||||
result += pattern;
|
||||
}//if//
|
||||
else {
|
||||
switch(type) {
|
||||
case TYPE_NUMBER: {
|
||||
result += "number";
|
||||
break;
|
||||
}//case//
|
||||
case TYPE_INTEGER: {
|
||||
result += "integer";
|
||||
break;
|
||||
}//case//
|
||||
case TYPE_CURRENCY: {
|
||||
result += "currency";
|
||||
break;
|
||||
}//case//
|
||||
case TYPE_PERCENT: {
|
||||
result += "percent";
|
||||
break;
|
||||
}//case//
|
||||
default: {
|
||||
result += "number";
|
||||
break;
|
||||
}//default//
|
||||
}//switch//
|
||||
}//else//
|
||||
|
||||
return result;
|
||||
}//toString()//
|
||||
}//FormatNumber//
|
||||
142
Foundation/src/com/foundation/view/HighlightDecoration.java
Normal file
142
Foundation/src/com/foundation/view/HighlightDecoration.java
Normal file
@@ -0,0 +1,142 @@
|
||||
/*
|
||||
* Copyright (c) 2009 Declarative Engineering LLC.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Declarative Engineering LLC
|
||||
* verson 1 which accompanies this distribution, and is available at
|
||||
* http://declarativeengineering.com/legal/DE_Developer_License_v1.txt
|
||||
*/
|
||||
package com.foundation.view;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import com.common.io.IObjectInputStream;
|
||||
import com.common.io.IObjectOutputStream;
|
||||
import com.foundation.view.resource.ResourceReference;
|
||||
|
||||
public class HighlightDecoration extends AbstractDecoration {
|
||||
private Object backgroundColor = null;
|
||||
private Object foregroundColor = null;
|
||||
private Object font = null;
|
||||
private Object selectionGradient = null;
|
||||
private Object toolTip = null;
|
||||
/**
|
||||
* HighlightDecoration constructor.
|
||||
*/
|
||||
public HighlightDecoration() {
|
||||
}//HighlightDecoration()//
|
||||
/**
|
||||
* HighlightDecoration constructor.
|
||||
* @param backgroundColorReference The optional background color resource reference.
|
||||
* @param foregroundColorReference The optional foreground color resource reference.
|
||||
* @param fontReference The optional font resource reference.
|
||||
* @param selectionGradientReference The optional selection gradient resource reference.
|
||||
* @param toolTipReference The optional tooltip resource reference.
|
||||
*/
|
||||
public HighlightDecoration(ResourceReference backgroundColorReference, ResourceReference foregroundColorReference, ResourceReference fontReference, ResourceReference selectionGradientReference, ResourceReference toolTipReference) {
|
||||
this.backgroundColor = backgroundColorReference;
|
||||
this.foregroundColor = foregroundColorReference;
|
||||
this.selectionGradient = selectionGradientReference;
|
||||
this.toolTip = toolTipReference;
|
||||
}//HighlightDecoration()//
|
||||
/**
|
||||
* HighlightDecoration constructor.
|
||||
* @param backgroundColorReference The optional background color resource reference.
|
||||
* @param foregroundColorReference The optional foreground color resource reference.
|
||||
* @param fontReference The optional font resource reference.
|
||||
* @param selectionGradientReference The optional selection gradient resource reference.
|
||||
* @param toolTip The optional tooltip.
|
||||
*/
|
||||
public HighlightDecoration(ResourceReference backgroundColorReference, ResourceReference foregroundColorReference, ResourceReference fontReference, ResourceReference selectionGradientReference, String toolTip) {
|
||||
this.backgroundColor = backgroundColorReference;
|
||||
this.foregroundColor = foregroundColorReference;
|
||||
this.selectionGradient = selectionGradientReference;
|
||||
this.toolTip = toolTip;
|
||||
}//HighlightDecoration()//
|
||||
/**
|
||||
* HighlightDecoration constructor.
|
||||
* @param backgroundColor The optional background color.
|
||||
* @param foregroundColor The optional foreground color.
|
||||
* @param font The optional font.
|
||||
* @param selectionColor The optional selection gradient.
|
||||
* @param toolTip The optional tooltip.
|
||||
*/
|
||||
public HighlightDecoration(JefColor backgroundColor, JefColor foregroundColor, JefFont font, JefGradient selectionGradient, String toolTip) {
|
||||
this.backgroundColor = backgroundColor;
|
||||
this.foregroundColor = foregroundColor;
|
||||
this.selectionGradient = selectionGradient;
|
||||
this.toolTip = toolTip;
|
||||
}//HighlightDecoration()//
|
||||
/**
|
||||
* HighlightDecoration constructor.
|
||||
* @param backgroundColor The optional background color.
|
||||
* @param foregroundColor The optional foreground color.
|
||||
* @param font The optional font.
|
||||
* @param selectionGradient The optional selection gradient.
|
||||
* @param toolTipReference The optional tooltip resource reference.
|
||||
*/
|
||||
public HighlightDecoration(JefColor backgroundColor, JefColor foregroundColor, JefFont font, JefGradient selectionGradient, ResourceReference toolTipReference) {
|
||||
this.backgroundColor = backgroundColor;
|
||||
this.foregroundColor = foregroundColor;
|
||||
this.selectionGradient = selectionGradient;
|
||||
this.toolTip = toolTipReference;
|
||||
}//HighlightDecoration()//
|
||||
/**
|
||||
* Gets the optional resource reference or color for the background color.
|
||||
* @return The background color's ResourceReference or JefColor.
|
||||
*/
|
||||
public Object getBackgroundColor() {
|
||||
return backgroundColor;
|
||||
}//getBackgroundColor()//
|
||||
/**
|
||||
* Gets the optional resource reference or color for the foreground color.
|
||||
* @return The foreground color's ResourceReference or JefColor.
|
||||
*/
|
||||
public Object getForegroundColor() {
|
||||
return foregroundColor;
|
||||
}//getForegroundColor()//
|
||||
/**
|
||||
* Gets the optional resource reference or font for the text.
|
||||
* @return The font's ResourceReference or JefFont.
|
||||
*/
|
||||
public Object getFont() {
|
||||
return font;
|
||||
}//getFont()//
|
||||
/**
|
||||
* Gets the optional resource reference or color for the selection color.
|
||||
* @return The selection color's ResourceReference or JefColor.
|
||||
*/
|
||||
public Object getSelectionGradient() {
|
||||
return selectionGradient;
|
||||
}//getSelectionGradient()//
|
||||
/**
|
||||
* Gets the optional tool tip resource reference or text.
|
||||
* @return The tool tip ResourceReference or String.
|
||||
*/
|
||||
public Object getToolTip() {
|
||||
return toolTip;
|
||||
}//getToolTip()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.common.io.IExternalizable#readExternal(com.common.io.IObjectInputStream)
|
||||
*/
|
||||
public Object readExternal(IObjectInputStream in) throws IOException, ClassNotFoundException {
|
||||
in.readByte();
|
||||
backgroundColor = in.readObject();
|
||||
foregroundColor = in.readObject();
|
||||
font = in.readObject();
|
||||
selectionGradient = in.readObject();
|
||||
toolTip = in.readObject();
|
||||
|
||||
return null;
|
||||
}//readExternal()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.common.io.IExternalizable#writeExternal(com.common.io.IObjectOutputStream)
|
||||
*/
|
||||
public void writeExternal(IObjectOutputStream out) throws IOException {
|
||||
out.writeByte(0);
|
||||
out.writeObject(backgroundColor);
|
||||
out.writeObject(foregroundColor);
|
||||
out.writeObject(font);
|
||||
out.writeObject(selectionGradient);
|
||||
out.writeObject(toolTip);
|
||||
}//writeExternal()//
|
||||
}//HighlightDecoration//
|
||||
44
Foundation/src/com/foundation/view/IAbstractComponent.java
Normal file
44
Foundation/src/com/foundation/view/IAbstractComponent.java
Normal file
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright (c) 2004,2009 Declarative Engineering LLC.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Declarative Engineering LLC
|
||||
* verson 1 which accompanies this distribution, and is available at
|
||||
* http://declarativeengineering.com/legal/DE_Developer_License_v1.txt
|
||||
*/
|
||||
package com.foundation.view;
|
||||
|
||||
import com.foundation.view.resource.AbstractResourceService;
|
||||
|
||||
public interface IAbstractComponent extends IAttributeAssociationChangeListener, IEventAssociationChangeListener, ISingleResourceAssociationChangeListener, IMultiResourceAssociationChangeListener, IVariableResourceAssociationChangeListener {
|
||||
/**
|
||||
* Gets the container for this component.
|
||||
* @return The component's parent container.
|
||||
*/
|
||||
public IAbstractContainer getContainer();
|
||||
/**
|
||||
* Called by the value holders when a registered attribute changes value or the value containing the attribute changes.
|
||||
* @param attributeAssociation The attribute association used to register with the attribute.
|
||||
*/
|
||||
public void onValueChanged(IAttributeAssociation attributeAssociation);
|
||||
/**
|
||||
* Called when any event association connected with this component is notified that the event has fired.
|
||||
* @param eventAssociation The event association for the event that was fired.
|
||||
* @param eventArguments The non-null event arguments as received by the event association.
|
||||
*/
|
||||
public void onEventFired(IEventAssociation eventAssociation, Object[] eventArguments);
|
||||
/**
|
||||
* Gets the resource service associated with this view component.
|
||||
* @return The view's resource service.
|
||||
*/
|
||||
public AbstractResourceService getResourceService();
|
||||
/**
|
||||
* Gets the component's name.
|
||||
* @return The name for the component which is useful for debugging.
|
||||
*/
|
||||
public String getName();
|
||||
/**
|
||||
* Verifies that the calling thread is allowed access to the view components.
|
||||
* <p>Note: This is really a debug only operation and should be turned off for production applications.</p>
|
||||
*/
|
||||
public void verifyThread();
|
||||
}//IAbstractComponent//
|
||||
33
Foundation/src/com/foundation/view/IAbstractContainer.java
Normal file
33
Foundation/src/com/foundation/view/IAbstractContainer.java
Normal file
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright (c) 2004,2009 Declarative Engineering LLC.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Declarative Engineering LLC
|
||||
* verson 1 which accompanies this distribution, and is available at
|
||||
* http://declarativeengineering.com/legal/DE_Developer_License_v1.txt
|
||||
*/
|
||||
package com.foundation.view;
|
||||
|
||||
import com.common.util.IList;
|
||||
|
||||
public interface IAbstractContainer extends IAbstractComponent, IView {
|
||||
/**
|
||||
* Gets the collection of components for this container.
|
||||
* @return The components contained within this container.
|
||||
*/
|
||||
public IList getComponents();
|
||||
/**
|
||||
* Adds the component to the container.
|
||||
* @param component The component added.
|
||||
*/
|
||||
public void addComponent(IAbstractComponent component);
|
||||
/**
|
||||
* Adds the component to the container.
|
||||
* @param component The component added.
|
||||
*/
|
||||
public void removeComponent(IAbstractComponent component);
|
||||
/**
|
||||
* Whether the component is suspending all layouts and packing while the view is getting setup.
|
||||
* @return Whether the component should not layout or pack.
|
||||
*/
|
||||
public boolean isSuspendingLayouts();
|
||||
}//IAbstractContainer//
|
||||
47
Foundation/src/com/foundation/view/IAssociationHandler.java
Normal file
47
Foundation/src/com/foundation/view/IAssociationHandler.java
Normal file
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright (c) 2003,2008 Declarative Engineering LLC.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Declarative Engineering LLC
|
||||
* verson 1 which accompanies this distribution, and is available at
|
||||
* http://declarativeengineering.com/legal/DE_Developer_License_v1.txt
|
||||
*/
|
||||
package com.foundation.view;
|
||||
|
||||
public interface IAssociationHandler {
|
||||
public static final byte INVOKE_GETTER_METHOD_FLAG = 0;
|
||||
public static final byte INVOKE_SETTER_METHOD_FLAG = 1;
|
||||
public static final byte INVOKE_ORIGINAL_VALUE_GETTER_METHOD_FLAG = 2;
|
||||
/**
|
||||
* Invokes a method that is neither a getter or setter method. An example would be an action method which is called when a button is pressed.
|
||||
* @param associationNumber The number that identifies the method to be called.
|
||||
* @param value The value which defines the method and is being invoked.
|
||||
* @param parameters The optional parameters which must be passed.
|
||||
* @return The result of the method invokation.
|
||||
*/
|
||||
public Object invokeMethod(int associationNumber, Object value, Object[] parameters);
|
||||
/**
|
||||
* Invokes a method that is either a getter or setter.
|
||||
* This differs from the invoke get/set method calls only in that N parameters may be passed.
|
||||
* This is primarily used by associations which may be bound to a value holder's value but pass a row object in addition to the standard getter/setter parameters.
|
||||
* @param associationNumber The number that identifies the method to be called.
|
||||
* @param value The value which defines the method and is being invoked.
|
||||
* @param parameters The optional parameters which must be passed.
|
||||
* @param flags Whether this method call is a getter, original value getter, or setter. The possible flags are defined in this class as INVOKE_XXX.
|
||||
* @return The result of the method invokation.
|
||||
*/
|
||||
public Object invokeMethod(int associationNumber, Object value, Object[] parameters, byte flags);
|
||||
/**
|
||||
* Invokes a get method associated with an attribute.
|
||||
* @param associationNumber The number identifying which attribute get method to call.
|
||||
* @param value The value which defines the method and is being invoked.
|
||||
* @return The result of the method invokation.
|
||||
*/
|
||||
public Object invokeGetMethod(int associationNumber, Object value);
|
||||
/**
|
||||
* Invokes a set method associated with an attribute.
|
||||
* @param associationNumber The number identifying which attribute set method to call.
|
||||
* @param value The value which defines the method and is being invoked.
|
||||
* @param parameter The value that the attribute will be assigned.
|
||||
*/
|
||||
public void invokeSetMethod(int associationNumber, Object value, Object parameter);
|
||||
}//IAssociationHandler//
|
||||
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Copyright (c) 2003,2009 Declarative Engineering LLC.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Declarative Engineering LLC
|
||||
* verson 1 which accompanies this distribution, and is available at
|
||||
* http://declarativeengineering.com/legal/DE_Developer_License_v1.txt
|
||||
*/
|
||||
package com.foundation.view;
|
||||
|
||||
public interface IAttributeAssociation extends IEventAssociation {
|
||||
/**
|
||||
* Gets the attribute value for the associated object.
|
||||
* <p>This method should only be called if the association <b>is</b> value holder associated.</p>
|
||||
* @return The value assigned to the attribute.
|
||||
* @see #getIsValueHolderAssociated()
|
||||
*/
|
||||
public Object getAttributeValue();
|
||||
/**
|
||||
* Gets the attribute value for the associated object.
|
||||
* <p>This method should only be called if the association <b>is not</b> value holder associated.</p>
|
||||
* @param object The object whose attribute value is desired.
|
||||
* @return The value assigned to the attribute.
|
||||
* @see #getIsValueHolderAssociated()
|
||||
*/
|
||||
public Object getAttributeValue(Object object);
|
||||
/**
|
||||
* Sets the attribute value for the associated object.
|
||||
* <p>This method should only be called if the association <b>is</b> value holder associated.</p>
|
||||
* @param value The value to be assigned to the attribute.
|
||||
* @see #getIsValueHolderAssociated()
|
||||
*/
|
||||
public void setAttributeValue(Object value);
|
||||
/**
|
||||
* Sets the attribute value for the associated object.
|
||||
* <p>This method should only be called if the association <b>is not</b> value holder associated.</p>
|
||||
* @param object The object whose attribute value is desired.
|
||||
* @param value The value to be assigned to the attribute.
|
||||
* @see #getIsValueHolderAssociated()
|
||||
*/
|
||||
public void setAttributeValue(Object object, Object value);
|
||||
/**
|
||||
* Registers the attribute association to begin receiving updates.
|
||||
*/
|
||||
public void register();
|
||||
/**
|
||||
* Unregisters the attribute association to stop receiving updates.
|
||||
*/
|
||||
public void unregister();
|
||||
/**
|
||||
* Gets the attribute name.
|
||||
* @return The name of the attribute being associated.
|
||||
*/
|
||||
public String getAttributeName();
|
||||
/**
|
||||
* Gets whether the association is connected via a value holder.
|
||||
* @return Whether there is a value holder connection for this association.
|
||||
*/
|
||||
public boolean getIsValueHolderAssociated();
|
||||
/**
|
||||
* Sets the change listener to something other than the component that created the association.
|
||||
* @param listener The listener which will be notified of attribute value changes.
|
||||
*/
|
||||
public void setChangeListener(IAttributeAssociationChangeListener listener);
|
||||
}//IAttributeAssociation//
|
||||
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* Copyright (c) 2005,2006 Declarative Engineering LLC.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Declarative Engineering LLC
|
||||
* verson 1 which accompanies this distribution, and is available at
|
||||
* http://declarativeengineering.com/legal/DE_Developer_License_v1.txt
|
||||
*/
|
||||
package com.foundation.view;
|
||||
|
||||
/**
|
||||
* The listener of attribute association change events such as when the attribute association receives an attribute value changed event.
|
||||
*/
|
||||
public interface IAttributeAssociationChangeListener {
|
||||
/**
|
||||
* Called by the value holders when a registered attribute changes value or the value containing the attribute changes.
|
||||
* @param attributeAssociation The attribute association used to register with the attribute.
|
||||
*/
|
||||
public void onValueChanged(IAttributeAssociation attributeAssociation);
|
||||
}//IAttributeAssociationChangeListener//
|
||||
@@ -0,0 +1,11 @@
|
||||
/*
|
||||
* Copyright (c) 2005,2009 Declarative Engineering LLC.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Declarative Engineering LLC
|
||||
* verson 1 which accompanies this distribution, and is available at
|
||||
* http://declarativeengineering.com/legal/DE_Developer_License_v1.txt
|
||||
*/
|
||||
package com.foundation.view;
|
||||
|
||||
public interface ICollectingMultiResourceAssociation extends IResourceAssociation {
|
||||
}//ICollectingMultiResourceAssociation//
|
||||
@@ -0,0 +1,11 @@
|
||||
/*
|
||||
* Copyright (c) 2005,2007 Declarative Engineering LLC.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Declarative Engineering LLC
|
||||
* verson 1 which accompanies this distribution, and is available at
|
||||
* http://declarativeengineering.com/legal/DE_Developer_License_v1.txt
|
||||
*/
|
||||
package com.foundation.view;
|
||||
|
||||
public interface ICollectingSingleResourceAssociation extends IResourceAssociation {
|
||||
}//ICollectingSingleResourceAssociation//
|
||||
34
Foundation/src/com/foundation/view/IEventAssociation.java
Normal file
34
Foundation/src/com/foundation/view/IEventAssociation.java
Normal file
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright (c) 2005,2009 Declarative Engineering LLC.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Declarative Engineering LLC
|
||||
* verson 1 which accompanies this distribution, and is available at
|
||||
* http://declarativeengineering.com/legal/DE_Developer_License_v1.txt
|
||||
*/
|
||||
package com.foundation.view;
|
||||
|
||||
/**
|
||||
* Provides an association between a view system and an event declared by an event emitter entity.
|
||||
*/
|
||||
public interface IEventAssociation extends IEventCapableAssociation {
|
||||
/** A reusable empty argument array. */
|
||||
public static final Object[] EMPTY_ARGS = new Object[0];
|
||||
/**
|
||||
* Registers the event association to begin receiving updates.
|
||||
*/
|
||||
public void register();
|
||||
/**
|
||||
* Unregisters the event association to stop receiving updates.
|
||||
*/
|
||||
public void unregister();
|
||||
/**
|
||||
* Gets whether the association is connected via a value holder.
|
||||
* @return Whether there is a value holder connection for this association.
|
||||
*/
|
||||
public boolean getIsValueHolderAssociated();
|
||||
/**
|
||||
* Sets the change listener to something other than the component that created the association.
|
||||
* @param listener The listener which will be notified of attribute value changes.
|
||||
*/
|
||||
public void setChangeListener(IEventAssociationChangeListener listener);
|
||||
}//IEventAssociation//
|
||||
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* Copyright (c) 2005,2006 Declarative Engineering LLC.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Declarative Engineering LLC
|
||||
* verson 1 which accompanies this distribution, and is available at
|
||||
* http://declarativeengineering.com/legal/DE_Developer_License_v1.txt
|
||||
*/
|
||||
package com.foundation.view;
|
||||
|
||||
public interface IEventAssociationChangeListener {
|
||||
/**
|
||||
* Called when any event association connected with this component is notified that the event has fired.
|
||||
* @param eventAssociation The event association for the event that was fired.
|
||||
* @param eventArguments The non-null event arguments as received by the event association.
|
||||
*/
|
||||
public void onEventFired(IEventAssociation eventAssociation, Object[] eventArguments);
|
||||
}//IEventAssociationChangeListener//
|
||||
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
* Copyright (c) 2005,2009 Declarative Engineering LLC.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Declarative Engineering LLC
|
||||
* verson 1 which accompanies this distribution, and is available at
|
||||
* http://declarativeengineering.com/legal/DE_Developer_License_v1.txt
|
||||
*/
|
||||
package com.foundation.view;
|
||||
|
||||
import com.foundation.event.IHandler;
|
||||
|
||||
public interface IEventCapableAssociation extends IHandler, IValueHolderAssociation {
|
||||
/**
|
||||
* Gets the unqiue (within the the context of the declaring classes' type hierarchy) event number.
|
||||
* @return The number of the event being associated.
|
||||
*/
|
||||
public int getEventNumber();
|
||||
}//IEventCapableAssociation//
|
||||
15
Foundation/src/com/foundation/view/IFormat.java
Normal file
15
Foundation/src/com/foundation/view/IFormat.java
Normal file
@@ -0,0 +1,15 @@
|
||||
package com.foundation.view;
|
||||
|
||||
import java.io.Externalizable;
|
||||
import java.text.Format;
|
||||
|
||||
/**
|
||||
* Copyright Declarative Engineering LLC 2009<p>
|
||||
*/
|
||||
public interface IFormat extends Externalizable, Cloneable {
|
||||
/**
|
||||
* Creates the actual formatting object.
|
||||
* @return The format instance that will do the actual formatting work.
|
||||
*/
|
||||
public Format createFormat();
|
||||
}//IFormat//
|
||||
25
Foundation/src/com/foundation/view/IKeyBinding.java
Normal file
25
Foundation/src/com/foundation/view/IKeyBinding.java
Normal file
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright (c) 2007 Declarative Engineering LLC.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Declarative Engineering LLC
|
||||
* verson 1 which accompanies this distribution, and is available at
|
||||
* http://declarativeengineering.com/legal/DE_Developer_License_v1.txt
|
||||
*/
|
||||
package com.foundation.view;
|
||||
|
||||
public interface IKeyBinding extends IMethodAssociation {
|
||||
public static final int MODIFIER_ALT = 0x01;
|
||||
public static final int MODIFIER_SHIFT = 0x02;
|
||||
public static final int MODIFIER_CONTROL = 0x04;
|
||||
public static final int MODIFIER_COMMAND = 0x08;
|
||||
/**
|
||||
* Gets the modifiers applied to the key code, or if the key code is null this may only contain one modifier.
|
||||
* @return The modifiers applied to the character typed.
|
||||
*/
|
||||
public int getModifiers();
|
||||
/**
|
||||
* Gets the unmodified character which causes the key event. This may be null if only the modifier should be used. In such a case there may only be one modifier specified.
|
||||
* @return The character typed.
|
||||
*/
|
||||
public Integer getKeyCode();
|
||||
}//IKeyBinding//
|
||||
46
Foundation/src/com/foundation/view/IMethodAssociation.java
Normal file
46
Foundation/src/com/foundation/view/IMethodAssociation.java
Normal file
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright (c) 2003,2007 Declarative Engineering LLC.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Declarative Engineering LLC
|
||||
* verson 1 which accompanies this distribution, and is available at
|
||||
* http://declarativeengineering.com/legal/DE_Developer_License_v1.txt
|
||||
*/
|
||||
package com.foundation.view;
|
||||
|
||||
public interface IMethodAssociation extends IValueHolderAssociation, IValueHolderListener {
|
||||
/**
|
||||
* Invokes the method on the held value.
|
||||
* @param parameters The parameters for the method. This may be null if there are no parameters.
|
||||
* @param synchronous Whether the invokation should be synchronous (on this same thread).
|
||||
* @return The return value for the method.
|
||||
*/
|
||||
public Object invoke(Object[] parameters, boolean synchronous);
|
||||
/**
|
||||
* Invokes the method on the specified value. This method must be called for method associations that are not connected to a value holder.
|
||||
* @param value The value to invoke the method on.
|
||||
* @param parameters The parameters for the method. This may be null if there are no parameters.
|
||||
* @param synchronous Whether the invokation should be synchronous (on this same thread).
|
||||
* @return The return value for the method.
|
||||
*/
|
||||
public Object invoke(Object value, Object[] parameters, boolean synchronous);
|
||||
/**
|
||||
* Gets whether the association is connected via a value holder.
|
||||
* @return Whether there is a value holder connection for this association.
|
||||
*/
|
||||
public boolean getIsValueHolderAssociated();
|
||||
/**
|
||||
* Registers the method association to begin receiving updates from the associated value holder.
|
||||
* <p>Note: This is only used if this method is associated with a value holder, and is a 'getter' type method.</p>
|
||||
*/
|
||||
public void register();
|
||||
/**
|
||||
* Unregisters the method association to stop receiving updates from the associated value holder.
|
||||
* <p>Note: This is only used if this method is associated with a value holder, and is a 'getter' type method.</p>
|
||||
*/
|
||||
public void unregister();
|
||||
/**
|
||||
* Sets the change listener that will be called, if the method is registered and is value holder bound, when the value holder's held value changes.
|
||||
* @param listener The listener to be notified when the associated value holder's held value changes.
|
||||
*/
|
||||
public void setChangeListener(IMethodAssociationChangeListener listener);
|
||||
}//IMethodAssociation//
|
||||
@@ -0,0 +1,16 @@
|
||||
/*
|
||||
* Copyright (c) 2005,2009 Declarative Engineering LLC.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Declarative Engineering LLC
|
||||
* verson 1 which accompanies this distribution, and is available at
|
||||
* http://declarativeengineering.com/legal/DE_Developer_License_v1.txt
|
||||
*/
|
||||
package com.foundation.view;
|
||||
|
||||
public interface IMethodAssociationChangeListener {
|
||||
/**
|
||||
* Called when any method association connected with this component is notified that the associated value holder's held value has changed.
|
||||
* @param methodAssociation The method association for the event that was fired.
|
||||
*/
|
||||
public void onEventFired(IMethodAssociation methodAssociation);
|
||||
}//IMethodAssociationChangeListener//
|
||||
@@ -0,0 +1,11 @@
|
||||
/*
|
||||
* Copyright (c) 2005,2009 Declarative Engineering LLC.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Declarative Engineering LLC
|
||||
* verson 1 which accompanies this distribution, and is available at
|
||||
* http://declarativeengineering.com/legal/DE_Developer_License_v1.txt
|
||||
*/
|
||||
package com.foundation.view;
|
||||
|
||||
public interface IMultiResourceAssociation extends IResourceAssociation {
|
||||
}//IMultiResourceAssociation//
|
||||
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Copyright (c) 2005,2009 Declarative Engineering LLC.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Declarative Engineering LLC
|
||||
* verson 1 which accompanies this distribution, and is available at
|
||||
* http://declarativeengineering.com/legal/DE_Developer_License_v1.txt
|
||||
*/
|
||||
package com.foundation.view;
|
||||
|
||||
import com.foundation.controller.DecorationManager;
|
||||
import com.foundation.view.resource.AbstractResourceService;
|
||||
|
||||
/**
|
||||
* Defines a listener to changes in a multi-resource association.
|
||||
*/
|
||||
public interface IMultiResourceAssociationChangeListener {
|
||||
/**
|
||||
* Called by the resource association (target ONLY) when the underlying model has changed, but a user's change is overriding it.
|
||||
* @param resourceAssociation The resource association used to register with the resource.
|
||||
* @param alteredItem The item whose value has been altered.
|
||||
* @param data The data that was passed in when the item was registered (most likely a row object encapsulating the item's control data).
|
||||
* @param isCleared Whether the original value was cleared, in which case the original value parameter should be ignored.
|
||||
* @param originalValue The original value.
|
||||
*/
|
||||
public void onModelExternallyChanged(ResourceAssociation resourceAssociation, Object alteredItem, Object data, boolean isCleared, Object originalValue);
|
||||
/**
|
||||
* Called by the resource association when a registered resource changes value.
|
||||
* @param resourceAssociation The resource association used to register with the resource.
|
||||
* @param alteredItem The item whose value has been altered.
|
||||
* @param data The data that was passed in when the item was registered (most likely a row object encapsulating the item's control data).
|
||||
* @param isUpdate Whether the change is an update from the reflected object.
|
||||
*/
|
||||
public void onValueChanged(ResourceAssociation resourceAssociation, Object alteredItem, Object data, boolean isUpdate);
|
||||
/**
|
||||
* Gets the resource service associated with this listener.
|
||||
* @return The listener's resource service.
|
||||
*/
|
||||
public AbstractResourceService getResourceService();
|
||||
/**
|
||||
* Gets the decoration manager for the view.
|
||||
* @return The view's decoration manager.
|
||||
*/
|
||||
public DecorationManager getDecorationManager();
|
||||
/**
|
||||
* Gives the listener the opportunity to temporarily halt messages that might result from setting the association value.
|
||||
* This is called when the association value is set to allow the view components to delay message processing until after the value is done being set.
|
||||
*/
|
||||
public void addMessageHold();
|
||||
/**
|
||||
* Called once for each call to addMessageHold().
|
||||
*/
|
||||
public void removeMessageHold();
|
||||
/**
|
||||
* Notifies the listener that a relevant decoration has been added.
|
||||
* @param association The association from which the decoration originated.
|
||||
* @param row The row from which the decoration originated.
|
||||
* @param data The data that was passed in when the item was registered (most likely a row object encapsulating the item's control data).
|
||||
* @param decoration The decoration added.
|
||||
*/
|
||||
public void addDecoration(ResourceAssociation association, Object row, Object data, AbstractDecoration decoration);
|
||||
/**
|
||||
* Notifies the listener that a relevant decoration has been removed.
|
||||
* @param association The association from which the decoration originated.
|
||||
* @param row The row from which the decoration originated.
|
||||
* @param data The data that was passed in when the item was registered (most likely a row object encapsulating the item's control data).
|
||||
* @param decoration The decoration removed.
|
||||
*/
|
||||
public void removeDecoration(ResourceAssociation association, Object row, Object data, AbstractDecoration decoration);
|
||||
}//IMultiResourceAssociationChangeListener//
|
||||
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Copyright (c) 2005,2009 Declarative Engineering LLC.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Declarative Engineering LLC
|
||||
* verson 1 which accompanies this distribution, and is available at
|
||||
* http://declarativeengineering.com/legal/DE_Developer_License_v1.txt
|
||||
*/
|
||||
package com.foundation.view;
|
||||
|
||||
import com.foundation.controller.DecorationManager;
|
||||
import com.foundation.view.resource.AbstractResourceService;
|
||||
|
||||
/**
|
||||
* Defines a listener to changes in a multi-resource association.
|
||||
*/
|
||||
public interface IMultiResourceAssociationChangeListener2 {
|
||||
/**
|
||||
* Called by the resource association (target ONLY) when the underlying model has changed, but a user's change is overriding it.
|
||||
* @param resourceAssociation The resource association used to register with the resource.
|
||||
* @param alteredItem The item whose value has been altered.
|
||||
* @param data The data that was passed in when the item was registered (most likely a row object encapsulating the item's control data).
|
||||
* @param isCleared Whether the original value was cleared, in which case the original value parameter should be ignored.
|
||||
* @param originalValue The original value.
|
||||
*/
|
||||
public void onModelExternallyChanged(ResourceAssociation resourceAssociation, Object alteredItem, Object data, boolean isCleared, Object originalValue);
|
||||
/**
|
||||
* Called by the resource association when a registered resource changes value.
|
||||
* @param resourceAssociation The resource association used to register with the resource.
|
||||
* @param alteredItem The item whose value has been altered.
|
||||
* @param data The data that was passed in when the item was registered (most likely a row object encapsulating the item's control data).
|
||||
* @param isUpdate Whether the change is an update from the reflected object.
|
||||
*/
|
||||
public void onValueChanged(ResourceAssociation resourceAssociation, Object alteredItem, Object data, boolean isUpdate);
|
||||
/**
|
||||
* Gets the resource service associated with this listener.
|
||||
* @return The listener's resource service.
|
||||
*/
|
||||
public AbstractResourceService getResourceService();
|
||||
/**
|
||||
* Gets the decoration manager for the view.
|
||||
* @return The view's decoration manager.
|
||||
*/
|
||||
public DecorationManager getDecorationManager();
|
||||
/**
|
||||
* Gives the listener the opportunity to temporarily halt messages that might result from setting the association value.
|
||||
* This is called when the association value is set to allow the view components to delay message processing until after the value is done being set.
|
||||
*/
|
||||
public void addMessageHold();
|
||||
/**
|
||||
* Called once for each call to addMessageHold().
|
||||
*/
|
||||
public void removeMessageHold();
|
||||
/**
|
||||
* Notifies the listener that a relevant decoration has been added.
|
||||
* @param association The association from which the decoration originated.
|
||||
* @param row The row from which the decoration originated.
|
||||
* @param data The data that was passed in when the item was registered (most likely a row object encapsulating the item's control data).
|
||||
* @param decoration The decoration added.
|
||||
*/
|
||||
public void addDecoration(ResourceAssociation association, Object row, Object data, AbstractDecoration decoration);
|
||||
/**
|
||||
* Notifies the listener that a relevant decoration has been removed.
|
||||
* @param association The association from which the decoration originated.
|
||||
* @param row The row from which the decoration originated.
|
||||
* @param data The data that was passed in when the item was registered (most likely a row object encapsulating the item's control data).
|
||||
* @param decoration The decoration removed.
|
||||
*/
|
||||
public void removeDecoration(ResourceAssociation association, Object row, Object data, AbstractDecoration decoration);
|
||||
}//IMultiResourceAssociationChangeListener//
|
||||
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* Copyright (c) 2003,2009 Declarative Engineering LLC.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Declarative Engineering LLC
|
||||
* verson 1 which accompanies this distribution, and is available at
|
||||
* http://declarativeengineering.com/legal/DE_Developer_License_v1.txt
|
||||
*/
|
||||
package com.foundation.view;
|
||||
|
||||
public interface IRemoteSessionContext extends IViewContext {
|
||||
/**
|
||||
* Creates a remote view context.
|
||||
* @param viewController The view controller requiring the view context.
|
||||
* @return The new view context.
|
||||
*/
|
||||
public IRemoteViewContext createViewContext(com.foundation.controller.AbstractViewController viewController);
|
||||
}//IRemoteSessionContext//
|
||||
223
Foundation/src/com/foundation/view/IRemoteViewContext.java
Normal file
223
Foundation/src/com/foundation/view/IRemoteViewContext.java
Normal file
@@ -0,0 +1,223 @@
|
||||
/*
|
||||
* Copyright (c) 2003,2009 Declarative Engineering LLC.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Declarative Engineering LLC
|
||||
* verson 1 which accompanies this distribution, and is available at
|
||||
* http://declarativeengineering.com/legal/DE_Developer_License_v1.txt
|
||||
*/
|
||||
package com.foundation.view;
|
||||
|
||||
import java.io.Externalizable;
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectInput;
|
||||
import java.io.ObjectOutput;
|
||||
|
||||
import com.common.orb.Orb;
|
||||
import com.common.thread.IRunnable;
|
||||
import com.common.util.IContentStream;
|
||||
|
||||
public interface IRemoteViewContext {
|
||||
/**
|
||||
* Tells the view context to send the messages to the client and not to wait at all for the messages to be received.
|
||||
*/
|
||||
public static final byte SEND_MESSAGES_ASYNCHRONOUS = 0;
|
||||
/**
|
||||
* Tells the view context to send the messages to the client and to wait long enough to be certain the client received them and queued them up.
|
||||
*/
|
||||
public static final byte SEND_MESSAGES_PARTIALLY_SYNCHRONOUS = 1;
|
||||
/**
|
||||
* Tells the view context to send the messages to the client and to wait for the messages to all be processed.
|
||||
*/
|
||||
public static final byte SEND_MESSAGES_FULLY_SYNCHRONOUS = 2;
|
||||
|
||||
/**
|
||||
* A simple container for the results of the remoteLoad operation.
|
||||
*/
|
||||
public static class LoadData {
|
||||
private String name = null;
|
||||
private byte[] data = null;
|
||||
|
||||
public LoadData(String name, byte[] data) {
|
||||
this.name = name;
|
||||
this.data = data;
|
||||
}//LoadData()//
|
||||
/**
|
||||
* Gets the name of the file or location where the data was retrieved. This should also contain any extension data that identifies the data type.
|
||||
* @return The name of the file or location of the data.
|
||||
*/
|
||||
public String getName() {
|
||||
return name;
|
||||
}//getName()//
|
||||
/**
|
||||
* Gets the contents of the load.
|
||||
* @return The contents of the file or data location.
|
||||
*/
|
||||
public byte[] getData() {
|
||||
return data;
|
||||
}//getData()//
|
||||
}//LoadData//
|
||||
|
||||
/**
|
||||
* A simple container for the results of the remoteStreamedLoad operation.
|
||||
*/
|
||||
public static class LoadStreamedData implements Externalizable {
|
||||
private String name;
|
||||
private long totalSize;
|
||||
private IContentStream contentStream = null;
|
||||
|
||||
/**
|
||||
* LoadStreamedData constructor.
|
||||
*/
|
||||
public LoadStreamedData() {
|
||||
}//LoadStreamedData()//
|
||||
/**
|
||||
* LoadStreamedData constructor.
|
||||
* @param name The name of the stream content. This might be the file name if streaming a file.
|
||||
* @param totalSize The size of the whole stream if known, otherwise less than zero.
|
||||
* @param contentStream The stream containing the data.
|
||||
*/
|
||||
public LoadStreamedData(String name, long totalSize, IContentStream contentStream) {
|
||||
this.name = name;
|
||||
this.totalSize = totalSize;
|
||||
this.contentStream = contentStream;
|
||||
}//LoadStreamedData()//
|
||||
/**
|
||||
* Gets the name of the file or location where the data was retrieved. This should also contain any extension data that identifies the data type.
|
||||
* @return The name of the file or location of the data.
|
||||
*/
|
||||
public String getName() {
|
||||
return name;
|
||||
}//getName()//
|
||||
/**
|
||||
* Gets the number of bytes of content.
|
||||
* @return The number of bytes in the content. This may be less than zero if the size is unknown.
|
||||
*/
|
||||
public long getTotalSize() {
|
||||
return totalSize;
|
||||
}//getTotalSize()//
|
||||
/**
|
||||
* Gets the contents of the load.
|
||||
* @return The contents of the file or data location.
|
||||
*/
|
||||
public IContentStream getContentStream() {
|
||||
return contentStream;
|
||||
}//getContentStream()//
|
||||
/* (non-Javadoc)
|
||||
* @see java.io.Externalizable#readExternal(java.io.ObjectInput)
|
||||
*/
|
||||
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
|
||||
name = (String) in.readObject();
|
||||
totalSize = in.readLong();
|
||||
contentStream = (IContentStream) in.readObject();
|
||||
}//readExternal()//
|
||||
/* (non-Javadoc)
|
||||
* @see java.io.Externalizable#writeExternal(java.io.ObjectOutput)
|
||||
*/
|
||||
public void writeExternal(ObjectOutput out) throws IOException {
|
||||
out.writeObject(name);
|
||||
out.writeLong(totalSize);
|
||||
out.writeObject(Orb.getProxy(contentStream, IContentStream.class));
|
||||
}//writeExternal()//
|
||||
}//LoadStreamedData//
|
||||
/**
|
||||
* Closes the view context when the view has been closed.
|
||||
*/
|
||||
public void close();
|
||||
/**
|
||||
* Decrements the hold count on the message queue so that messages will be queued up until the count reaches zero.
|
||||
*/
|
||||
public void decrementMessageHoldCount();
|
||||
/**
|
||||
* Increments the hold count on the message queue so that messages will be queued up until the count reaches zero or a message requires a return value.
|
||||
*/
|
||||
public void incrementMessageHoldCount();
|
||||
/**
|
||||
* Gets the session context associated with the view.
|
||||
*/
|
||||
public IRemoteSessionContext getSessionContext();
|
||||
/**
|
||||
* Whether the view context is valid.
|
||||
* @return Whether the view context is active.
|
||||
*/
|
||||
public boolean isValid();
|
||||
/**
|
||||
* Executes the runnable on the view's event thread.
|
||||
* <p>It is necessary to run most view related operations on the view thread since all views and all associated model and controller objects are not thread safe.</p>
|
||||
*/
|
||||
public Object execute(IRunnable runnable);
|
||||
/**
|
||||
* Executes the runnable on the view's event thread and returns immediatly.
|
||||
* <p>It is necessary to run most view related operations on the view thread since all views and all associated model and controller objects are not thread safe.</p>
|
||||
*/
|
||||
public void executeAsync(IRunnable runnable);
|
||||
/**
|
||||
* Save the data into a temp file with the given extension and then requests the operating system use the default application to open the file.
|
||||
* @param prefix The optional prefix for the file name.
|
||||
* @param extension The file extension which tells the operating system which application to use when opening the file. This value should only contain alpha numeric characters. Example: "pdf" or "doc".
|
||||
* @param data The contents of the file.
|
||||
*/
|
||||
public void remoteOpen(String prefix, String extension, byte[] data);
|
||||
/**
|
||||
* Saves the data to the location of the user's choice.
|
||||
* @param defaultName The optional default name for the file.
|
||||
* @param data The data the file will contain.
|
||||
* @deprecated Use the streamed call instead.
|
||||
*/
|
||||
public void remoteSave(String defaultName, byte[] data);
|
||||
/**
|
||||
* Loads the data from the location of the user's choice.
|
||||
* @param extensions The optional file extension which tells the system what file types to allow. The extensions should be formatted similar to: '*.pdf' or '*.*'.
|
||||
* @param allowMultiple Whether multiple files can be selected.
|
||||
* @return The data for the selected file, or null if no file was selected (user canceled the action).
|
||||
* @deprecated Use the streamed call instead.
|
||||
*/
|
||||
public LoadData[] remoteLoad(String[] extensions, boolean allowMultiple);
|
||||
/**
|
||||
* Saves the data to the location of the user's choice.
|
||||
* @param defaultPath The optional default path.
|
||||
* @param defaultName The optional default name for the file.
|
||||
* @param contentSize The optional size of the content. A value less than zero indicates that the size is unkown.
|
||||
* @param contentStream The content to be streamed to the client as the client is able to handle it.
|
||||
* @return The path and file name where the content was saved.
|
||||
*/
|
||||
public String remoteStreamedSaveToFile(String defaultPath, String defaultName, long contentSize, IContentStream contentStream);
|
||||
/**
|
||||
* Loads the data from the file location(s) of the user's choice.
|
||||
* @param extensions The optional file extension which tells the system what file types to allow. The extensions should be formatted similar to: '*.pdf' or '*.*'.
|
||||
* @param allowMultiple Whether multiple files can be selected.
|
||||
* @return The selected files wrappered in LoadStreamedData structures, or null if the user cancelled or otherwise selected nothing.
|
||||
*/
|
||||
public LoadStreamedData[] remoteStreamedLoadFromFile(String[] extensions, boolean allowMultiple);
|
||||
/**
|
||||
* Loads the data from the file location(s) of the user's choice.
|
||||
* @param extensions The optional file extension which tells the system what file types to allow. The extensions should be formatted similar to: '*.pdf' or '*.*'.
|
||||
* @param allowMultiple Whether multiple files can be selected.
|
||||
* @param fileName The optional file name. This can include the path.
|
||||
* @param filterPath The optional filter path. This is not necessary if fileName contains a path.
|
||||
* @return The selected files wrappered in LoadStreamedData structures, or null if the user cancelled or otherwise selected nothing.
|
||||
*/
|
||||
public LoadStreamedData[] remoteStreamedLoadFromFile(String[] extensions, boolean allowMultiple, String fileName, String filterPath);
|
||||
/**
|
||||
* Saves a bunch of files to the same directory.
|
||||
* @param defaultPath The default directory to save to.
|
||||
* @param fileNames The names of the files (must be the same length as content streams).
|
||||
* @param contentStreams The content streams for each file.
|
||||
* @return The count of streams written to files.
|
||||
*/
|
||||
public int remoteSaveToFilesStreamed(String defaultPath, String[] fileNames, IContentStream[] contentStreams);
|
||||
/**
|
||||
* Collects files that may need renaming.
|
||||
* @param extensions The optional file extension which tells the system what file types to allow. The extensions should be formatted similar to: '*.pdf' or '*.*'.
|
||||
* @param allowMultiple Whether multiple files can be selected.
|
||||
* @param fileName The optional file name. This can include the path.
|
||||
* @param filterPath The optional filter path. This is not necessary if fileName contains a path.
|
||||
* @return The array of paths and file names selected by the user.
|
||||
*/
|
||||
public String[] remoteCollectFilesToRename(String[] extensions, boolean allowMultiple, String fileName, String filterPath);
|
||||
/**
|
||||
* Renames the previously collected files.
|
||||
* @param newFileNames The array of new file names (and optional paths). This must be equal in length to the array returned by the last collect call.
|
||||
* @param includesPaths Whether the file names will include paths (and move the files).
|
||||
*/
|
||||
public void remoteRenameFiles(String[] newFileNames, boolean includesPaths);
|
||||
}//IRemoteViewContext()//
|
||||
26
Foundation/src/com/foundation/view/IResourceAssociation.java
Normal file
26
Foundation/src/com/foundation/view/IResourceAssociation.java
Normal file
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright (c) 2005,2007 Declarative Engineering LLC.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Declarative Engineering LLC
|
||||
* verson 1 which accompanies this distribution, and is available at
|
||||
* http://declarativeengineering.com/legal/DE_Developer_License_v1.txt
|
||||
*/
|
||||
package com.foundation.view;
|
||||
|
||||
/**
|
||||
* Defines the methods accessable by the view controller for a resource association.
|
||||
*/
|
||||
public interface IResourceAssociation {
|
||||
/**
|
||||
* Gets the optional default value which will be used as the resource value/URL if there isn't an attribute reference, or if the attribute's value is null.
|
||||
* @return The value to use for the resource value or URL if there isn't an attribute reference or attribute value.
|
||||
*/
|
||||
public Object getDefaultValue();
|
||||
/**
|
||||
* Sets the optional default value which will be used as the resource value/URL if there isn't an attribute reference, or if the attribute's value is null.
|
||||
* <p>Note: Calling this method will cause a value changed event to be processed by the component.</p>
|
||||
* <p>Warning: The default value is used only if the associated attribute's value is null, not if the attribute's value is a resource reference whose value is null.</p>
|
||||
* @param defaultValue The value to use for the resource value or URL if there isn't an attribute reference or attribute value.
|
||||
*/
|
||||
public void setDefaultValue(Object defaultValue);
|
||||
}//IResourceAssociation//
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright (c) 2006,2009 Declarative Engineering LLC.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Declarative Engineering LLC
|
||||
* verson 1 which accompanies this distribution, and is available at
|
||||
* http://declarativeengineering.com/legal/DE_Developer_License_v1.txt
|
||||
*/
|
||||
package com.foundation.view;
|
||||
|
||||
public interface IResourceAssociationTypes {
|
||||
/** The text resource association. */
|
||||
public static final int TYPE_TEXT = 1;
|
||||
/** The boolean (yes/no or true/false) resource association. */
|
||||
public static final int TYPE_BOOLEAN = 2;
|
||||
/** The color resource association. */
|
||||
public static final int TYPE_COLOR = 3;
|
||||
/** The font resource association. */
|
||||
public static final int TYPE_FONT = 4;
|
||||
/** The image resource association. */
|
||||
public static final int TYPE_IMAGE = 5;
|
||||
/** The integer resource association. */
|
||||
public static final int TYPE_INTEGER = 6;
|
||||
/** The long resource association. */
|
||||
public static final int TYPE_LONG = 7;
|
||||
/** The object resource association. */
|
||||
public static final int TYPE_OBJECT = 8;
|
||||
/** The float resource association. */
|
||||
public static final int TYPE_FLOAT = 9;
|
||||
/** The double resource association. */
|
||||
public static final int TYPE_DOUBLE = 10;
|
||||
/** The date resource association. */
|
||||
public static final int TYPE_DATE = 11;
|
||||
/** The gradient resource association. */
|
||||
public static final int TYPE_GRADIENT = 12;
|
||||
/** The gradient resource association. */
|
||||
public static final int TYPE_BIG_DECIMAL = 13;
|
||||
/** The images (array of images) resource association. */
|
||||
public static final int TYPE_IMAGES = 14;
|
||||
|
||||
/** The minimum (inclusive) number used to define the resource association's value type. */
|
||||
public static final int TYPE_MIN = 1;
|
||||
/** The maximum (inclusive) number used to define the resource association's value type. */
|
||||
public static final int TYPE_MAX = 14;
|
||||
}//IResourceAssociationTypes//
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright (c) 2006,2009 Declarative Engineering LLC.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Declarative Engineering LLC
|
||||
* verson 1 which accompanies this distribution, and is available at
|
||||
* http://declarativeengineering.com/legal/DE_Developer_License_v1.txt
|
||||
*/
|
||||
package com.foundation.view;
|
||||
|
||||
import com.common.util.IHashSet;
|
||||
import com.foundation.view.resource.AbstractResourceService;
|
||||
|
||||
public interface IResourceHolderComponent {
|
||||
/**
|
||||
* Called by a resource holder when the held value changes either due to a new value being set, or due to a resource being changed.
|
||||
* @param resourceHolder The resource holder that is sending the notification.
|
||||
* @param oldValue The old value for the resource.
|
||||
* @param newValue The new value for the resource.
|
||||
* @param flags The change flags as defined by ISingleResourceAssociationChangeListener.
|
||||
*/
|
||||
public void resourceHolderChanged(AbstractResourceHolder resourceHolder, Object oldValue, Object newValue, int flags);
|
||||
/**
|
||||
* Called by a resource holder when the held value changes either due to a new value being set, or due to a resource being changed.
|
||||
* @param resourceHolder The resource holder that is sending the notification.
|
||||
* @param row The row that is affected by the value change.
|
||||
* @param oldValue The old value for the resource.
|
||||
* @param newValue The new value for the resource.
|
||||
*/
|
||||
public void resourceHolderChanged(AbstractMultiResourceHolder resourceHolder, Object row, Object oldValue, Object newValue);
|
||||
/**
|
||||
* Called by a resource holder when the held value changes either due to a new value being set, or due to a resource being changed.
|
||||
* @param resourceHolder The resource holder that is sending the notification.
|
||||
* @param rows The rows that are affected by the value change.
|
||||
* @param oldValue The old value for the resource.
|
||||
* @param newValue The new value for the resource.
|
||||
*/
|
||||
public void resourceHolderChanged(AbstractMultiResourceHolder resourceHolder, IHashSet rows, Object oldValue, Object newValue);
|
||||
/**
|
||||
* Gets the resource service.
|
||||
* @return The component's resource service.
|
||||
*/
|
||||
public AbstractResourceService getResourceService();
|
||||
}//IResourceHolderComponent//
|
||||
@@ -0,0 +1,11 @@
|
||||
/*
|
||||
* Copyright (c) 2005,2008 Declarative Engineering LLC.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Declarative Engineering LLC
|
||||
* verson 1 which accompanies this distribution, and is available at
|
||||
* http://declarativeengineering.com/legal/DE_Developer_License_v1.txt
|
||||
*/
|
||||
package com.foundation.view;
|
||||
|
||||
public interface ISingleResourceAssociation extends IResourceAssociation {
|
||||
}//ISingleResourceAssociation//
|
||||
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Copyright (c) 2005,2009 Declarative Engineering LLC.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Declarative Engineering LLC
|
||||
* verson 1 which accompanies this distribution, and is available at
|
||||
* http://declarativeengineering.com/legal/DE_Developer_License_v1.txt
|
||||
*/
|
||||
package com.foundation.view;
|
||||
|
||||
import com.foundation.controller.DecorationManager;
|
||||
import com.foundation.view.resource.AbstractResourceService;
|
||||
|
||||
/**
|
||||
* Defines a listener to changes in a single resource association.
|
||||
*/
|
||||
public interface ISingleResourceAssociationChangeListener {
|
||||
public static final int FLAG_NONE = 0;
|
||||
/** The event is caused by the reflection getting updated. */
|
||||
public static final int FLAG_IS_UPDATE = 1;
|
||||
/** The event is caused by the model backing the result changing (eg: the user selects a different model from a list and now the editor must display the new attribute value). */
|
||||
public static final int FLAG_CONTAINING_MODEL_CHANGED = 2;
|
||||
/**
|
||||
* Called by the resource association (target ONLY) when the underlying model has changed, but a user's change is overriding it.
|
||||
* @param resourceAssociation The resource association used to register with the resource.
|
||||
* @param isCleared Whether the original value was cleared, in which case the original value parameter should be ignored.
|
||||
* @param originalValue The original value.
|
||||
*/
|
||||
public void onModelExternallyChanged(ResourceAssociation resourceAssociation, boolean isCleared, Object originalValue);
|
||||
/**
|
||||
* Called by the resource association when a registered resource changes value.
|
||||
* @param resourceAssociation The resource association used to register with the resource.
|
||||
* @param flags The change flags as defined by ISingleResourceAssociationChangeListener.
|
||||
*/
|
||||
public void onValueChanged(ResourceAssociation resourceAssociation, int flags);
|
||||
/**
|
||||
* Gets the resource service associated with this listener.
|
||||
* @return The listener's resource service.
|
||||
*/
|
||||
public AbstractResourceService getResourceService();
|
||||
/**
|
||||
* Gets the decoration manager for the view.
|
||||
* @return The view's decoration manager.
|
||||
*/
|
||||
public DecorationManager getDecorationManager();
|
||||
/**
|
||||
* Gives the listener the opportunity to temporarily halt messages that might result from setting the association value.
|
||||
* This is called when the association value is set to allow the view components to delay message processing until after the value is done being set.
|
||||
*/
|
||||
public void addMessageHold();
|
||||
/**
|
||||
* Called once for each call to addMessageHold().
|
||||
*/
|
||||
public void removeMessageHold();
|
||||
/**
|
||||
* Notifies the listener that a relevant decoration has been added.
|
||||
* @param decoration The decoration added.
|
||||
*/
|
||||
public void addDecoration(AbstractDecoration decoration);
|
||||
/**
|
||||
* Notifies the listener that a relevant decoration has been removed.
|
||||
* @param decoration The decoration removed.
|
||||
*/
|
||||
public void removeDecoration(AbstractDecoration decoration);
|
||||
}//ISingleResourceAssociationChangeListener//
|
||||
46
Foundation/src/com/foundation/view/IValueHolder.java
Normal file
46
Foundation/src/com/foundation/view/IValueHolder.java
Normal file
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright (c) 2004,2009 Declarative Engineering LLC.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Declarative Engineering LLC
|
||||
* verson 1 which accompanies this distribution, and is available at
|
||||
* http://declarativeengineering.com/legal/DE_Developer_License_v1.txt
|
||||
*/
|
||||
package com.foundation.view;
|
||||
|
||||
public interface IValueHolder {
|
||||
/**
|
||||
* Gets the value holder's name.
|
||||
* @return The name of the value holder.
|
||||
*/
|
||||
public String getName();
|
||||
/**
|
||||
* Gets the value holder's held value.
|
||||
* @return The value held by the value holder.
|
||||
*/
|
||||
public com.foundation.event.IEventEmitter getValue();
|
||||
/**
|
||||
* Gets the class for the values held by this value holder.
|
||||
* @return The held value type.
|
||||
*/
|
||||
public Class getHeldType();
|
||||
/**
|
||||
* Registers an event association so that its component will receive future event notification.
|
||||
* @param eventAssociation The component's event association which specifies which event to listen to.
|
||||
*/
|
||||
public void registerListener(IEventCapableAssociation eventAssociation);
|
||||
/**
|
||||
* Unregisters an event association so that its component will not receive future event notification.
|
||||
* @param eventAssociation The component's event association which specifies which event to stop listening to.
|
||||
*/
|
||||
public void unregisterListener(IEventCapableAssociation eventAssociation);
|
||||
/**
|
||||
* Registers a listener that will be notified when the value holder's value changes.
|
||||
* @param listener The listener to be registered.
|
||||
*/
|
||||
public void registerListener(IValueHolderListener listener);
|
||||
/**
|
||||
* Unregisters a listener that was previously registered.
|
||||
* @param listener The listener to be unregistered.
|
||||
*/
|
||||
public void unregisterListener(IValueHolderListener listener);
|
||||
}//IValueHolder//
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright (c) 2005,2009 Declarative Engineering LLC.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Declarative Engineering LLC
|
||||
* verson 1 which accompanies this distribution, and is available at
|
||||
* http://declarativeengineering.com/legal/DE_Developer_License_v1.txt
|
||||
*/
|
||||
package com.foundation.view;
|
||||
|
||||
public interface IValueHolderAssociation {
|
||||
/**
|
||||
* Gets the value holder associated with the component.
|
||||
* @return The associated value holder, or null if one was not located with the proper name.
|
||||
*/
|
||||
public IValueHolder getValueHolder();
|
||||
/**
|
||||
* Gets the class for the class of object held by the associated value holder. If the value holder's held value is not of this type then this association is not applicable.
|
||||
* @return The class that determines applicability when associated with a value holder.
|
||||
*/
|
||||
public Class getValueHolderHeldType();
|
||||
/**
|
||||
* Gets the class used to determine whether this association applies to a specific row object (or parent object in the case of chaining).
|
||||
* @return The class that this association applies to for non-value holder relationships.
|
||||
*/
|
||||
public Class getRowType();
|
||||
/**
|
||||
* Gets whether the value holder association is connected to a value holder by name.
|
||||
* @return Whether there is a potential value holder connection.
|
||||
*/
|
||||
public boolean getIsValueHolderAssociated();
|
||||
/**
|
||||
* Determines whether the association can be applied to the held value (only useful when dealing with a value holder bound associations).
|
||||
* @return Whether the association exists in the held value.
|
||||
*/
|
||||
public boolean isApplicable();
|
||||
/**
|
||||
* Determines whether the association can be applied to the given value (only useful when dealing with a non value holder bound associations).
|
||||
* @param value The value to check against.
|
||||
* @return Whether the association exists in the passed value, or if the association is value holder bound then whether the method exists in the value holder's held value.
|
||||
*/
|
||||
public boolean isApplicable(Object value);
|
||||
}//IValueHolderAssociation//
|
||||
15
Foundation/src/com/foundation/view/IValueHolderListener.java
Normal file
15
Foundation/src/com/foundation/view/IValueHolderListener.java
Normal file
@@ -0,0 +1,15 @@
|
||||
/*
|
||||
* Copyright (c) 2005,2007 Declarative Engineering LLC.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Declarative Engineering LLC
|
||||
* verson 1 which accompanies this distribution, and is available at
|
||||
* http://declarativeengineering.com/legal/DE_Developer_License_v1.txt
|
||||
*/
|
||||
package com.foundation.view;
|
||||
|
||||
public interface IValueHolderListener {
|
||||
/**
|
||||
* Called when the value holder's held value is altered.
|
||||
*/
|
||||
public void heldValueChanged();
|
||||
}//IValueHolderListener//
|
||||
@@ -0,0 +1,11 @@
|
||||
/*
|
||||
* Copyright (c) 2006,2007 Declarative Engineering LLC.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Declarative Engineering LLC
|
||||
* verson 1 which accompanies this distribution, and is available at
|
||||
* http://declarativeengineering.com/legal/DE_Developer_License_v1.txt
|
||||
*/
|
||||
package com.foundation.view;
|
||||
|
||||
public interface IVariableResourceAssociationChangeListener extends ISingleResourceAssociationChangeListener, IMultiResourceAssociationChangeListener {
|
||||
}//IVariableResourceAssociationChangeListener//
|
||||
129
Foundation/src/com/foundation/view/IView.java
Normal file
129
Foundation/src/com/foundation/view/IView.java
Normal file
@@ -0,0 +1,129 @@
|
||||
/*
|
||||
* Copyright (c) 2003,2009 Declarative Engineering LLC.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Declarative Engineering LLC
|
||||
* verson 1 which accompanies this distribution, and is available at
|
||||
* http://declarativeengineering.com/legal/DE_Developer_License_v1.txt
|
||||
*/
|
||||
package com.foundation.view;
|
||||
|
||||
import com.common.thread.IRunnable;
|
||||
import com.foundation.event.IRequestHandler;
|
||||
import com.foundation.controller.AbstractViewController;
|
||||
|
||||
/**
|
||||
* An interface implemented by view components that can be the root of a view or partial view.
|
||||
*/
|
||||
public interface IView {
|
||||
/**
|
||||
* Gets the request handler associated with the view.
|
||||
* @return The view's request handler.
|
||||
*/
|
||||
public IRequestHandler getRequestHandler();
|
||||
/**
|
||||
* Executes the given runnable in a thread safe way.
|
||||
* @param runnable The object to be executed within the view's thread.
|
||||
* @return The return value of the given runnable.
|
||||
*/
|
||||
public Object execute(IRunnable runnable);
|
||||
/**
|
||||
* Executes the given runnable in a thread safe way and does not wait around for the operation to finish.
|
||||
* @param runnable The object to be executed within the view's thread.
|
||||
*/
|
||||
public void executeAsync(IRunnable runnable);
|
||||
/**
|
||||
* Gets the controller associated with the view or view part.
|
||||
* @return The controller reference that is managing the view.
|
||||
*/
|
||||
public AbstractViewController getController();
|
||||
/**
|
||||
* Sets the controller associated with the view or view part.
|
||||
* @param controller The controller reference that is managing the view.
|
||||
*/
|
||||
public void setController(AbstractViewController controller);
|
||||
/**
|
||||
* Sets the view so that it receives the focus.
|
||||
*/
|
||||
public void setFocus();
|
||||
/**
|
||||
* Determines whether the view is enabled and the user is able to interact with it.
|
||||
* @param isEnabled Whether the view and its components are enabled (or if not then disabled).
|
||||
*/
|
||||
public void setIsEnabled(boolean isEnabled);
|
||||
/**
|
||||
* Determines whether the view and its children are visible.
|
||||
* @param isVisible Whether the view and its components will be visible.
|
||||
*/
|
||||
public void setIsVisible(boolean isVisible);
|
||||
/**
|
||||
* Lays out the view components.
|
||||
*/
|
||||
public void layout();
|
||||
/**
|
||||
* Packs the view so it takes up a minimum amount of space.
|
||||
*/
|
||||
public void pack();
|
||||
/**
|
||||
* Centers the view on the primary monitor.
|
||||
*/
|
||||
public void center();
|
||||
/**
|
||||
* Centers the view on the primary monitor.
|
||||
* @param centerOnView The view to center on. If this is null then it will center on the primary monitor.
|
||||
*/
|
||||
public void center(IView centerOnView);
|
||||
/**
|
||||
* Sets the view to be visible, non-iconified, and have focus.
|
||||
*/
|
||||
public void show();
|
||||
/**
|
||||
* Maximizes the view so that it takes up all available screen realestate.
|
||||
*/
|
||||
public void maximize();
|
||||
/**
|
||||
* Minimizes the view so that it takes up no screen realestate.
|
||||
*/
|
||||
public void minimize();
|
||||
/**
|
||||
* Initializes this view and all its' contained components.
|
||||
*/
|
||||
public void viewInitializeAll();
|
||||
/**
|
||||
* Refreshes this view and all its' contained components.
|
||||
*/
|
||||
public void viewRefreshAll();
|
||||
/**
|
||||
* Releases this view and all its' contained components.
|
||||
*/
|
||||
public void viewReleaseAll();
|
||||
/**
|
||||
* Synchronizes this view and all its' contained components.
|
||||
*/
|
||||
public void viewSynchronizeAll();
|
||||
/**
|
||||
* Gets the view context for this view.
|
||||
* @return The context in which this view exists.
|
||||
*/
|
||||
public IViewContext getViewContext();
|
||||
/**
|
||||
* Determines whether the calling thread is the view's event thread.
|
||||
* @return Whether the view's event thread and the calling thread are one and the same.
|
||||
*/
|
||||
public boolean isViewThread();
|
||||
/**
|
||||
* Increments the hold count on the message queue so that messages will be queued up until the count reaches zero.
|
||||
*/
|
||||
public void addMessageHold();
|
||||
/**
|
||||
* Decrements the hold count on the message queue so that messages will be queued up until the count reaches zero.
|
||||
*/
|
||||
public void removeMessageHold();
|
||||
/**
|
||||
* Suspends all layouts and packing while the view is getting setup.
|
||||
*/
|
||||
public void suspendLayouts();
|
||||
/**
|
||||
* Resumes processing of requests to layout or pack components.
|
||||
*/
|
||||
public void resumeLayouts();
|
||||
}//IView//
|
||||
47
Foundation/src/com/foundation/view/IViewContext.java
Normal file
47
Foundation/src/com/foundation/view/IViewContext.java
Normal file
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright (c) 2005,2006 Declarative Engineering LLC.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Declarative Engineering LLC
|
||||
* verson 1 which accompanies this distribution, and is available at
|
||||
* http://declarativeengineering.com/legal/DE_Developer_License_v1.txt
|
||||
*/
|
||||
package com.foundation.view;
|
||||
|
||||
/**
|
||||
* The view context represents the view system that the views are being displayed in.
|
||||
* Any functionality related to the view system but not a perticular view is accessable here.
|
||||
*/
|
||||
public interface IViewContext {
|
||||
/**
|
||||
* Gets the view system metadata which describes the view system.
|
||||
* @return The metadata describing the view system.
|
||||
*/
|
||||
public ViewSystemMetadata getViewSystemMetadata();
|
||||
/**
|
||||
* Gets the request handler for the view context.
|
||||
* @return The request handler which manages the threading and processing of requests within this view context.
|
||||
*/
|
||||
public IViewRequestHandler getRequestHandler();
|
||||
/**
|
||||
* Gets the data specific to the application and this context.
|
||||
* @return The application specific context data.
|
||||
*/
|
||||
public Object getApplicationData();
|
||||
/**
|
||||
* Sets the data specific to the application and this context.
|
||||
* @param applicationData The application specific context data.
|
||||
*/
|
||||
public void setApplicationData(Object applicationData);
|
||||
/**
|
||||
* Gets the data specific to the application and this context.
|
||||
* @param key The key that indexes the data. Only one piece of data can be associated with a key. Keys are logically compared (doesn't use exact equality). The Key must provide a functional hashCode() and equals() implementation.
|
||||
* @return The application specific context data.
|
||||
*/
|
||||
public Object getApplicationData(Object key);
|
||||
/**
|
||||
* Sets the data specific to the application and this context.
|
||||
* @param key The key that indexes the data. Only one piece of data can be associated with a key. Keys are logically compared (doesn't use exact equality). The Key must provide a functional hashCode() and equals() implementation.
|
||||
* @param applicationData The application specific context data.
|
||||
*/
|
||||
public void setApplicationData(Object key, Object applicationData);
|
||||
}//IViewContext//
|
||||
19
Foundation/src/com/foundation/view/IViewRequestHandler.java
Normal file
19
Foundation/src/com/foundation/view/IViewRequestHandler.java
Normal file
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* Copyright (c) 2005,2009 Declarative Engineering LLC.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Declarative Engineering LLC
|
||||
* verson 1 which accompanies this distribution, and is available at
|
||||
* http://declarativeengineering.com/legal/DE_Developer_License_v1.txt
|
||||
*/
|
||||
package com.foundation.view;
|
||||
|
||||
import com.foundation.event.IRequestHandler;
|
||||
|
||||
public interface IViewRequestHandler extends IRequestHandler {
|
||||
/**
|
||||
* Processes a request through the event thread. This is required to access any SWT method due to the design of SWT.
|
||||
* @param request The object that will be run when the request is processed.
|
||||
* @param synchronous Whether the thread should block until the request has been processed (should be true if a result is desired).
|
||||
*/
|
||||
public void processRequest(Runnable request, boolean synchronous);
|
||||
}//IViewRequestHandler//
|
||||
99
Foundation/src/com/foundation/view/ImageDecoration.java
Normal file
99
Foundation/src/com/foundation/view/ImageDecoration.java
Normal file
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
* Copyright (c) 2007,2009 Declarative Engineering LLC.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Declarative Engineering LLC
|
||||
* verson 1 which accompanies this distribution, and is available at
|
||||
* http://declarativeengineering.com/legal/DE_Developer_License_v1.txt
|
||||
*/
|
||||
package com.foundation.view;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import com.common.io.IObjectInputStream;
|
||||
import com.common.io.IObjectOutputStream;
|
||||
import com.foundation.view.resource.ResourceReference;
|
||||
|
||||
/*
|
||||
* Decorates an image by overlaying one image on top of another.
|
||||
* This is useful for lists and trees where each row has an image which can be decorated to show something about the object the row represents.
|
||||
*/
|
||||
public class ImageDecoration extends AbstractDecoration {
|
||||
private ResourceReference imageReference = null;
|
||||
/** The alpha value used when rendering the image. Must be on the range of (0..1). */
|
||||
private float alpha = 0.8f;
|
||||
/**
|
||||
* ImageDecoration constructor.
|
||||
* <p>Warning: This constructor is to allow serialization only.</p>
|
||||
*/
|
||||
public ImageDecoration() {
|
||||
super();
|
||||
}//ImageDecoration()//
|
||||
/**
|
||||
* ImageDecoration constructor.
|
||||
* @param imageReference The reference to the image in the resources. Example: "res://package/group/resourceName".
|
||||
*/
|
||||
public ImageDecoration(String imageReference) {
|
||||
super();
|
||||
this.imageReference = new ResourceReference(imageReference);
|
||||
}//ImageDecoration()//
|
||||
/**
|
||||
* ImageDecoration constructor.
|
||||
* @param imageReference The reference to the image in the resources.
|
||||
*/
|
||||
public ImageDecoration(ResourceReference imageReference) {
|
||||
super();
|
||||
this.imageReference = imageReference;
|
||||
}//ImageDecoration()//
|
||||
/**
|
||||
* ImageDecoration constructor.
|
||||
* @param imageReference The reference to the image in the resources. Example: "res://package/group/resourceName".
|
||||
* @param alpha The optional alpha value to use when rendering the image. Must be on the range of (0..1).
|
||||
*/
|
||||
public ImageDecoration(String imageReference, float alpha) {
|
||||
super();
|
||||
this.imageReference = new ResourceReference(imageReference);
|
||||
this.alpha = alpha < 0 || alpha > 1 ? 1 : alpha;
|
||||
}//ImageDecoration()//
|
||||
/**
|
||||
* ImageDecoration constructor.
|
||||
* @param imageReference The reference to the image in the resources.
|
||||
* @param alpha The optional alpha value to use when rendering the image. Must be on the range of (0..1).
|
||||
*/
|
||||
public ImageDecoration(ResourceReference imageReference, float alpha) {
|
||||
super();
|
||||
this.imageReference = imageReference;
|
||||
this.alpha = alpha < 0 || alpha > 1 ? 1 : alpha;
|
||||
}//ImageDecoration()//
|
||||
/**
|
||||
* Gets the alpha value for the image.
|
||||
* @return The alpha used when rendering the image.
|
||||
*/
|
||||
public float getAlpha() {
|
||||
return alpha;
|
||||
}//getAlpha()//
|
||||
/**
|
||||
* Gets the resource reference for the image.
|
||||
* @return The image's resource reference.
|
||||
*/
|
||||
public ResourceReference getImageReference() {
|
||||
return imageReference;
|
||||
}//getImageReference()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.common.io.IExternalizable#readExternal(com.common.io.IObjectInputStream)
|
||||
*/
|
||||
public Object readExternal(IObjectInputStream in) throws IOException, ClassNotFoundException {
|
||||
in.readByte();
|
||||
imageReference = new ResourceReference(in.readUTF8());
|
||||
alpha = in.readFloat();
|
||||
|
||||
return null;
|
||||
}//readExternal()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.common.io.IExternalizable#writeExternal(com.common.io.IObjectOutputStream)
|
||||
*/
|
||||
public void writeExternal(IObjectOutputStream out) throws IOException {
|
||||
out.writeByte(0);
|
||||
out.writeUTF8(imageReference.toString());
|
||||
out.writeFloat(alpha);
|
||||
}//writeExternal()//
|
||||
}//ImageDecoration//
|
||||
639
Foundation/src/com/foundation/view/JefColor.java
Normal file
639
Foundation/src/com/foundation/view/JefColor.java
Normal file
@@ -0,0 +1,639 @@
|
||||
/*
|
||||
* Copyright (c) 2004,2009 Declarative Engineering LLC.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Declarative Engineering LLC
|
||||
* verson 1 which accompanies this distribution, and is available at
|
||||
* http://declarativeengineering.com/legal/DE_Developer_License_v1.txt
|
||||
*/
|
||||
package com.foundation.view;
|
||||
|
||||
import java.io.Externalizable;
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectInput;
|
||||
import java.io.ObjectOutput;
|
||||
|
||||
import com.common.util.StringSupport;
|
||||
import com.common.util.optimized.*;
|
||||
|
||||
/*
|
||||
* An immutable color descriptor.
|
||||
*/
|
||||
public final class JefColor implements Externalizable, Cloneable {
|
||||
/** Default color white (value is 0). */
|
||||
public static final int COLOR_WHITE = 0;
|
||||
/** Default color black (value is 1). */
|
||||
public static final int COLOR_BLACK = 1;
|
||||
/** Default color red (value is 2). */
|
||||
public static final int COLOR_RED = 2;
|
||||
/** Default color dark red (value is 3). */
|
||||
public static final int COLOR_DARK_RED = 3;
|
||||
/** Default color green (value is 4). */
|
||||
public static final int COLOR_GREEN = 4;
|
||||
/** Default color dark green (value is 5). */
|
||||
public static final int COLOR_DARK_GREEN = 5;
|
||||
/** Default color yellow (value is 6). */
|
||||
public static final int COLOR_YELLOW = 6;
|
||||
/** Default color dark yello (value is 7). */
|
||||
public static final int COLOR_DARK_YELLOW = 7;
|
||||
/** Default color blue (value is 8). */
|
||||
public static final int COLOR_BLUE = 8;
|
||||
/** Default color dark blue (value is 9). */
|
||||
public static final int COLOR_DARK_BLUE = 9;
|
||||
/** Default color magenta (value is 10). */
|
||||
public static final int COLOR_MAGENTA = 10;
|
||||
/** Default color dark magenta (value is 11). */
|
||||
public static final int COLOR_DARK_MAGENTA = 11;
|
||||
/** Default color cyan (value is 12). */
|
||||
public static final int COLOR_CYAN = 12;
|
||||
/** Default color dark cyan (value is 13). */
|
||||
public static final int COLOR_DARK_CYAN = 13;
|
||||
/** Default color gray (value is 14). */
|
||||
public static final int COLOR_GRAY = 14;
|
||||
/** Default color dark gray (value is 15). */
|
||||
public static final int COLOR_DARK_GRAY = 15;
|
||||
/** Default color gray (value is 14). */
|
||||
public static final int COLOR_GREY = 14;
|
||||
/** Default color dark gray (value is 15). */
|
||||
public static final int COLOR_DARK_GREY = 15;
|
||||
/** System color used to paint dark shadow areas (value is 16). */
|
||||
public static final int COLOR_WIDGET_DARK_SHADOW = 16;
|
||||
/** System color used to paint normal shadow areas (value is 17). */
|
||||
public static final int COLOR_WIDGET_NORMAL_SHADOW = 17;
|
||||
/** System color used to paint light shadow areas (value is 18). */
|
||||
public static final int COLOR_WIDGET_LIGHT_SHADOW = 18;
|
||||
/** System color used to paint highlight shadow areas (value is 19). */
|
||||
public static final int COLOR_WIDGET_HIGHLIGHT_SHADOW = 19;
|
||||
/** System color used to paint foreground areas (value is 20). */
|
||||
public static final int COLOR_WIDGET_FOREGROUND = 20;
|
||||
/** System color used to paint background areas (value is 21). */
|
||||
public static final int COLOR_WIDGET_BACKGROUND = 21;
|
||||
/** System color used to paint border areas (value is 22). */
|
||||
public static final int COLOR_WIDGET_BORDER = 22;
|
||||
/** System color used to paint list foreground areas (value is 23). */
|
||||
public static final int COLOR_LIST_FOREGROUND = 23;
|
||||
/** System color used to paint list background areas (value is 24). */
|
||||
public static final int COLOR_LIST_BACKGROUND = 24;
|
||||
/** System color used to paint list selection background areas (value is 25). */
|
||||
public static final int COLOR_LIST_SELECTION = 25;
|
||||
/** System color used to paint list selected text (value is 26). */
|
||||
public static final int COLOR_LIST_SELECTION_TEXT = 26;
|
||||
/** System color used to paint tooltip text (value is 27). */
|
||||
public static final int COLOR_INFO_FOREGROUND = 27;
|
||||
/** System color used to paint tooltip background areas (value is 28). */
|
||||
public static final int COLOR_INFO_BACKGROUND = 28;
|
||||
/** System color used to paint title text (value is 29). */
|
||||
public static final int COLOR_TITLE_FOREGROUND = 29;
|
||||
/** System color used to paint title background areas (value is 30). */
|
||||
public static final int COLOR_TITLE_BACKGROUND = 30;
|
||||
/** System color used to paint title background gradient (value is 31). */
|
||||
public static final int COLOR_TITLE_BACKGROUND_GRADIENT = 31;
|
||||
/** System color used to paint inactive title text (value is 32). */
|
||||
public static final int COLOR_TITLE_INACTIVE_FOREGROUND = 32;
|
||||
/** System color used to paint inactive title background areas (value is 33). */
|
||||
public static final int COLOR_TITLE_INACTIVE_BACKGROUND = 33;
|
||||
/** System color used to paint inactive title background gradient (value is 34). */
|
||||
public static final int COLOR_TITLE_INACTIVE_BACKGROUND_GRADIENT = 34;
|
||||
|
||||
public static final String COLOR_NAME_WHITE = "white";
|
||||
public static final String COLOR_NAME_BLACK = "black";
|
||||
public static final String COLOR_NAME_RED = "red";
|
||||
public static final String COLOR_NAME_DARK_RED = "dark-red";
|
||||
public static final String COLOR_NAME_GREEN = "green";
|
||||
public static final String COLOR_NAME_DARK_GREEN = "dark-green";
|
||||
public static final String COLOR_NAME_YELLOW = "yellow";
|
||||
public static final String COLOR_NAME_DARK_YELLOW = "dark-yellow";
|
||||
public static final String COLOR_NAME_BLUE = "blue";
|
||||
public static final String COLOR_NAME_DARK_BLUE = "dark-blue";
|
||||
public static final String COLOR_NAME_MAGENTA = "magenta";
|
||||
public static final String COLOR_NAME_DARK_MAGENTA = "dark-magenta";
|
||||
public static final String COLOR_NAME_CYAN = "cyan";
|
||||
public static final String COLOR_NAME_DARK_CYAN = "dark-cyan";
|
||||
public static final String COLOR_NAME_GRAY = "gray";
|
||||
public static final String COLOR_NAME_DARK_GRAY = "dark-gray";
|
||||
public static final String COLOR_NAME_GREY = "grey";
|
||||
public static final String COLOR_NAME_DARK_GREY = "dark-grey";
|
||||
public static final String COLOR_NAME_WIDGET_DARK_SHADOW = "widget-dark-shadow";
|
||||
public static final String COLOR_NAME_WIDGET_NORMAL_SHADOW = "widget-normal-shadow";
|
||||
public static final String COLOR_NAME_WIDGET_LIGHT_SHADOW = "widget-light-shadow";
|
||||
public static final String COLOR_NAME_WIDGET_HIGHLIGHT_SHADOW = "widget-highlight-shadow";
|
||||
public static final String COLOR_NAME_WIDGET_FOREGROUND = "widget-foreground";
|
||||
public static final String COLOR_NAME_WIDGET_BACKGROUND = "widget-background";
|
||||
public static final String COLOR_NAME_WIDGET_BORDER = "widget-border";
|
||||
public static final String COLOR_NAME_LIST_FOREGROUND = "list-foreground";
|
||||
public static final String COLOR_NAME_LIST_BACKGROUND = "list-background";
|
||||
public static final String COLOR_NAME_LIST_SELECTION = "list-selection";
|
||||
public static final String COLOR_NAME_LIST_SELECTION_TEXT = "list-selection-text";
|
||||
public static final String COLOR_NAME_INFO_FOREGROUND = "info-foreground";
|
||||
public static final String COLOR_NAME_INFO_BACKGROUND = "info-background";
|
||||
public static final String COLOR_NAME_TITLE_FOREGROUND = "title-foreground";
|
||||
public static final String COLOR_NAME_TITLE_BACKGROUND = "title-background";
|
||||
public static final String COLOR_NAME_TITLE_BACKGROUND_GRADIENT = "title-background-gradient";
|
||||
public static final String COLOR_NAME_TITLE_INACTIVE_FOREGROUND = "title-inactive-foreground";
|
||||
public static final String COLOR_NAME_TITLE_INACTIVE_BACKGROUND = "title-inactive-background";
|
||||
public static final String COLOR_NAME_TITLE_INACTIVE_BACKGROUND_GRADIENT = "title-inactive-background-gradient";
|
||||
|
||||
private static final IntObjectHashMap colorNameByNumberMap = new IntObjectHashMap(40);
|
||||
private static final ObjectIntHashMap colorNumberByNameMap = new ObjectIntHashMap(40);
|
||||
private static final int[] colorConstantNumbers = new int[] {
|
||||
COLOR_WHITE,
|
||||
COLOR_BLACK,
|
||||
COLOR_RED,
|
||||
COLOR_DARK_RED,
|
||||
COLOR_GREEN,
|
||||
COLOR_DARK_GREEN,
|
||||
COLOR_YELLOW,
|
||||
COLOR_DARK_YELLOW,
|
||||
COLOR_BLUE,
|
||||
COLOR_DARK_BLUE,
|
||||
COLOR_MAGENTA,
|
||||
COLOR_DARK_MAGENTA,
|
||||
COLOR_CYAN,
|
||||
COLOR_DARK_CYAN,
|
||||
COLOR_GRAY,
|
||||
COLOR_DARK_GRAY,
|
||||
COLOR_GREY,
|
||||
COLOR_DARK_GREY,
|
||||
COLOR_WIDGET_DARK_SHADOW,
|
||||
COLOR_WIDGET_NORMAL_SHADOW,
|
||||
COLOR_WIDGET_LIGHT_SHADOW,
|
||||
COLOR_WIDGET_HIGHLIGHT_SHADOW,
|
||||
COLOR_WIDGET_FOREGROUND,
|
||||
COLOR_WIDGET_BACKGROUND,
|
||||
COLOR_WIDGET_BORDER,
|
||||
COLOR_LIST_FOREGROUND,
|
||||
COLOR_LIST_BACKGROUND,
|
||||
COLOR_LIST_SELECTION,
|
||||
COLOR_LIST_SELECTION_TEXT,
|
||||
COLOR_INFO_FOREGROUND,
|
||||
COLOR_INFO_BACKGROUND,
|
||||
COLOR_TITLE_FOREGROUND,
|
||||
COLOR_TITLE_BACKGROUND,
|
||||
COLOR_TITLE_BACKGROUND_GRADIENT,
|
||||
COLOR_TITLE_INACTIVE_FOREGROUND,
|
||||
COLOR_TITLE_INACTIVE_BACKGROUND,
|
||||
COLOR_TITLE_INACTIVE_BACKGROUND_GRADIENT
|
||||
};
|
||||
private static final String[] colorConstantNames = new String[] {
|
||||
COLOR_NAME_WHITE,
|
||||
COLOR_NAME_BLACK,
|
||||
COLOR_NAME_RED,
|
||||
COLOR_NAME_DARK_RED,
|
||||
COLOR_NAME_GREEN,
|
||||
COLOR_NAME_DARK_GREEN,
|
||||
COLOR_NAME_YELLOW,
|
||||
COLOR_NAME_DARK_YELLOW,
|
||||
COLOR_NAME_BLUE,
|
||||
COLOR_NAME_DARK_BLUE,
|
||||
COLOR_NAME_MAGENTA,
|
||||
COLOR_NAME_DARK_MAGENTA,
|
||||
COLOR_NAME_CYAN,
|
||||
COLOR_NAME_DARK_CYAN,
|
||||
COLOR_NAME_GRAY,
|
||||
COLOR_NAME_DARK_GRAY,
|
||||
COLOR_NAME_GREY,
|
||||
COLOR_NAME_DARK_GREY,
|
||||
COLOR_NAME_WIDGET_DARK_SHADOW,
|
||||
COLOR_NAME_WIDGET_NORMAL_SHADOW,
|
||||
COLOR_NAME_WIDGET_LIGHT_SHADOW,
|
||||
COLOR_NAME_WIDGET_HIGHLIGHT_SHADOW,
|
||||
COLOR_NAME_WIDGET_FOREGROUND,
|
||||
COLOR_NAME_WIDGET_BACKGROUND,
|
||||
COLOR_NAME_WIDGET_BORDER,
|
||||
COLOR_NAME_LIST_FOREGROUND,
|
||||
COLOR_NAME_LIST_BACKGROUND,
|
||||
COLOR_NAME_LIST_SELECTION,
|
||||
COLOR_NAME_LIST_SELECTION_TEXT,
|
||||
COLOR_NAME_INFO_FOREGROUND,
|
||||
COLOR_NAME_INFO_BACKGROUND,
|
||||
COLOR_NAME_TITLE_FOREGROUND,
|
||||
COLOR_NAME_TITLE_BACKGROUND,
|
||||
COLOR_NAME_TITLE_BACKGROUND_GRADIENT,
|
||||
COLOR_NAME_TITLE_INACTIVE_FOREGROUND,
|
||||
COLOR_NAME_TITLE_INACTIVE_BACKGROUND,
|
||||
COLOR_NAME_TITLE_INACTIVE_BACKGROUND_GRADIENT
|
||||
};
|
||||
|
||||
static {
|
||||
colorNumberByNameMap.put(COLOR_NAME_WHITE, COLOR_WHITE);
|
||||
colorNumberByNameMap.put(COLOR_NAME_BLACK, COLOR_BLACK);
|
||||
colorNumberByNameMap.put(COLOR_NAME_RED, COLOR_RED);
|
||||
colorNumberByNameMap.put(COLOR_NAME_DARK_RED, COLOR_DARK_RED);
|
||||
colorNumberByNameMap.put(COLOR_NAME_GREEN, COLOR_GREEN);
|
||||
colorNumberByNameMap.put(COLOR_NAME_DARK_GREEN, COLOR_DARK_GREEN);
|
||||
colorNumberByNameMap.put(COLOR_NAME_YELLOW, COLOR_YELLOW);
|
||||
colorNumberByNameMap.put(COLOR_NAME_DARK_YELLOW, COLOR_DARK_YELLOW);
|
||||
colorNumberByNameMap.put(COLOR_NAME_BLUE, COLOR_BLUE);
|
||||
colorNumberByNameMap.put(COLOR_NAME_DARK_BLUE, COLOR_DARK_BLUE);
|
||||
colorNumberByNameMap.put(COLOR_NAME_MAGENTA, COLOR_MAGENTA);
|
||||
colorNumberByNameMap.put(COLOR_NAME_DARK_MAGENTA, COLOR_DARK_MAGENTA);
|
||||
colorNumberByNameMap.put(COLOR_NAME_CYAN, COLOR_CYAN);
|
||||
colorNumberByNameMap.put(COLOR_NAME_DARK_CYAN, COLOR_DARK_CYAN);
|
||||
colorNumberByNameMap.put(COLOR_NAME_GRAY, COLOR_GRAY);
|
||||
colorNumberByNameMap.put(COLOR_NAME_DARK_GRAY, COLOR_DARK_GRAY);
|
||||
colorNumberByNameMap.put(COLOR_NAME_GREY, COLOR_GREY);
|
||||
colorNumberByNameMap.put(COLOR_NAME_DARK_GREY, COLOR_DARK_GREY);
|
||||
colorNumberByNameMap.put(COLOR_NAME_WIDGET_DARK_SHADOW, COLOR_WIDGET_DARK_SHADOW);
|
||||
colorNumberByNameMap.put(COLOR_NAME_WIDGET_NORMAL_SHADOW, COLOR_WIDGET_NORMAL_SHADOW);
|
||||
colorNumberByNameMap.put(COLOR_NAME_WIDGET_LIGHT_SHADOW, COLOR_WIDGET_LIGHT_SHADOW);
|
||||
colorNumberByNameMap.put(COLOR_NAME_WIDGET_HIGHLIGHT_SHADOW, COLOR_WIDGET_HIGHLIGHT_SHADOW);
|
||||
colorNumberByNameMap.put(COLOR_NAME_WIDGET_FOREGROUND, COLOR_WIDGET_FOREGROUND);
|
||||
colorNumberByNameMap.put(COLOR_NAME_WIDGET_BACKGROUND, COLOR_WIDGET_BACKGROUND);
|
||||
colorNumberByNameMap.put(COLOR_NAME_WIDGET_BORDER, COLOR_WIDGET_BORDER);
|
||||
colorNumberByNameMap.put(COLOR_NAME_LIST_FOREGROUND, COLOR_LIST_FOREGROUND);
|
||||
colorNumberByNameMap.put(COLOR_NAME_LIST_BACKGROUND, COLOR_LIST_BACKGROUND);
|
||||
colorNumberByNameMap.put(COLOR_NAME_LIST_SELECTION, COLOR_LIST_SELECTION);
|
||||
colorNumberByNameMap.put(COLOR_NAME_LIST_SELECTION_TEXT, COLOR_LIST_SELECTION_TEXT);
|
||||
colorNumberByNameMap.put(COLOR_NAME_INFO_FOREGROUND, COLOR_INFO_FOREGROUND);
|
||||
colorNumberByNameMap.put(COLOR_NAME_INFO_BACKGROUND, COLOR_INFO_BACKGROUND);
|
||||
colorNumberByNameMap.put(COLOR_NAME_TITLE_FOREGROUND, COLOR_TITLE_FOREGROUND);
|
||||
colorNumberByNameMap.put(COLOR_NAME_TITLE_BACKGROUND, COLOR_TITLE_BACKGROUND);
|
||||
colorNumberByNameMap.put(COLOR_NAME_TITLE_BACKGROUND_GRADIENT, COLOR_TITLE_BACKGROUND_GRADIENT);
|
||||
colorNumberByNameMap.put(COLOR_NAME_TITLE_INACTIVE_FOREGROUND, COLOR_TITLE_INACTIVE_FOREGROUND);
|
||||
colorNumberByNameMap.put(COLOR_NAME_TITLE_INACTIVE_BACKGROUND, COLOR_TITLE_INACTIVE_BACKGROUND);
|
||||
colorNumberByNameMap.put(COLOR_NAME_TITLE_INACTIVE_BACKGROUND_GRADIENT, COLOR_TITLE_INACTIVE_BACKGROUND_GRADIENT);
|
||||
|
||||
colorNameByNumberMap.put(COLOR_WHITE, COLOR_NAME_WHITE);
|
||||
colorNameByNumberMap.put(COLOR_BLACK, COLOR_NAME_BLACK);
|
||||
colorNameByNumberMap.put(COLOR_RED, COLOR_NAME_RED);
|
||||
colorNameByNumberMap.put(COLOR_DARK_RED, COLOR_NAME_DARK_RED);
|
||||
colorNameByNumberMap.put(COLOR_GREEN, COLOR_NAME_GREEN);
|
||||
colorNameByNumberMap.put(COLOR_DARK_GREEN, COLOR_NAME_DARK_GREEN);
|
||||
colorNameByNumberMap.put(COLOR_YELLOW, COLOR_NAME_YELLOW);
|
||||
colorNameByNumberMap.put(COLOR_DARK_YELLOW, COLOR_NAME_DARK_YELLOW);
|
||||
colorNameByNumberMap.put(COLOR_BLUE, COLOR_NAME_BLUE);
|
||||
colorNameByNumberMap.put(COLOR_DARK_BLUE, COLOR_NAME_DARK_BLUE);
|
||||
colorNameByNumberMap.put(COLOR_MAGENTA, COLOR_NAME_MAGENTA);
|
||||
colorNameByNumberMap.put(COLOR_DARK_MAGENTA, COLOR_NAME_DARK_MAGENTA);
|
||||
colorNameByNumberMap.put(COLOR_CYAN, COLOR_NAME_CYAN);
|
||||
colorNameByNumberMap.put(COLOR_DARK_CYAN, COLOR_NAME_DARK_CYAN);
|
||||
colorNameByNumberMap.put(COLOR_GRAY, COLOR_NAME_GRAY);
|
||||
colorNameByNumberMap.put(COLOR_DARK_GRAY, COLOR_NAME_DARK_GRAY);
|
||||
colorNameByNumberMap.put(COLOR_GREY, COLOR_NAME_GREY);
|
||||
colorNameByNumberMap.put(COLOR_DARK_GREY, COLOR_NAME_DARK_GREY);
|
||||
colorNameByNumberMap.put(COLOR_WIDGET_DARK_SHADOW, COLOR_NAME_WIDGET_DARK_SHADOW);
|
||||
colorNameByNumberMap.put(COLOR_WIDGET_NORMAL_SHADOW, COLOR_NAME_WIDGET_NORMAL_SHADOW);
|
||||
colorNameByNumberMap.put(COLOR_WIDGET_LIGHT_SHADOW, COLOR_NAME_WIDGET_LIGHT_SHADOW);
|
||||
colorNameByNumberMap.put(COLOR_WIDGET_HIGHLIGHT_SHADOW, COLOR_NAME_WIDGET_HIGHLIGHT_SHADOW);
|
||||
colorNameByNumberMap.put(COLOR_WIDGET_FOREGROUND, COLOR_NAME_WIDGET_FOREGROUND);
|
||||
colorNameByNumberMap.put(COLOR_WIDGET_BACKGROUND, COLOR_NAME_WIDGET_BACKGROUND);
|
||||
colorNameByNumberMap.put(COLOR_WIDGET_BORDER, COLOR_NAME_WIDGET_BORDER);
|
||||
colorNameByNumberMap.put(COLOR_LIST_FOREGROUND, COLOR_NAME_LIST_FOREGROUND);
|
||||
colorNameByNumberMap.put(COLOR_LIST_BACKGROUND, COLOR_NAME_LIST_BACKGROUND);
|
||||
colorNameByNumberMap.put(COLOR_LIST_SELECTION, COLOR_NAME_LIST_SELECTION);
|
||||
colorNameByNumberMap.put(COLOR_LIST_SELECTION_TEXT, COLOR_NAME_LIST_SELECTION_TEXT);
|
||||
colorNameByNumberMap.put(COLOR_INFO_FOREGROUND, COLOR_NAME_INFO_FOREGROUND);
|
||||
colorNameByNumberMap.put(COLOR_INFO_BACKGROUND, COLOR_NAME_INFO_BACKGROUND);
|
||||
colorNameByNumberMap.put(COLOR_TITLE_FOREGROUND, COLOR_NAME_TITLE_FOREGROUND);
|
||||
colorNameByNumberMap.put(COLOR_TITLE_BACKGROUND, COLOR_NAME_TITLE_BACKGROUND);
|
||||
colorNameByNumberMap.put(COLOR_TITLE_BACKGROUND_GRADIENT, COLOR_NAME_TITLE_BACKGROUND_GRADIENT);
|
||||
colorNameByNumberMap.put(COLOR_TITLE_INACTIVE_FOREGROUND, COLOR_NAME_TITLE_INACTIVE_FOREGROUND);
|
||||
colorNameByNumberMap.put(COLOR_TITLE_INACTIVE_BACKGROUND, COLOR_NAME_TITLE_INACTIVE_BACKGROUND);
|
||||
colorNameByNumberMap.put(COLOR_TITLE_INACTIVE_BACKGROUND_GRADIENT, COLOR_NAME_TITLE_INACTIVE_BACKGROUND_GRADIENT);
|
||||
}//static//
|
||||
|
||||
private byte red = 0;
|
||||
private byte green = 0;
|
||||
private byte blue = 0;
|
||||
private byte alpha = (byte) 255;
|
||||
/** One of the system defined colors. The RGB value will not be valid if this is non-zero. */
|
||||
private int colorConstant = -1;
|
||||
/**
|
||||
* JefColor default constructor.
|
||||
* <p>Note: This constructor is only public such that the class can easily be serialized.</p>
|
||||
*/
|
||||
public JefColor() {
|
||||
super();
|
||||
}//JefColor()//
|
||||
/**
|
||||
* JefColor constructor.
|
||||
* <p>TODO: Provide a way to pass a color constant as well as a default color if the constant is not available.</p>
|
||||
* @param colorText The text containing color data in the form of: "constant-name", #RGB, #RGBA, #RRGGBB, #RRGGBBAA, (r,g,b), or (r,g,b,a) where r/g/b/a can be on the range of [0..255] or [0.0..1.0], and R/G/B/A are hex digits.
|
||||
*/
|
||||
public JefColor(String colorText) {
|
||||
super();
|
||||
|
||||
if(colorText != null) {
|
||||
parseString(colorText);
|
||||
}//if//
|
||||
}//JefColor()//
|
||||
/**
|
||||
* JefColor constructor.
|
||||
* @param red The red part of the color. This must be a value between 0..255.
|
||||
* @param green The green part of the color. This must be a value between 0..255.
|
||||
* @param blue The blue part of the color. This must be a value between 0..255.
|
||||
*/
|
||||
public JefColor(int red, int green, int blue) {
|
||||
this(red, green, blue, 255);
|
||||
}//JefColor()//
|
||||
/**
|
||||
* JefColor constructor.
|
||||
* @param red The red part of the color. This must be a value between 0..255.
|
||||
* @param green The green part of the color. This must be a value between 0..255.
|
||||
* @param blue The blue part of the color. This must be a value between 0..255.
|
||||
* @param alpha The how transparent the color is. A value of 255 is fully opaque and a value or zero is fully transparent.
|
||||
*/
|
||||
public JefColor(int red, int green, int blue, int alpha) {
|
||||
super();
|
||||
this.red = (byte) (red & 0xFF);
|
||||
this.green = (byte) (green & 0xFF);
|
||||
this.blue = (byte) (blue & 0xFF);
|
||||
this.alpha = (byte) (alpha & 0xFF);
|
||||
}//JefColor()//
|
||||
/**
|
||||
* JefColor constructor.
|
||||
* @param colorConstant One of the constants defined by this class.
|
||||
*/
|
||||
public JefColor(int colorConstant) {
|
||||
this(colorConstant, 255);
|
||||
}//JefColor()//
|
||||
/**
|
||||
* JefColor constructor.
|
||||
* @param colorConstant One of the constants defined by this class.
|
||||
* @param alpha The how transparent the color is. A value of 255 is fully opaque and a value or zero is fully transparent.
|
||||
*/
|
||||
public JefColor(int colorConstant, int alpha) {
|
||||
this.colorConstant = colorConstant;
|
||||
this.alpha = (byte) (alpha & 0xFF);
|
||||
}//JefColor()//
|
||||
/**
|
||||
* Parses the color text and saves the color values.
|
||||
* @param colorText The text containing color data in the form of: "constant-name", #RGB, #RGBA, #RRGGBB, #RRGGBBAA, (r,g,b), or (r,g,b,a) where r/g/b/a can be on the range of [0..255] or [0.0..1.0], and R/G/B/A are hex digits.
|
||||
*/
|
||||
private void parseString(String colorText) {
|
||||
if(colorText != null) {
|
||||
colorText = colorText.trim();
|
||||
|
||||
//Handle '#FFFFFFFF', '(255, 255, 255, 0)', and 'button-background 120' color texts.//
|
||||
if(colorText.charAt(0) == '#') {
|
||||
readHexColors(colorText);
|
||||
}//if//
|
||||
else if((colorText.charAt(0) == '(') && (colorText.charAt(colorText.length() - 1) == ')')) {
|
||||
readParameratizedColors(colorText);
|
||||
}//else if//
|
||||
else {
|
||||
int spaceIndex = colorText.indexOf(' ');
|
||||
|
||||
//Check to see if the string provides default values in the event that the constant doesn't match to a color for this platform.//
|
||||
if(spaceIndex != -1) {
|
||||
String defaultColorText = colorText.substring(spaceIndex).trim();
|
||||
|
||||
//Remove the defaults from the colored text string.//
|
||||
colorText = colorText.substring(0, spaceIndex);
|
||||
|
||||
//Process the default values.//
|
||||
if(defaultColorText.charAt(0) == '#') {
|
||||
readHexColors(defaultColorText);
|
||||
}//if//
|
||||
else if((defaultColorText.charAt(0) == '(') && (defaultColorText.charAt(defaultColorText.length() - 1) == ')')) {
|
||||
readParameratizedColors(defaultColorText);
|
||||
}//else if//
|
||||
else {
|
||||
int alpha = 0;
|
||||
|
||||
if(defaultColorText.indexOf('.') != -1) {
|
||||
alpha = Math.round(Float.parseFloat(defaultColorText) * 255);
|
||||
}//if//
|
||||
else {
|
||||
alpha = Integer.parseInt(defaultColorText);
|
||||
}//else//
|
||||
|
||||
if(alpha > 255) {
|
||||
alpha = 255;
|
||||
}//if//
|
||||
else if(alpha < 0) {
|
||||
alpha = 0;
|
||||
}//else if//
|
||||
|
||||
this.alpha = (byte) alpha;
|
||||
}//else//
|
||||
}//if//
|
||||
|
||||
//Lookup the color constant value.//
|
||||
if(colorNumberByNameMap.containsKey(colorText)) {
|
||||
this.colorConstant = colorNumberByNameMap.get(colorText);
|
||||
}//if//
|
||||
else {
|
||||
//Perform a case insensitive search.//
|
||||
for(int index = 0; index < colorConstantNames.length; index++) {
|
||||
if(colorConstantNames[index].equalsIgnoreCase(colorText)) {
|
||||
this.colorConstant = colorConstantNumbers[index];
|
||||
break;
|
||||
}//if//
|
||||
}//for//
|
||||
}//else//
|
||||
}//else//
|
||||
}//if//
|
||||
}//parseString()//
|
||||
/**
|
||||
* Reads the color values from a parameter string where the rgba values are enclosed in parenthesis and separated by commas. The values may be expressed as either floats or integers, and not all values must be present.
|
||||
* @param colorText The text which must start with a '(' and should be followed by the red, green, blue, and alpha values as floats or integers separated by commas, followed by an ending ')'.
|
||||
*/
|
||||
private void readParameratizedColors(String colorText) {
|
||||
String[] values = StringSupport.split(colorText.substring(1, colorText.length() - 1), ',');
|
||||
int red = 0;
|
||||
int green = 0;
|
||||
int blue = 0;
|
||||
int alpha = 0;
|
||||
boolean useFloats = false;
|
||||
|
||||
//Determine whether the values are specified using floating points, otherwise they will be specified using 0..255.//
|
||||
for(int index = 0; (!useFloats) && (index < values.length) && (index < 4); index++) {
|
||||
useFloats = values[index].indexOf('.') != -1;
|
||||
}//for//
|
||||
|
||||
if(values.length > 0) {
|
||||
red = readColorNumber(values[0], useFloats);
|
||||
}//if//
|
||||
|
||||
if(values.length > 1) {
|
||||
green = readColorNumber(values[1], useFloats);
|
||||
}//if//
|
||||
|
||||
if(values.length > 2) {
|
||||
blue = readColorNumber(values[2], useFloats);
|
||||
}//if//
|
||||
|
||||
if(values.length > 3) {
|
||||
alpha = readColorNumber(values[3], useFloats);
|
||||
}//if//
|
||||
|
||||
this.red = (byte) red;
|
||||
this.green = (byte) green;
|
||||
this.blue = (byte) blue;
|
||||
this.alpha = (byte) alpha;
|
||||
}//readParameratizedColors()//
|
||||
/**
|
||||
* Reads the color values from a hexadecimal string beinning with a pound sign (#).
|
||||
* @param colorText The text which must start with a # and should be followed by 3, 4, 6, or 8 hex characters [0..9|A..F].
|
||||
*/
|
||||
private void readHexColors(String colorText) {
|
||||
int red = 0;
|
||||
int green = 0;
|
||||
int blue = 0;
|
||||
int alpha = 0;
|
||||
|
||||
if(colorText.length() < 6) {
|
||||
if(colorText.length() > 1) {
|
||||
red = Integer.parseInt(colorText.substring(1, 2) + 'F', 16);
|
||||
}//if//
|
||||
|
||||
if(colorText.length() > 2) {
|
||||
green = Integer.parseInt(colorText.substring(2, 3) + 'F', 16);
|
||||
}//if//
|
||||
|
||||
if(colorText.length() > 3) {
|
||||
blue = Integer.parseInt(colorText.substring(3, 4) + 'F', 16);
|
||||
}//if//
|
||||
|
||||
if(colorText.length() > 4) {
|
||||
alpha = Integer.parseInt(colorText.substring(4, 5) + 'F', 16);
|
||||
}//if//
|
||||
}//if//
|
||||
else {
|
||||
if(colorText.length() > 2) {
|
||||
red = Integer.parseInt(colorText.substring(1, 3), 16);
|
||||
}//if//
|
||||
|
||||
if(colorText.length() > 4) {
|
||||
green = Integer.parseInt(colorText.substring(3, 5), 16);
|
||||
}//if//
|
||||
|
||||
if(colorText.length() > 6) {
|
||||
blue = Integer.parseInt(colorText.substring(5, 7), 16);
|
||||
}//if//
|
||||
|
||||
if(colorText.length() > 8) {
|
||||
alpha = Integer.parseInt(colorText.substring(7, 9), 16);
|
||||
}//if//
|
||||
}//else//
|
||||
|
||||
this.red = (byte) red;
|
||||
this.green = (byte) green;
|
||||
this.blue = (byte) blue;
|
||||
this.alpha = (byte) alpha;
|
||||
}//readHexColors()//
|
||||
/**
|
||||
* Reads a color numeric value from a string containing an integer on the range of [0..255] or a float on the range of [0..1].
|
||||
* <p>This method does not result in any errors, but will choose a color closest to the provided data if there is a problem.</p>
|
||||
* @param number The number as text.
|
||||
* @param useFloats Whether the number should be read as a float.
|
||||
* @return The color value on the range of [0..255].
|
||||
*/
|
||||
private int readColorNumber(String number, boolean useFloats) {
|
||||
int result = 0;
|
||||
|
||||
if(useFloats) {
|
||||
float value = Float.parseFloat(number);
|
||||
|
||||
result = Math.round(255 * value);
|
||||
}//if//
|
||||
else {
|
||||
result = Integer.parseInt(number);
|
||||
}//else//
|
||||
|
||||
if(result > 255) {
|
||||
result = 255;
|
||||
}//if//
|
||||
else if(result < 0) {
|
||||
result = 0;
|
||||
}//else if//
|
||||
|
||||
return result;
|
||||
}//readColorNumber()//
|
||||
/**
|
||||
* Determines whether the color is represented by a system defined constant.
|
||||
* @return Whether the color constant must be used to get a red/green/blue value set.
|
||||
*/
|
||||
public boolean isColorConstant() {
|
||||
return colorConstant != -1;
|
||||
}//isConstant()//
|
||||
/**
|
||||
* Gets the red color value.
|
||||
* @return The red part of the color.
|
||||
*/
|
||||
public int getRed() {
|
||||
return red & 0xFF;
|
||||
}//getRed()//
|
||||
/**
|
||||
* Gets the green color value.
|
||||
* @return The green part of the color.
|
||||
*/
|
||||
public int getGreen() {
|
||||
return green & 0xFF;
|
||||
}//getRed()//
|
||||
/**
|
||||
* Gets the blue color value.
|
||||
* @return The blue part of the color.
|
||||
*/
|
||||
public int getBlue() {
|
||||
return blue & 0xFF;
|
||||
}//getRed()//
|
||||
/**
|
||||
* Gets the alpha color value.
|
||||
* @return The alpha part of the color.
|
||||
*/
|
||||
public int getAlpha() {
|
||||
return alpha & 0xFF;
|
||||
}//getAlpha()//
|
||||
/**
|
||||
* Gets the color constant requested.
|
||||
* @return The color constant, or zero if no color constant is requested.
|
||||
*/
|
||||
public int getColorConstant() {
|
||||
return colorConstant;
|
||||
}//getColorConstant()//
|
||||
/* (non-Javadoc)
|
||||
* @see java.io.Externalizable#readExternal(java.io.ObjectInput)
|
||||
*/
|
||||
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
|
||||
in.readByte();
|
||||
red = in.readByte();
|
||||
green = in.readByte();
|
||||
blue = in.readByte();
|
||||
alpha = in.readByte();
|
||||
colorConstant = in.readInt();
|
||||
}//readExternal()//
|
||||
/* (non-Javadoc)
|
||||
* @see java.io.Externalizable#writeExternal(java.io.ObjectOutput)
|
||||
*/
|
||||
public void writeExternal(ObjectOutput out) throws IOException {
|
||||
out.writeByte(0);
|
||||
out.writeByte(red);
|
||||
out.writeByte(green);
|
||||
out.writeByte(blue);
|
||||
out.writeByte(alpha);
|
||||
out.writeInt(colorConstant);
|
||||
}//writeExternal()//
|
||||
/* (non-Javadoc)
|
||||
* @see java.lang.Object#clone()
|
||||
*/
|
||||
public Object clone() throws CloneNotSupportedException {
|
||||
JefColor clone = (JefColor) super.clone();
|
||||
|
||||
clone.red = red;
|
||||
clone.blue = blue;
|
||||
clone.green = green;
|
||||
clone.alpha = alpha;
|
||||
clone.colorConstant = colorConstant;
|
||||
|
||||
return clone;
|
||||
}//clone()//
|
||||
/* (non-Javadoc)
|
||||
* @see java.lang.Object#toString()
|
||||
*/
|
||||
public String toString() {
|
||||
return isColorConstant() ? colorConstantNames[getColorConstant()] : ("(" + (red & 0xFF) + ',' + (green & 0xFF) + ',' + (blue & 0xFF) + ',' + (alpha & 0xFF) + ')');
|
||||
}//toString()//
|
||||
/* (non-Javadoc)
|
||||
* @see java.lang.Object#equals(java.lang.Object)
|
||||
*/
|
||||
public boolean equals(Object object) {
|
||||
return (object instanceof JefColor) && (colorConstant != -1 ? colorConstant == ((JefColor) object).colorConstant : (red == ((JefColor) object).red) && (green == ((JefColor) object).green) && (blue == ((JefColor) object).blue) && (alpha == ((JefColor) object).alpha));
|
||||
}//equals()//
|
||||
/* (non-Javadoc)
|
||||
* @see java.lang.Object#hashCode()
|
||||
*/
|
||||
public int hashCode() {
|
||||
return colorConstant != -1 ? colorConstant : ((int) red) << 24 | ((int) green) << 16 |((int) blue) << 8 | ((int) alpha);
|
||||
}//hashCode()//
|
||||
}//JefColor//
|
||||
313
Foundation/src/com/foundation/view/JefFont.java
Normal file
313
Foundation/src/com/foundation/view/JefFont.java
Normal file
@@ -0,0 +1,313 @@
|
||||
/*
|
||||
* Copyright (c) 2004,2009 Declarative Engineering LLC.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Declarative Engineering LLC
|
||||
* verson 1 which accompanies this distribution, and is available at
|
||||
* http://declarativeengineering.com/legal/DE_Developer_License_v1.txt
|
||||
*/
|
||||
package com.foundation.view;
|
||||
|
||||
import java.io.Externalizable;
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectInput;
|
||||
import java.io.ObjectOutput;
|
||||
|
||||
import com.common.comparison.Comparator;
|
||||
import com.common.util.StringSupport;
|
||||
|
||||
public class JefFont implements Externalizable, Cloneable {
|
||||
public static final int STYLE_NORMAL = 0x0000;
|
||||
public static final int STYLE_BOLD = 0x0001;
|
||||
public static final int STYLE_ITALIC = 0x0002;
|
||||
public static final int STYLE_STRIKE_THROUGH = 0x0004;
|
||||
|
||||
private String name = null;
|
||||
private int height = 12; //TODO: Shouldn't this be a float?//
|
||||
private int style = STYLE_NORMAL;
|
||||
private String locale = null;
|
||||
/**
|
||||
* JefFont constructor.
|
||||
* <p>Note: This constructor is only public such that the class can easily be serialized.</p>
|
||||
*/
|
||||
public JefFont() {
|
||||
super();
|
||||
}//JefFont()//
|
||||
/**
|
||||
* JefFont constructor. Creates the system font.
|
||||
* @param height The font height.
|
||||
* @param style The font style (STYLE_BOLD | STYLE_NORMAL | STYLE_ITALIC).
|
||||
* @param locale Future use only.
|
||||
*/
|
||||
public JefFont(int height, int style, String locale) {
|
||||
super();
|
||||
|
||||
this.name = "";
|
||||
this.height = height;
|
||||
this.style = style;
|
||||
this.locale = locale;
|
||||
}//JefFont()//
|
||||
/**
|
||||
* JefFont constructor.
|
||||
* @param name The font name (TODO: Describe format!).
|
||||
* @param height The font height.
|
||||
* @param style The font style (STYLE_BOLD | STYLE_NORMAL | STYLE_ITALIC).
|
||||
* @param locale Future use only.
|
||||
*/
|
||||
public JefFont(String name, int height, int style, String locale) {
|
||||
super();
|
||||
|
||||
this.name = name;
|
||||
this.height = height;
|
||||
this.style = style;
|
||||
this.locale = locale;
|
||||
}//JefFont()//
|
||||
/**
|
||||
* Gets the font name or family name.
|
||||
* @return The font or font family name.
|
||||
*/
|
||||
public String getName() {
|
||||
return name;
|
||||
}//getName()//
|
||||
/**
|
||||
* Gets the font height.
|
||||
* @return The height in points requested for the font.
|
||||
*/
|
||||
public int getHeight() {
|
||||
return height;
|
||||
}//getHeight()//
|
||||
/**
|
||||
* Gets the styles for the font.
|
||||
* @return The set of styles requested for the font.
|
||||
*/
|
||||
public int getStyle() {
|
||||
return style;
|
||||
}//getStyle()//
|
||||
/**
|
||||
* Gets the font locale.
|
||||
* @return The locale requested for the font.
|
||||
*/
|
||||
public String getLocale() {
|
||||
return locale;
|
||||
}//getLocale()//
|
||||
//TODO: add a method that takes a JefFont and returns a string.//
|
||||
/* (non-Javadoc)
|
||||
* @see java.io.Externalizable#readExternal(java.io.ObjectInput)
|
||||
*/
|
||||
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
|
||||
in.readByte();
|
||||
name = (String) in.readObject();
|
||||
height = in.readInt();
|
||||
style = in.readInt();
|
||||
locale = (String) in.readObject();
|
||||
}//readExternal()//
|
||||
/* (non-Javadoc)
|
||||
* @see java.io.Externalizable#writeExternal(java.io.ObjectOutput)
|
||||
*/
|
||||
public void writeExternal(ObjectOutput out) throws IOException {
|
||||
out.writeByte(0);
|
||||
out.writeObject(name);
|
||||
out.writeInt(height);
|
||||
out.writeInt(style);
|
||||
out.writeObject(locale);
|
||||
}//writeExternal()//
|
||||
/* (non-Javadoc)
|
||||
* @see java.lang.Object#clone()
|
||||
*/
|
||||
public Object clone() throws CloneNotSupportedException {
|
||||
JefFont clone = (JefFont) super.clone();
|
||||
|
||||
clone.name = name;
|
||||
clone.height = height;
|
||||
clone.style = style;
|
||||
clone.locale = locale;
|
||||
|
||||
return clone;
|
||||
}//clone()//
|
||||
/**
|
||||
* Gets an array of JefFonts for the SWT font for the given device.
|
||||
* @param device The SWT device whose font is required.
|
||||
* @param fontString The string describing the font desired.
|
||||
* @return The best matching font for the device.
|
||||
*/
|
||||
public static JefFont[] getJefFonts(String fontString) {
|
||||
JefFont[] jefFonts = null;
|
||||
|
||||
if(fontString != null) {
|
||||
String[] elements = null;
|
||||
String[] names = null;
|
||||
int[] heights = null;
|
||||
int[] styles = null;
|
||||
|
||||
elements = StringSupport.split(fontString, '|');
|
||||
|
||||
if(elements.length > 0) {
|
||||
int firstStyle = 0;
|
||||
String firstName = "";
|
||||
int firstHeight = 12;
|
||||
boolean firstStyleSet = false;
|
||||
boolean firstHeightSet = false;
|
||||
|
||||
names = new String[elements.length];
|
||||
heights = new int[elements.length];
|
||||
styles = new int[elements.length];
|
||||
|
||||
for(int x = 0; x < elements.length; x++) {
|
||||
String[] familyData = StringSupport.split(elements[x], ',');
|
||||
|
||||
if(familyData.length > 0) {
|
||||
int currentStyle = 0;
|
||||
String currentName = null;
|
||||
int currentHeight = -1;
|
||||
boolean styleSet = false;
|
||||
|
||||
for(int y = 0; y < familyData.length; y++) {
|
||||
String currentPart = familyData[y].trim();
|
||||
|
||||
if(currentPart.compareToIgnoreCase("normal") == 0) {
|
||||
currentStyle = currentStyle | STYLE_NORMAL;
|
||||
styleSet = true;
|
||||
}//if//
|
||||
else if(currentPart.compareToIgnoreCase("bold") == 0) {
|
||||
currentStyle = currentStyle | STYLE_BOLD;
|
||||
styleSet = true;
|
||||
}//else if//
|
||||
else if(currentPart.compareToIgnoreCase("italic") == 0) {
|
||||
currentStyle = currentStyle | STYLE_ITALIC;
|
||||
styleSet = true;
|
||||
}//else if//
|
||||
else {
|
||||
//It is either the height which would be an integer, or the font name which is never an integer.//
|
||||
try {
|
||||
currentHeight = Integer.parseInt(currentPart);
|
||||
}//try//
|
||||
catch(NumberFormatException e) {
|
||||
currentName = currentPart;
|
||||
}//catch//
|
||||
}//else//
|
||||
}//for//
|
||||
|
||||
if(styleSet) {
|
||||
styles[x] = currentStyle;
|
||||
|
||||
if(styleSet && (!firstStyleSet)) {
|
||||
firstStyle = currentStyle;
|
||||
firstStyleSet = true;
|
||||
}//if//
|
||||
}//if//
|
||||
else {
|
||||
styles[x] = 99;
|
||||
}//else//
|
||||
|
||||
if(currentName != null) {
|
||||
names[x] = currentName;
|
||||
|
||||
if(firstName == null) {
|
||||
firstName = currentName;
|
||||
}//if//
|
||||
}//if//
|
||||
if(currentHeight != -1) {
|
||||
heights[x] = currentHeight;
|
||||
|
||||
if(!firstHeightSet) {
|
||||
firstHeight = currentHeight;
|
||||
firstHeightSet = true;
|
||||
}//if//
|
||||
}//if//
|
||||
else {
|
||||
heights[x] = 0;
|
||||
}//else//
|
||||
}//if//
|
||||
}//for//
|
||||
|
||||
jefFonts = new JefFont[elements.length];
|
||||
|
||||
for(int p = 0; p < elements.length ; p++) {
|
||||
String name = names[p] == null ? firstName : names[p];
|
||||
int height = heights[p] == 0 ? firstHeight : heights[p];
|
||||
int style = styles[p] == 99 ? firstStyle : styles[p];
|
||||
|
||||
jefFonts[p] = new JefFont(name, height, style, null);
|
||||
}//for//
|
||||
}//if//
|
||||
}//if//
|
||||
|
||||
return jefFonts;
|
||||
}//getJefFonts()//
|
||||
/**
|
||||
* Provides a string representation of the JefFonts passed in.
|
||||
* @param jefFonts The JefFonts to add to the string.
|
||||
* @return A String of JefFont data that can be used to recreate the JefFonts.
|
||||
*/
|
||||
public static String getJefFontsString(JefFont[] jefFonts) {
|
||||
String result = null;
|
||||
StringBuffer buffer = null;
|
||||
JefFont current = null;
|
||||
|
||||
//TODO: When locale is enabled this method should be updated.
|
||||
if(jefFonts != null && jefFonts.length > 0) {
|
||||
buffer = new StringBuffer();
|
||||
boolean firstRun = true;
|
||||
int setStyle = 0;
|
||||
|
||||
for(int x = 0; x < jefFonts.length; x++) {
|
||||
current = jefFonts[x];
|
||||
|
||||
if(current != null) {
|
||||
if(!firstRun) {
|
||||
buffer.append(" | ");
|
||||
}//if//
|
||||
|
||||
buffer.append((current.name == null || current.name.length() == 0 ? "" : current.name + ", "));
|
||||
buffer.append(current.height + ", ");
|
||||
setStyle = current.style;
|
||||
|
||||
switch(setStyle) {
|
||||
case 0: {
|
||||
buffer.append("normal");
|
||||
break;
|
||||
}//case//
|
||||
case 1: {
|
||||
buffer.append("bold");
|
||||
break;
|
||||
}//case//
|
||||
case 2: {
|
||||
buffer.append("italic");
|
||||
break;
|
||||
}//case//
|
||||
case 3: {
|
||||
buffer.append("italic, bold");
|
||||
break;
|
||||
}//case//
|
||||
default: {
|
||||
buffer.append("normal");
|
||||
}//default//
|
||||
}//switch//
|
||||
|
||||
firstRun = false;
|
||||
}//if//
|
||||
}//for//
|
||||
|
||||
result = buffer.toString();
|
||||
}//if//
|
||||
|
||||
return result;
|
||||
}//getJefFontsAsString()//
|
||||
/* (non-Javadoc)
|
||||
* @see java.lang.Object#equals(java.lang.Object)
|
||||
*/
|
||||
public boolean equals(Object object) {
|
||||
return (object instanceof JefFont) && (style == ((JefFont) object).style) && (height == ((JefFont) object).height) && (Comparator.getStringComparator().compare(name, ((JefFont) object).name) == 0) && (Comparator.getStringComparator().compare(locale, ((JefFont) object).locale) == 0);
|
||||
}//equals()//
|
||||
/* (non-Javadoc)
|
||||
* @see java.lang.Object#hashCode()
|
||||
*/
|
||||
public int hashCode() {
|
||||
return style ^ (locale != null ? locale.hashCode() : 0) ^ (name != null ? name.hashCode() : 0) ^ height;
|
||||
}//hashCode()//
|
||||
/* (non-Javadoc)
|
||||
* @see java.lang.Object#toString()
|
||||
*/
|
||||
public String toString() {
|
||||
return "Name: " + name + " Style: " + style + " Height: " + height + " Locale: " + locale;
|
||||
}//toString()//
|
||||
}//JefFont//
|
||||
206
Foundation/src/com/foundation/view/JefGradient.java
Normal file
206
Foundation/src/com/foundation/view/JefGradient.java
Normal file
@@ -0,0 +1,206 @@
|
||||
/*
|
||||
* Copyright (c) 2007,2009 Declarative Engineering LLC.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Declarative Engineering LLC
|
||||
* verson 1 which accompanies this distribution, and is available at
|
||||
* http://declarativeengineering.com/legal/DE_Developer_License_v1.txt
|
||||
*/
|
||||
package com.foundation.view;
|
||||
|
||||
import java.io.Externalizable;
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectInput;
|
||||
import java.io.ObjectOutput;
|
||||
|
||||
import com.common.comparison.Comparator;
|
||||
|
||||
/*
|
||||
* An immutable gradient descriptor.
|
||||
*/
|
||||
public class JefGradient implements Externalizable, Cloneable {
|
||||
public static final int HORIZONTAL = 0;
|
||||
public static final int VERTICAL = 1;
|
||||
public static final int DIAGONAL = 2;
|
||||
public static final int REVERSE_DIAGONAL = 3;
|
||||
|
||||
private int direction = DIAGONAL;
|
||||
private JefColor startColor;
|
||||
private JefColor endColor;
|
||||
/**
|
||||
* JefGradient constructor.
|
||||
*/
|
||||
public JefGradient() {
|
||||
}//JefGradient()//
|
||||
/**
|
||||
* JefGradient constructor.
|
||||
* @param gradientText The text containing the properly formatted gradient data.
|
||||
*/
|
||||
public JefGradient(String gradientText) {
|
||||
parseString(gradientText);
|
||||
}//JefGradient()//
|
||||
/**
|
||||
* JefGradient constructor.
|
||||
* @param solidColor The solid color - the gradient will result in a solid color instead of a gradient.
|
||||
*/
|
||||
public JefGradient(JefColor color) {
|
||||
this.startColor = color;
|
||||
this.endColor = null;
|
||||
this.direction = 0;
|
||||
}//JefGradient()//
|
||||
/**
|
||||
* JefGradient constructor.
|
||||
* @param startColor The color that starts the gradient, or is the solid color if the endColor is not provided.
|
||||
* @param endColor The end color for the gradient, or null if using a solid color.
|
||||
* @param direction The direction of the gradient.
|
||||
* @see #HORIZONTAL
|
||||
* @see #VERTICAL
|
||||
* @see #DIAGONAL
|
||||
* @see #REVERSE_DIAGONAL
|
||||
*/
|
||||
public JefGradient(JefColor startColor, JefColor endColor, int direction) {
|
||||
this.startColor = startColor;
|
||||
this.endColor = endColor;
|
||||
this.direction = direction;
|
||||
}//JefGradient()//
|
||||
/**
|
||||
* Parses a string to obtain the gradient.
|
||||
* @param gradientText The text containing the gradient information.
|
||||
*/
|
||||
private void parseString(String gradientText) {
|
||||
if(gradientText != null) {
|
||||
int nextComma = -1;
|
||||
String color1 = null;
|
||||
String color2 = null;
|
||||
String direction = null;
|
||||
|
||||
nextComma = getFirstCommaLocation(gradientText);
|
||||
|
||||
if(nextComma != -1) {
|
||||
color1 = gradientText.substring(0, nextComma);
|
||||
gradientText = gradientText.length() > nextComma + 1 ? gradientText.substring(nextComma + 1) : "";
|
||||
nextComma = getFirstCommaLocation(gradientText);
|
||||
|
||||
if(nextComma != -1) {
|
||||
color2 = gradientText.substring(0, nextComma);
|
||||
direction = gradientText.length() > nextComma + 1 ? gradientText.substring(nextComma + 1).trim() : "";
|
||||
}//if//
|
||||
else {
|
||||
//Use the default direction.//
|
||||
color2 = gradientText;
|
||||
}//else//
|
||||
}//if//
|
||||
else {
|
||||
//We only have one color.//
|
||||
color1 = gradientText;
|
||||
}//else//
|
||||
|
||||
startColor = new JefColor(color1);
|
||||
|
||||
if(color2 != null) {
|
||||
endColor = new JefColor(color2);
|
||||
}//if//
|
||||
|
||||
if(direction != null) {
|
||||
this.direction = direction.equalsIgnoreCase("vertical") ? VERTICAL : direction.equalsIgnoreCase("horizontal") ? HORIZONTAL : direction.equalsIgnoreCase("reverse diagonal") ? REVERSE_DIAGONAL : DIAGONAL;
|
||||
}//if//
|
||||
}//if//
|
||||
else {
|
||||
//Invalid text.//
|
||||
startColor = new JefColor(JefColor.COLOR_BLACK);
|
||||
}//else//
|
||||
}//parseString()//
|
||||
/**
|
||||
* Locates the next comma not inside parenthises (which are used by the color text to store the color values).
|
||||
* @param gradientText The gradient text.
|
||||
* @return Gets the first comma location.
|
||||
*/
|
||||
private int getFirstCommaLocation(String gradientText) {
|
||||
int nextParen = gradientText.indexOf('(');
|
||||
int nextComma = gradientText.indexOf(',');
|
||||
|
||||
while((nextParen != -1) && (nextComma > nextParen)) {
|
||||
nextParen = gradientText.indexOf(')', nextParen);
|
||||
|
||||
if(nextComma < nextParen) {
|
||||
nextComma = gradientText.indexOf(',', nextParen);
|
||||
}//if//
|
||||
|
||||
nextParen = gradientText.indexOf('(', nextParen);
|
||||
}//while//
|
||||
|
||||
return nextComma;
|
||||
}//getFirstCommaLocation()//
|
||||
/**
|
||||
* Gets the color that starts the gradient.
|
||||
* @return The color that begins the gradient, or the solid color if the end color is not supplied. This will never be null.
|
||||
*/
|
||||
public JefColor getStartColor() {
|
||||
return startColor;
|
||||
}//getStartColor()//
|
||||
/**
|
||||
* Gets the color that ends the gradient.
|
||||
* @return The color that ends the gradient, or null if the gradient is really a solid color.
|
||||
*/
|
||||
public JefColor getEndColor() {
|
||||
return endColor;
|
||||
}//getEndColor()//
|
||||
/**
|
||||
* Gets the gradient direction which can be horizontal, vertical, diagonal, or reverse diagonal.
|
||||
* @return The direction of the gradient.
|
||||
* @see #HORIZONTAL
|
||||
* @see #VERTICAL
|
||||
* @see #DIAGONAL
|
||||
* @see #REVERSE_DIAGONAL
|
||||
*/
|
||||
public int getDirection() {
|
||||
return direction;
|
||||
}//getDirection()//
|
||||
/* (non-Javadoc)
|
||||
* @see java.io.Externalizable#readExternal(java.io.ObjectInput)
|
||||
*/
|
||||
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
|
||||
in.readByte();
|
||||
startColor = (JefColor) in.readObject();
|
||||
endColor = (JefColor) in.readObject();
|
||||
direction = in.readInt();
|
||||
}//readExternal()//
|
||||
/* (non-Javadoc)
|
||||
* @see java.io.Externalizable#writeExternal(java.io.ObjectOutput)
|
||||
*/
|
||||
public void writeExternal(ObjectOutput out) throws IOException {
|
||||
out.writeByte(0);
|
||||
out.writeObject(startColor);
|
||||
out.writeObject(endColor);
|
||||
out.writeInt(direction);
|
||||
}//writeExternal()//
|
||||
/* (non-Javadoc)
|
||||
* @see java.lang.Object#clone()
|
||||
*/
|
||||
public Object clone() throws CloneNotSupportedException {
|
||||
JefGradient clone = new JefGradient();
|
||||
|
||||
clone.startColor = (JefColor) startColor.clone();
|
||||
clone.endColor = (JefColor) (endColor != null ? endColor.clone() : null);
|
||||
clone.direction = direction;
|
||||
|
||||
return clone;
|
||||
}//clone()//
|
||||
/* (non-Javadoc)
|
||||
* @see java.lang.Object#toString()
|
||||
*/
|
||||
public String toString() {
|
||||
return startColor + (endColor != null ? ',' + endColor.toString() + ',' + (direction == DIAGONAL ? "diagonal" : direction == VERTICAL ? "vertical" : direction == HORIZONTAL ? "horizontal" : "reverse diagonal") : "");
|
||||
}//toString()//
|
||||
/* (non-Javadoc)
|
||||
* @see java.lang.Object#equals(java.lang.Object)
|
||||
*/
|
||||
public boolean equals(Object object) {
|
||||
return (object instanceof JefGradient) && (Comparator.equals(startColor, ((JefGradient) object).startColor)) && (Comparator.equals(endColor, ((JefGradient) object).endColor)) && (direction == ((JefGradient) object).direction);
|
||||
}//equals()//
|
||||
/* (non-Javadoc)
|
||||
* @see java.lang.Object#hashCode()
|
||||
*/
|
||||
public int hashCode() {
|
||||
return startColor.hashCode() ^ (endColor != null ? endColor.hashCode() : 0) ^ direction;
|
||||
}//hashCode()//
|
||||
}//JefGradient//
|
||||
176
Foundation/src/com/foundation/view/JefImage.java
Normal file
176
Foundation/src/com/foundation/view/JefImage.java
Normal file
@@ -0,0 +1,176 @@
|
||||
/*
|
||||
* Copyright (c) 2005,2009 Declarative Engineering LLC.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Declarative Engineering LLC
|
||||
* verson 1 which accompanies this distribution, and is available at
|
||||
* http://declarativeengineering.com/legal/DE_Developer_License_v1.txt
|
||||
*/
|
||||
package com.foundation.view;
|
||||
|
||||
import java.io.Externalizable;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectInput;
|
||||
import java.io.ObjectOutput;
|
||||
|
||||
import com.common.debug.Debug;
|
||||
|
||||
public class JefImage implements Externalizable, Cloneable {
|
||||
static final long serialVersionUID = -7739741206125442482L;
|
||||
public static final String[] SWT_SUPPORTED_IMAGE_EXTENSIONS = new String[] {"*.jpg;*.jpeg;*.gif;*.bmp;*.tiff;*.png;*.ico"};
|
||||
|
||||
/** The image data that can be used by the view system to initialize the view system specific image structure. */
|
||||
private byte[] imageData = null;
|
||||
/** The hash code which is a simple hash of the image data. */
|
||||
private int hashCode = 0;
|
||||
/**
|
||||
* JefImage default constructor.
|
||||
* <p>Note: The only reason this is public is to allow easy creation by the serialization code.</p>
|
||||
*/
|
||||
public JefImage() {
|
||||
super();
|
||||
}//JefImage()//
|
||||
/**
|
||||
* JefImage constructor.
|
||||
* @param fileName The path and file name to the image file.
|
||||
* @throws IOException Thrown if the file could not be found, opened, or read, or did not contain a valid image.
|
||||
*/
|
||||
public JefImage(String fileName) {
|
||||
super();
|
||||
loadImageData(fileName);
|
||||
initialize();
|
||||
}//JefImage()//
|
||||
/**
|
||||
* JefImage constructor.
|
||||
* @param imageData
|
||||
*/
|
||||
public JefImage(byte[] imageData) {
|
||||
super();
|
||||
this.imageData = imageData;
|
||||
initialize();
|
||||
}//JefImage()//
|
||||
/**
|
||||
* Gets the raw bytes for the image.
|
||||
* @return The image data.
|
||||
*/
|
||||
public byte[] getImageData() {
|
||||
return imageData;
|
||||
}//getImageData()//
|
||||
/**
|
||||
* Initializes the image data.
|
||||
* @param fileName The path and file name to the image file.
|
||||
*/
|
||||
private void loadImageData(String fileName) {
|
||||
File file = new File(fileName);
|
||||
boolean success = false;
|
||||
|
||||
if(file.exists() && file.canRead() && file.isFile()) {
|
||||
FileInputStream in = null;
|
||||
|
||||
try {
|
||||
int count = 0;
|
||||
|
||||
in = new FileInputStream(file);
|
||||
imageData = new byte[in.available()];
|
||||
|
||||
while(count != imageData.length) {
|
||||
count += in.read((byte[]) imageData);
|
||||
}//while//
|
||||
|
||||
success = true;
|
||||
}//try//
|
||||
catch(FileNotFoundException e) {
|
||||
Debug.log(e, "Unable to load the file: " + file);
|
||||
}//catch//
|
||||
catch(IOException e) {
|
||||
Debug.log(e, "Unable to load the file: " + file);
|
||||
}//catch//
|
||||
finally {
|
||||
try {
|
||||
in.close();
|
||||
}//try//
|
||||
catch(Throwable e) {
|
||||
Debug.handle(e);
|
||||
}//catch//
|
||||
}//finally//
|
||||
}//if//
|
||||
|
||||
if(!success) {
|
||||
Debug.log(new IOException("Failed to load the image: " + fileName));
|
||||
}//if//
|
||||
}//loadImageData()//
|
||||
/**
|
||||
* Initializes the image data.
|
||||
* @param fileName The path and file name to the image file.
|
||||
*/
|
||||
private void initialize() {
|
||||
//Initialize the hash code.//
|
||||
if(imageData != null) {
|
||||
for(int index = 0; index < imageData.length; index++) {
|
||||
switch(index % 4) {
|
||||
case 0:
|
||||
hashCode ^= ((int) imageData[index]);
|
||||
break;
|
||||
case 1:
|
||||
hashCode ^= (((int) imageData[index]) << 8);
|
||||
break;
|
||||
case 2:
|
||||
hashCode ^= (((int) imageData[index]) << 16);
|
||||
break;
|
||||
case 3:
|
||||
hashCode ^= (((int) imageData[index]) << 24);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}//switch//
|
||||
}//for//
|
||||
}//if//
|
||||
}//initialize()//
|
||||
/* (non-Javadoc)
|
||||
* @see java.io.Externalizable#readExternal(java.io.ObjectInput)
|
||||
*/
|
||||
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
|
||||
/*byte version = */in.readByte();
|
||||
int imageLength = in.readInt();
|
||||
int readCount = 0;
|
||||
|
||||
imageData = new byte[imageLength];
|
||||
|
||||
while(readCount != imageData.length) {
|
||||
readCount += in.read(imageData, readCount, imageData.length - readCount);
|
||||
}//while//
|
||||
}//readExternal()//
|
||||
/* (non-Javadoc)
|
||||
* @see java.io.Externalizable#writeExternal(java.io.ObjectOutput)
|
||||
*/
|
||||
public void writeExternal(ObjectOutput out) throws IOException {
|
||||
out.writeByte(0);
|
||||
out.writeInt(imageData.length);
|
||||
out.write(imageData);
|
||||
}//writeExternal()//
|
||||
/* (non-Javadoc)
|
||||
* @see java.lang.Object#equals(java.lang.Object)
|
||||
*/
|
||||
public boolean equals(Object object) {
|
||||
boolean result = object instanceof JefImage && object.hashCode() == hashCode() && ((imageData == null && ((JefImage) object).getImageData() == null) || (((JefImage) object).getImageData() != null && imageData != null && ((JefImage) object).getImageData().length == imageData.length));
|
||||
|
||||
//Perform a full comparison on the image data if all else checks out.//
|
||||
if(result) {
|
||||
byte[] objectImageData = ((JefImage) object).getImageData();
|
||||
|
||||
for(int index = imageData.length - 1; (result) && (index >= 0); index--) {
|
||||
result = objectImageData[index] == imageData[index];
|
||||
}//for//
|
||||
}//if//
|
||||
|
||||
return result;
|
||||
}//equals()//
|
||||
/* (non-Javadoc)
|
||||
* @see java.lang.Object#hashCode()
|
||||
*/
|
||||
public int hashCode() {
|
||||
return hashCode;
|
||||
}//hashCode()//
|
||||
}//JefImage//
|
||||
43
Foundation/src/com/foundation/view/KeyBinding.java
Normal file
43
Foundation/src/com/foundation/view/KeyBinding.java
Normal file
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright (c) 2007 Declarative Engineering LLC.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Declarative Engineering LLC
|
||||
* verson 1 which accompanies this distribution, and is available at
|
||||
* http://declarativeengineering.com/legal/DE_Developer_License_v1.txt
|
||||
*/
|
||||
package com.foundation.view;
|
||||
|
||||
public class KeyBinding extends MethodAssociation implements IKeyBinding {
|
||||
/** The modifiers applied to the key code, or if the key code is null this may only contain one modifier. */
|
||||
private int modifiers = 0;
|
||||
/** The unmodified character which causes the key event. This may be null if only the modifier should be used. In such a case there may only be one modifier specified. */
|
||||
private Integer keyCode = null;
|
||||
/**
|
||||
* KeyBinding constructor.
|
||||
* @param handler The object that handles the method invocations.
|
||||
* @param associationNumber The association number which will be passed to the handler when the assocation is invoked. This number allows fast indexing of the methods that the handler handles.
|
||||
* @param component The component the association is connected to.
|
||||
* @param valueHolderName The optional name of the value holder for the association.
|
||||
* @param heldType The optional class which is a subclass of the value holder's held type and defines the association.
|
||||
* @param rowType The optional class which defines the association if a held type or value holder is not provided, and it determines whether this association is used based on whether it matches the control's row type.
|
||||
* @param modifiers The modifiers applied to the key code, or if the key code is null this may only contain one modifier.
|
||||
* @param keyCode The unmodified character which causes the key event. This may be null if only the modifier should be used. In such a case there may only be one modifier specified.
|
||||
*/
|
||||
public KeyBinding(IAssociationHandler handler, int associationNumber, IAbstractComponent component, String valueHolderName, Class heldType, Class rowType, int modifiers, Integer keyCode) {
|
||||
super(handler, associationNumber, component, valueHolderName, heldType, rowType);
|
||||
this.modifiers = modifiers;
|
||||
this.keyCode = keyCode;
|
||||
}//KeyBinding()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.view.IKeyBinding#getModifiers()
|
||||
*/
|
||||
public int getModifiers() {
|
||||
return modifiers;
|
||||
}//getModifiers()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.view.IKeyBinding#getKeyCode()
|
||||
*/
|
||||
public Integer getKeyCode() {
|
||||
return keyCode;
|
||||
}//getKeyCode()//
|
||||
}//KeyBinding//
|
||||
122
Foundation/src/com/foundation/view/LinkData.java
Normal file
122
Foundation/src/com/foundation/view/LinkData.java
Normal file
@@ -0,0 +1,122 @@
|
||||
/*
|
||||
* Copyright (c) 2006,2009 Declarative Engineering LLC.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Declarative Engineering LLC
|
||||
* verson 1 which accompanies this distribution, and is available at
|
||||
* http://declarativeengineering.com/legal/DE_Developer_License_v1.txt
|
||||
*/
|
||||
package com.foundation.view;
|
||||
|
||||
/**
|
||||
* Encapsulates a link between one component's functionality and another component's target.
|
||||
*/
|
||||
public class LinkData {
|
||||
private Object component = null;
|
||||
private int target = -1;
|
||||
private Object data = null;
|
||||
private boolean isBoolean = false;
|
||||
private boolean invertLogic = false;
|
||||
private boolean nullValue = false;
|
||||
/**
|
||||
* LinkData constructor.
|
||||
* @param component The targeted component.
|
||||
* @param target The target component's target identifier for the linked link-target.
|
||||
* @param data The optional data passed when the link is invoked.
|
||||
* @param invertLogic Whether binary logic should be inverted when invoking the link.
|
||||
*/
|
||||
public LinkData(Object component, int target, Object data) {
|
||||
super();
|
||||
this.component = component;
|
||||
this.target = target;
|
||||
this.data = data;
|
||||
this.isBoolean = false;
|
||||
}//LinkData()//
|
||||
/**
|
||||
* LinkData constructor.
|
||||
* @param component The targeted component.
|
||||
* @param target The target component's target identifier for the linked link-target.
|
||||
* @param data The optional data passed when the link is invoked.
|
||||
* @param invertLogic Whether binary logic should be inverted when invoking the link.
|
||||
*/
|
||||
public LinkData(Object component, int target, Object data, boolean invertLogic) {
|
||||
this(component, target, data, invertLogic, false);
|
||||
}//LinkData()//
|
||||
/**
|
||||
* LinkData constructor.
|
||||
* @param component The targeted component.
|
||||
* @param target The target component's target identifier for the linked link-target.
|
||||
* @param data The optional data passed when the link is invoked.
|
||||
* @param invertLogic Whether binary logic should be inverted when invoking the link.
|
||||
* @param nullValue The value to use when the input is null. The inverse will be used when the value is non-null and non-boolean.
|
||||
*/
|
||||
public LinkData(Object component, int target, Object data, boolean invertLogic, boolean nullValue) {
|
||||
super();
|
||||
this.component = component;
|
||||
this.target = target;
|
||||
this.data = data;
|
||||
this.invertLogic = invertLogic;
|
||||
this.nullValue = nullValue;
|
||||
this.isBoolean = true;
|
||||
}//LinkData()//
|
||||
/**
|
||||
* LinkData constructor.
|
||||
* @param component The targeted component.
|
||||
* @param target The target component's target identifier for the linked link-target.
|
||||
* @param data The optional data passed when the link is invoked.
|
||||
* @param isBoolean Whether the invertLogic and nullValue parameters are used.
|
||||
* @param invertLogic Whether binary logic should be inverted when invoking the link.
|
||||
* @param nullValue The value to use when the input is null. The inverse will be used when the value is non-null and non-boolean.
|
||||
*/
|
||||
public LinkData(Object component, int target, Object data, boolean isBoolean, boolean invertLogic, boolean nullValue) {
|
||||
super();
|
||||
this.component = component;
|
||||
this.target = target;
|
||||
this.data = data;
|
||||
this.invertLogic = invertLogic;
|
||||
this.nullValue = nullValue;
|
||||
this.isBoolean = isBoolean;
|
||||
}//LinkData()//
|
||||
/**
|
||||
* Gets the reference to the targeted component.
|
||||
* @return The targeted component.
|
||||
*/
|
||||
public Object getComponent() {
|
||||
return component;
|
||||
}//getComponent()//
|
||||
/**
|
||||
* Gets the target identifier as defined by the target component.
|
||||
* @return The target component's target identifier for the linked link-target.
|
||||
*/
|
||||
public int getTarget() {
|
||||
return target;
|
||||
}//getTarget()//
|
||||
/**
|
||||
* Gets the optional static data passed when invoking the link.
|
||||
* This will only be non-null if the link target requires data, the required data type is serializable and immutable, and the linking component does not provide any data.
|
||||
* @return The optional data passed when the link is invoked.
|
||||
*/
|
||||
public Object getData() {
|
||||
return data;
|
||||
}//getData()//
|
||||
/**
|
||||
* Gets whether the link is passing boolean data.
|
||||
* @return Whether the link is transfering a boolean value, in which case the nullValue and invertLogic flags should not be ignored.
|
||||
*/
|
||||
public boolean isBoolean() {
|
||||
return isBoolean;
|
||||
}//isBoolean()//
|
||||
/**
|
||||
* Determines whether binary logic should be inverted when invoking the link.
|
||||
* @return Whether inversion of binary logic should occur.
|
||||
*/
|
||||
public boolean invertLogic() {
|
||||
return invertLogic;
|
||||
}//invertLogic()//
|
||||
/**
|
||||
* Gets the value used when the input is null. The inverse will be used when the value is non-null and non-boolean.
|
||||
* @return The value used for a null input.
|
||||
*/
|
||||
public boolean nullValue() {
|
||||
return nullValue;
|
||||
}//nullValue()//
|
||||
}//LinkData//
|
||||
84
Foundation/src/com/foundation/view/MethodAssociation.java
Normal file
84
Foundation/src/com/foundation/view/MethodAssociation.java
Normal file
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* Copyright (c) 2003,2007 Declarative Engineering LLC.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Declarative Engineering LLC
|
||||
* verson 1 which accompanies this distribution, and is available at
|
||||
* http://declarativeengineering.com/legal/DE_Developer_License_v1.txt
|
||||
*/
|
||||
package com.foundation.view;
|
||||
|
||||
import com.common.debug.*;
|
||||
|
||||
public class MethodAssociation extends ValueHolderAssociation implements IMethodAssociation {
|
||||
private int associationNumber = 0;
|
||||
private IAssociationHandler handler = null;
|
||||
/** The listener for held value changed notifications. */
|
||||
private IMethodAssociationChangeListener listener = null;
|
||||
/**
|
||||
* MethodAssociation constructor.
|
||||
* @param handler The object that handles the method invocations.
|
||||
* @param associationNumber The association number which will be passed to the handler when the assocation is invoked. This number allows fast indexing of the methods that the handler handles.
|
||||
* @param component The component the association is connected to.
|
||||
* @param valueHolderName The optional name of the value holder for the association.
|
||||
* @param heldType The optional class which is a subclass of the value holder's held type and defines the association.
|
||||
* @param rowType The optional class which defines the association if a held type or value holder is not provided, and it determines whether this association is used based on whether it matches the control's row type.
|
||||
*/
|
||||
public MethodAssociation(IAssociationHandler handler, int associationNumber, IAbstractComponent component, String valueHolderName, Class heldType, Class rowType) {
|
||||
super(component, valueHolderName, heldType, rowType);
|
||||
|
||||
this.handler = handler;
|
||||
this.associationNumber = associationNumber;
|
||||
}//MethodAssociation()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.tcv.swt.server.IMethodAssociation#invoke(java.lang.Object[], boolean)
|
||||
*/
|
||||
public Object invoke(Object[] parameters, boolean synchronous) {
|
||||
return invoke(null, parameters, synchronous);
|
||||
}//invoke()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.tcv.swt.server.IMethodAssociation#invoke(java.lang.Object, java.lang.Object[], boolean)
|
||||
*/
|
||||
public Object invoke(final Object value, final Object[] parameters, boolean synchronous) {
|
||||
Object result = null;
|
||||
Object object = value != null ? value : getValueHolder().getValue();
|
||||
|
||||
try {
|
||||
if(object != null) {
|
||||
result = handler.invokeMethod(associationNumber, object, parameters);
|
||||
}//if//
|
||||
}//try//
|
||||
catch(Throwable e) {
|
||||
Debug.log(e);
|
||||
}//catch//
|
||||
|
||||
return result;
|
||||
}//invoke()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.view.IMethodAssociation#heldValueChanged()
|
||||
*/
|
||||
public void heldValueChanged() {
|
||||
listener.onEventFired(this);
|
||||
}//heldValueChanged()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.view.IMethodAssociation#register()
|
||||
*/
|
||||
public void register() {
|
||||
if(getIsValueHolderAssociated()) {
|
||||
getValueHolder().registerListener(this);
|
||||
}//if//
|
||||
}//register()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.view.IMethodAssociation#unregister()
|
||||
*/
|
||||
public void unregister() {
|
||||
if(getIsValueHolderAssociated()) {
|
||||
getValueHolder().unregisterListener(this);
|
||||
}//if//
|
||||
}//unregister()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.view.IMethodAssociation#setChangeListener(com.foundation.view.IMethodAssociationChangeListener)
|
||||
*/
|
||||
public void setChangeListener(IMethodAssociationChangeListener listener) {
|
||||
this.listener = listener;
|
||||
}//setChangeListener()//
|
||||
}//MethodAssociation//
|
||||
543
Foundation/src/com/foundation/view/MultiAssociation.java
Normal file
543
Foundation/src/com/foundation/view/MultiAssociation.java
Normal file
@@ -0,0 +1,543 @@
|
||||
/*
|
||||
* Copyright (c) 2006,2009 Declarative Engineering LLC.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Declarative Engineering LLC
|
||||
* verson 1 which accompanies this distribution, and is available at
|
||||
* http://declarativeengineering.com/legal/DE_Developer_License_v1.txt
|
||||
*/
|
||||
package com.foundation.view;
|
||||
|
||||
import com.common.util.IHashSet;
|
||||
import com.common.util.IIterator;
|
||||
import com.common.util.LiteHashSet;
|
||||
import com.foundation.event.EventSupport;
|
||||
import com.foundation.event.IEventEmitter;
|
||||
import com.foundation.event.IEventHandler;
|
||||
import com.foundation.metadata.Attribute;
|
||||
import com.foundation.util.IManagedCollection;
|
||||
import com.foundation.view.EventAssociation;
|
||||
import com.foundation.view.IAssociationHandler;
|
||||
import com.foundation.view.IEventAssociation;
|
||||
import com.foundation.view.IEventAssociationChangeListener;
|
||||
import com.foundation.view.IValueHolderListener;
|
||||
import com.foundation.view.MultiAssociationContainer.RowDataContainer;
|
||||
|
||||
/*
|
||||
* Models an association that results in one value for each row value given to it.
|
||||
*/
|
||||
public class MultiAssociation extends Association implements IEventAssociationChangeListener, IValueHolderListener {
|
||||
/** The set of events that the association registers for when the input changes. If any of these events are triggered it indicates that this association's value has changed. */
|
||||
private EventAssociation[] events = null;
|
||||
/** Tracks the row data instances created by the multi-association. */
|
||||
private IHashSet rowDataSet = null;
|
||||
/** Whether the association produces a target result. If this is false then the result will be feed back into the pool of associations in the container. */
|
||||
private boolean isTarget = false;
|
||||
/** The attribute to be used to access decorations for the association. If this is null then the object its self will have the decorations. */
|
||||
private Attribute decoratedAttribute;
|
||||
/** Whether decorations should be accessed for the association. */
|
||||
private boolean useDecorations;
|
||||
/** The optional fixed value. This will only be used if the association identifier is -2. */
|
||||
private Object fixedValue;
|
||||
|
||||
/**
|
||||
* Makes up a doublely linked list where the start of the list is held by the association container.
|
||||
* Each row data in the list represents one step in the chain of objects navigated to get to the final target result.
|
||||
*/
|
||||
public static class RowData extends MultiAssociationContainer.RowData implements IEventHandler {
|
||||
/** The row that last generated the result. */
|
||||
private Object row = null;
|
||||
/** The last known result for this association. */
|
||||
private Object result = null;
|
||||
/** The next association in the chain. This should always be null if this is a target association. */
|
||||
private MultiAssociation.RowData next = null;
|
||||
/** The previous association in the chain. This should always be null if this is a target association. */
|
||||
private MultiAssociation.RowData previous = null;
|
||||
/** The support object that tracks events registered via the AssociationNodes. This will only be non-null if association attribute and nodes were provided. */
|
||||
private EventSupport nodeEventSupport = null;
|
||||
/** The container for the row that this row data models. */
|
||||
private MultiAssociationContainer.RowDataContainer rowDataContainer = null;
|
||||
|
||||
/**
|
||||
* RowData constructor.
|
||||
* @param association The association that created the row data.
|
||||
* @param row The row object that initialized the row data and generated the result.
|
||||
* @param rowDataContainer The container that this row data exists in.
|
||||
* @param result The result for the row.
|
||||
*/
|
||||
public RowData(MultiAssociation association, Object row, MultiAssociationContainer.RowDataContainer rowDataContainer, Object result) {
|
||||
super(association);
|
||||
this.row = row;
|
||||
this.rowDataContainer = rowDataContainer;
|
||||
this.result = result;
|
||||
}//RowData()//
|
||||
/**
|
||||
* Gets the latest result for the association row.
|
||||
* @return The association row's current result.
|
||||
*/
|
||||
public Object getResult() {
|
||||
return result;
|
||||
}//getResult()//
|
||||
/**
|
||||
* Gets the next association in the chain.
|
||||
* @return The next association in the chain.
|
||||
*/
|
||||
public MultiAssociation.RowData getNext() {
|
||||
return next;
|
||||
}//getNext()//
|
||||
/**
|
||||
* Sets the next association in the chain.
|
||||
* @param next The next association in the chain.
|
||||
*/
|
||||
public void setNext(MultiAssociation.RowData next) {
|
||||
this.next = next;
|
||||
}//setNext()//
|
||||
/**
|
||||
* Gets the previous association in the chain.
|
||||
* @return The previous association in the chain.
|
||||
*/
|
||||
public MultiAssociation.RowData getPrevious() {
|
||||
return previous;
|
||||
}//getPrevious()//
|
||||
/**
|
||||
* Sets the previous association in the chain.
|
||||
* @param previous The previous association in the chain.
|
||||
*/
|
||||
public void setPrevious(MultiAssociation.RowData previous) {
|
||||
this.previous = previous;
|
||||
}//setPrevious()//
|
||||
/**
|
||||
* Gets the row data container.
|
||||
* @return The container containing this row data.
|
||||
*/
|
||||
public MultiAssociationContainer.RowDataContainer getContainer() {
|
||||
return rowDataContainer;
|
||||
}//getContainer()//
|
||||
/**
|
||||
* Gets the row that this row data describes.
|
||||
* @return The row object which was input for this row data.
|
||||
*/
|
||||
public Object getRow() {
|
||||
return row;
|
||||
}//getRow()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.event.IEventHandler#evaluate(com.foundation.event.IEventEmitter, int, java.lang.Object[], int)
|
||||
*/
|
||||
public void evaluate(IEventEmitter eventEmitter, int eventNumber, Object[] eventParameters, int flags) {
|
||||
//Called when an event, registered on the row object that generated the result, is fired.//
|
||||
((MultiAssociation) getAssociation()).updateAssociation(this, getAssociation().isTargetAssociation() ? flags : 0);
|
||||
}//evaluate()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.event.IHandler#evaluate(int, java.lang.Object[], int)
|
||||
*/
|
||||
public void evaluate(int eventNumber, Object[] eventParameters, int flags) {
|
||||
//Never called.//
|
||||
}//evaluate()//
|
||||
}//RowData//
|
||||
/**
|
||||
* MultiAssociation constructor.
|
||||
* @param rowType The type of row the association applies to. In the case of a root association in a single association this is the type held by the value holder.
|
||||
* @param fixedValue The value to return as the result.
|
||||
*/
|
||||
public MultiAssociation(Class rowType, Object fixedValue, boolean isTarget) {
|
||||
this(rowType, -2, null, false, null, null, null, null, isTarget, null, false, fixedValue);
|
||||
}//MultiAssociation()//
|
||||
/**
|
||||
* MultiAssociation constructor.
|
||||
* @param rowType The type of row the association applies to. In the case of a root association in a single association this is the type held by the value holder.
|
||||
* @param associationNumber The association number which will be passed to the handler when the assocation is invoked. This number allows fast indexing of the methods that the handler handles. This value may be -1 if the input value should be used as the output value for the association (short circuiting).
|
||||
* @param handler The object that handles the method invocations to get and set the value.
|
||||
* @param invertLogic Whether the result of the association will be inverted. This is ignored unless the result is a Boolean value.
|
||||
* @param attributes The attributes to listen to and whose values are passed to the nodes. This may be null.
|
||||
* @param nodes The nodes of the arbitrary tree of listeners for value change events for this association's result value. This may be null.
|
||||
* @param children The child associations in search order. The children will be searched for the first match for this association's result. This should always be null for target associations, and should never be null for non-target associations.
|
||||
* @param events The non-null array of events that will be used to listen to the supplied row object for changes in the result.
|
||||
* @param valueHolderName The name of the optional value holder whose value holds the getter and setter methods used by the association. If no value holder is defined then the row object passed to the association must define the getter and setter methods.
|
||||
*/
|
||||
public MultiAssociation(Class rowType, int associationNumber, IAssociationHandler handler, boolean invertLogic, AssociationAttribute[] attributes, AssociationNode[] nodes, EventAssociation[] events, String valueHolderName, boolean isTarget) {
|
||||
this(rowType, associationNumber, handler, invertLogic, attributes, nodes, events, valueHolderName, isTarget, null, false, null);
|
||||
|
||||
if(events == null) {
|
||||
throw new IllegalArgumentException("The events parameter may not be null.");
|
||||
}//if//
|
||||
}//MultiAssociation()//
|
||||
/**
|
||||
* MultiAssociation constructor.
|
||||
* @param rowType The type of row the association applies to. In the case of a root association in a single association this is the type held by the value holder.
|
||||
* @param associationNumber The association number which will be passed to the handler when the assocation is invoked. This number allows fast indexing of the methods that the handler handles. This value may be -1 if the input value should be used as the output value for the association (short circuiting).
|
||||
* @param handler The object that handles the method invocations to get and set the value.
|
||||
* @param invertLogic Whether the result of the association will be inverted. This is ignored unless the result is a Boolean value.
|
||||
* @param attributes The attributes to listen to and whose values are passed to the nodes. This may be null.
|
||||
* @param nodes The nodes of the arbitrary tree of listeners for value change events for this association's result value. This may be null.
|
||||
* @param children The child associations in search order. The children will be searched for the first match for this association's result. This should always be null for target associations, and should never be null for non-target associations.
|
||||
* @param events The non-null array of events that will be used to listen to the supplied row object for changes in the result.
|
||||
* @param valueHolderName The name of the optional value holder whose value holds the getter and setter methods used by the association. If no value holder is defined then the row object passed to the association must define the getter and setter methods.
|
||||
* @param decoratedAttribute The attribute that the decorations will be attached to, or null if the object its self will be decorated.
|
||||
*/
|
||||
public MultiAssociation(Class rowType, int associationNumber, IAssociationHandler handler, boolean invertLogic, AssociationAttribute[] attributes, AssociationNode[] nodes, EventAssociation[] events, String valueHolderName, boolean isTarget, Attribute decoratedAttribute) {
|
||||
this(rowType, associationNumber, handler, invertLogic, attributes, nodes, events, valueHolderName, isTarget, decoratedAttribute, true, null);
|
||||
|
||||
if(events == null) {
|
||||
throw new IllegalArgumentException("The events parameter may not be null.");
|
||||
}//if//
|
||||
}//MultiAssociation()//
|
||||
/**
|
||||
* MultiAssociation constructor.
|
||||
* @param rowType The type of row the association applies to. In the case of a root association in a single association this is the type held by the value holder.
|
||||
* @param associationNumber The association number which will be passed to the handler when the assocation is invoked. This number allows fast indexing of the methods that the handler handles. This value may be -1 if the input value should be used as the output value for the association (short circuiting).
|
||||
* @param handler The object that handles the method invocations to get and set the value.
|
||||
* @param invertLogic Whether the result of the association will be inverted. This is ignored unless the result is a Boolean value.
|
||||
* @param attributes The attributes to listen to and whose values are passed to the nodes. This may be null.
|
||||
* @param nodes The nodes of the arbitrary tree of listeners for value change events for this association's result value. This may be null.
|
||||
* @param children The child associations in search order. The children will be searched for the first match for this association's result. This should always be null for target associations, and should never be null for non-target associations.
|
||||
* @param events The non-null array of events that will be used to listen to the supplied row object for changes in the result.
|
||||
* @param valueHolderName The name of the optional value holder whose value holds the getter and setter methods used by the association. If no value holder is defined then the row object passed to the association must define the getter and setter methods.
|
||||
* @param decoratedAttribute The attribute that the decorations will be attached to, or null if the object its self will be decorated.
|
||||
* @param useDecorations Whether the association should link to the decorations mappings. This is required because the decorated attribute may be null and the association may still link to the decorations.
|
||||
*/
|
||||
protected MultiAssociation(Class rowType, int associationNumber, IAssociationHandler handler, boolean invertLogic, AssociationAttribute[] attributes, AssociationNode[] nodes, EventAssociation[] events, String valueHolderName, boolean isTarget, Attribute decoratedAttribute, boolean useDecorations, Object fixedValue) {
|
||||
super(rowType, associationNumber, handler, invertLogic, attributes, nodes, valueHolderName);
|
||||
|
||||
this.fixedValue = fixedValue;
|
||||
this.isTarget = isTarget;
|
||||
this.events = events;
|
||||
this.useDecorations = useDecorations;
|
||||
this.decoratedAttribute = decoratedAttribute;
|
||||
|
||||
if(events != null) {
|
||||
for(int index = 0; index < events.length; index++) {
|
||||
events[index].setChangeListener(this);
|
||||
}//for//
|
||||
}//if//
|
||||
}//MultiAssociation()//
|
||||
/**
|
||||
* Gets the attribute whose decorations are referenced by this association.
|
||||
* @return The decorated attribute for this association, or null if the row object its self will be decorated.
|
||||
*/
|
||||
public Attribute getDecoratedAttribute() {
|
||||
return decoratedAttribute;
|
||||
}//getDecoratedAttribute()//
|
||||
/**
|
||||
* Gets whether the association should listen for decorations.
|
||||
* @return Whether the association is linked to the model for accessing decorations.
|
||||
*/
|
||||
public boolean useDecorations() {
|
||||
return useDecorations;
|
||||
}//useDecorations()//
|
||||
/**
|
||||
* Determines whether this is a target association meaning that its result is the value assigned to the initial association input.
|
||||
* @return Whether this association is a target and the result is not placed as input to another association.
|
||||
*/
|
||||
public boolean isTargetAssociation() {
|
||||
return isTarget;
|
||||
}//isTargetAssociation()//
|
||||
/**
|
||||
* Gets the current result of the association from the model.
|
||||
* @param row The row object that is the input to the association.
|
||||
* @return The result of the association.
|
||||
*/
|
||||
protected Object getResult(Object row) {
|
||||
Object result;
|
||||
int associationNumber = getAssociationNumber();
|
||||
|
||||
if(getIsValueHolderAssociated()) {
|
||||
if(associationNumber == -1) {
|
||||
result = getValueHolder().getValue();
|
||||
}//if//
|
||||
else if(associationNumber == -2) {
|
||||
result = fixedValue;
|
||||
}//else if//
|
||||
else {
|
||||
if(oneArray == null) {
|
||||
oneArray = new Object[1];
|
||||
}//if//
|
||||
|
||||
oneArray[0] = row;
|
||||
result = getHandler().invokeMethod(associationNumber, getValueHolder().getValue(), oneArray, IAssociationHandler.INVOKE_GETTER_METHOD_FLAG);
|
||||
oneArray[0] = null;
|
||||
}//if//
|
||||
}//if//
|
||||
else {
|
||||
if(associationNumber == -1) {
|
||||
result = row;
|
||||
}//if//
|
||||
else if(associationNumber == -2) {
|
||||
result = fixedValue;
|
||||
}//else if//
|
||||
else {
|
||||
result = getHandler().invokeMethod(associationNumber, row, null, IAssociationHandler.INVOKE_GETTER_METHOD_FLAG);
|
||||
}//else//
|
||||
}//else//
|
||||
|
||||
if(isTargetAssociation()) {
|
||||
result = convertValueToControl(result);
|
||||
}//if//
|
||||
|
||||
return result;
|
||||
}//getResult()//
|
||||
/**
|
||||
* Gets the original value for the association.
|
||||
* @return The value that the user has replaced via the view.
|
||||
*/
|
||||
public Object getOriginalResult(Object row) {
|
||||
Object result = null;
|
||||
int associationNumber = getAssociationNumber();
|
||||
|
||||
//Ignore non-target associations, short-circuit and fixed value setups.//
|
||||
if(isTargetAssociation() && associationNumber != -1 && associationNumber != -2) {
|
||||
if(getIsValueHolderAssociated()) {
|
||||
if(oneArray == null) {
|
||||
oneArray = new Object[1];
|
||||
}//if//
|
||||
|
||||
oneArray[0] = row;
|
||||
result = getHandler().invokeMethod(associationNumber, getValueHolder().getValue(), oneArray, IAssociationHandler.INVOKE_ORIGINAL_VALUE_GETTER_METHOD_FLAG);
|
||||
oneArray[0] = null;
|
||||
}//if//
|
||||
else {
|
||||
result = getHandler().invokeMethod(associationNumber, row, null, IAssociationHandler.INVOKE_ORIGINAL_VALUE_GETTER_METHOD_FLAG);
|
||||
}//else//
|
||||
|
||||
result = convertValueToControl(result);
|
||||
}//if//
|
||||
|
||||
return result;
|
||||
}//getOriginalResult()//
|
||||
/**
|
||||
* Sets the new result of the association into the model.
|
||||
* @param rowData The row object that is the input to the association.
|
||||
* @param result The new result which will be placed in the model.
|
||||
*/
|
||||
public void setResult(MultiAssociation.RowData rowData, Object result) {
|
||||
int associationNumber = getAssociationNumber();
|
||||
|
||||
//Ignore the short-circuit and fixed value setups.//
|
||||
if(associationNumber != -1 && associationNumber != -2) {
|
||||
result = convertValueToModel(result);
|
||||
|
||||
if(getIsValueHolderAssociated()) {
|
||||
if(twoArray == null) {
|
||||
twoArray = new Object[2];
|
||||
}//if//
|
||||
|
||||
twoArray[0] = rowData.getRow();
|
||||
twoArray[1] = result;
|
||||
getHandler().invokeMethod(associationNumber, getValueHolder().getValue(), twoArray, IAssociationHandler.INVOKE_SETTER_METHOD_FLAG);
|
||||
twoArray[0] = null;
|
||||
twoArray[1] = null;
|
||||
}//if//
|
||||
else {
|
||||
if(oneArray == null) {
|
||||
oneArray = new Object[1];
|
||||
}//if//
|
||||
|
||||
oneArray[0] = result;
|
||||
getHandler().invokeMethod(associationNumber, rowData.getRow(), oneArray, IAssociationHandler.INVOKE_SETTER_METHOD_FLAG);
|
||||
oneArray[0] = null;
|
||||
}//else//
|
||||
}//if//
|
||||
}//setResult()//
|
||||
/**
|
||||
* Called by the event handlers to update the association state when the result may have altered.
|
||||
* @param rowData The row data for the row being updated.
|
||||
* @param eventFlags The event flags that for the event that triggered the update. This will only be non-zero if an event triggered the call and the event was either an original value changed or cleared event.
|
||||
*/
|
||||
protected void updateAssociation(MultiAssociation.RowData rowData, int eventFlags) {
|
||||
if(EventSupport.isOriginalValueChanged(eventFlags) || EventSupport.isOriginalValueCleared(eventFlags)) {
|
||||
((MultiAssociationContainer) getAssociationContainer()).updateAssociations(rowData, null, eventFlags);
|
||||
}//if//
|
||||
else {
|
||||
Object row = rowData.row;
|
||||
Object newResult = getResult(row);
|
||||
|
||||
if(newResult != rowData.result) {
|
||||
Object oldResult = rowData.result;
|
||||
|
||||
unregisterListeners(row, rowData);
|
||||
rowData.result = newResult;
|
||||
registerListeners(row, rowData);
|
||||
((MultiAssociationContainer) getAssociationContainer()).updateAssociations(rowData, oldResult, eventFlags);
|
||||
}//if//
|
||||
else {
|
||||
//Always re-register all the extended listeners since they cannot handle attribute changes on their own.//
|
||||
unregisterListeners(row, rowData);
|
||||
registerListeners(row, rowData);
|
||||
}//else//
|
||||
}//else//
|
||||
}//updateAssociation()//
|
||||
/**
|
||||
* Registers a value with the association for use by a single association.
|
||||
* @param row The next value in the chain of associations for the single association.
|
||||
* @param rowData The array of row data used by the associations to store depth specific data. This association may only access the index at its depth.
|
||||
*/
|
||||
public MultiAssociation.RowData register(Object row, MultiAssociationContainer.RowDataContainer rowDataContainer) {
|
||||
//TODO: We could optimize this for the case where getAssociationNumber() == -1 or -2 (indicating that the row is the result [-1], or there is a fixed value for the result [-2]).
|
||||
return register(row, rowDataContainer, getResult(row));
|
||||
}//register()//
|
||||
/**
|
||||
* Registers a value with the association for use by a single association.
|
||||
* @param row The next value in the chain of associations for the single association.
|
||||
* @param rowDataContainer The container row data used by the associations to store data.
|
||||
* @param result The result for the row.
|
||||
*/
|
||||
protected MultiAssociation.RowData register(Object row, RowDataContainer rowDataContainer, Object result) {
|
||||
RowData rowData = new RowData(this, row, rowDataContainer, result);
|
||||
|
||||
if(events != null) {
|
||||
//Register all events.//
|
||||
for(int index = 0; index < events.length; index++) {
|
||||
EventAssociation eventAssociation = (EventAssociation) events[index];
|
||||
|
||||
if(eventAssociation.getIsValueHolderAssociated()) {
|
||||
//Register for the value holder's value changing and for the event on the held value (uses the value holder's event support).//
|
||||
eventAssociation.register();
|
||||
}//if//
|
||||
else if(row instanceof IEventEmitter) {
|
||||
//Register with the event on the row object using the resource association's event support.//
|
||||
rowDataContainer.getEventSupport().register((IEventEmitter) row, eventAssociation.getEventNumber(), rowData, true);
|
||||
}//else//
|
||||
}//for//
|
||||
}//if//
|
||||
|
||||
//Register the listener with the value holder.//
|
||||
if(getIsValueHolderAssociated()) {
|
||||
//Note: The value holder maintains a counter so we don't have to. This can be called multiple times without any negative effects.//
|
||||
getValueHolder().registerListener(this);
|
||||
}//if//
|
||||
|
||||
if(rowDataSet == null) {
|
||||
rowDataSet = new LiteHashSet(100, LiteHashSet.DEFAULT_LOAD_FACTOR, LiteHashSet.DEFAULT_COMPARATOR, LiteHashSet.STYLE_COUNT_DUPLICATES);
|
||||
}//if//
|
||||
|
||||
rowDataSet.add(rowData);
|
||||
registerListeners(row, rowData);
|
||||
|
||||
return rowData;
|
||||
}//register()//
|
||||
/**
|
||||
* Registers listeners with the result.
|
||||
* @param row The association's row to register all extended listeners with.
|
||||
* @param rowData The data for the row that is registering.
|
||||
*/
|
||||
protected void registerListeners(Object row, RowData rowData) {
|
||||
AssociationAttribute[] attributes = getAttributes();
|
||||
|
||||
if(attributes != null) {
|
||||
IHashSet trackedValues = new LiteHashSet(100);
|
||||
|
||||
if(rowData.nodeEventSupport == null) {
|
||||
rowData.nodeEventSupport = new EventSupport(null);
|
||||
}//if//
|
||||
|
||||
//TODO: It would be more efficient in some cases if we detected duplicate results for different row datas and had the events go to all affected row data's while only registering once.
|
||||
//This idea could turn out to be less efficient than the current design since it would require a more complex data structure.
|
||||
|
||||
for(int index = 0; index < attributes.length; index++) {
|
||||
Object attributeValue = attributes[index].getValue(row);
|
||||
|
||||
if(!trackedValues.containsValue(attributeValue)) {
|
||||
trackedValues.add(attributeValue);
|
||||
|
||||
//TODO: Should we support other collections? If so we would need to be notified when there are changes...
|
||||
if(attributeValue instanceof IManagedCollection) {
|
||||
IIterator iterator = ((IManagedCollection) attributeValue).iterator();
|
||||
|
||||
//Register for changes in the collected values.//
|
||||
while(iterator.hasNext()) {
|
||||
registerListeners(rowData.nodeEventSupport, trackedValues, rowData, iterator.next());
|
||||
}//while//
|
||||
|
||||
//Register for changes in the collection.//
|
||||
rowData.nodeEventSupport.register((IManagedCollection) attributeValue, IManagedCollection.EVENT, rowData, true);
|
||||
}//if//
|
||||
else {
|
||||
//Register for changes in the attribute value.//
|
||||
registerListeners(rowData.nodeEventSupport, trackedValues, rowData, attributeValue);
|
||||
}//else//
|
||||
}//if//
|
||||
}//for//
|
||||
}//if//
|
||||
}//registerListeners()//
|
||||
/**
|
||||
* Unregisters the row.
|
||||
* @param rowData The row data that was generated by the preceeding registeration.
|
||||
*/
|
||||
public void unregister(MultiAssociation.RowData rowData) {
|
||||
boolean removeFromRowDataSet = false;
|
||||
|
||||
if(events != null) {
|
||||
//Unregister all events.//
|
||||
for(int index = 0; index < events.length; index++) {
|
||||
EventAssociation eventAssociation = (EventAssociation) events[index];
|
||||
|
||||
if(eventAssociation.getIsValueHolderAssociated()) {
|
||||
//Register for the value holder's value changing and for the event on the held value (uses the value holder's event support).//
|
||||
eventAssociation.unregister();
|
||||
removeFromRowDataSet = true;
|
||||
}//if//
|
||||
else if(rowData.getRow() instanceof IEventEmitter) {
|
||||
//Register with the event on the row object using the resource association's event support.//
|
||||
rowData.getContainer().getEventSupport().unregister((IEventEmitter) rowData.getRow(), eventAssociation.getEventNumber(), rowData);
|
||||
}//else//
|
||||
}//for//
|
||||
}//if//
|
||||
|
||||
//Unregister the listener with the value holder.//
|
||||
if(getIsValueHolderAssociated()) {
|
||||
//Note: The value holder uses a counter so we don't have to.//
|
||||
getValueHolder().unregisterListener(this);
|
||||
removeFromRowDataSet = true;
|
||||
}//if//
|
||||
|
||||
if(removeFromRowDataSet) {
|
||||
rowDataSet.remove(rowData);
|
||||
}//if//
|
||||
|
||||
unregisterListeners(rowData.getRow(), rowData);
|
||||
}//unregister()//
|
||||
/**
|
||||
* Unregisters listeners with the result.
|
||||
* @param row The association's result to unregister all extended listeners with.
|
||||
* @param rowData The data for the row that is unregistering.
|
||||
*/
|
||||
protected void unregisterListeners(Object row, RowData rowData) {
|
||||
AssociationAttribute[] attributes = getAttributes();
|
||||
|
||||
if(attributes != null) {
|
||||
rowData.nodeEventSupport.unregisterAll();
|
||||
}//if//
|
||||
}//unregisterListeners()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.view.IEventAssociationChangeListener#onEventFired(com.foundation.view.IEventAssociation, java.lang.Object[])
|
||||
*/
|
||||
public void onEventFired(IEventAssociation eventAssociation, Object[] eventArguments) {
|
||||
//Called when an event is fired from a value holder. In this case all rows are affected by the event.//
|
||||
if(rowDataSet != null) {
|
||||
for(IIterator iterator = rowDataSet.iterator(); iterator.hasNext();) {
|
||||
updateAssociation((RowData) iterator.next(), 0);
|
||||
}//for//
|
||||
}//if//
|
||||
}//onEventFired()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.view.IValueHolderListener#heldValueChanged()
|
||||
*/
|
||||
public void heldValueChanged() {
|
||||
//Called when a value holder, that returned the result given the row value, has changed its value.//
|
||||
IIterator iterator = rowDataSet.iterator();
|
||||
|
||||
while(iterator.hasNext()) {
|
||||
updateAssociation((RowData) iterator.next(), 0);
|
||||
}//while//
|
||||
}//heldValueChanged()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.view.Association#initialize(com.foundation.view.AssociationContainer)
|
||||
*/
|
||||
public void initialize(AssociationContainer associationContainer) {
|
||||
super.initialize(associationContainer);
|
||||
}//initialize()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.view.Association#release()
|
||||
*/
|
||||
public void release() {
|
||||
super.release();
|
||||
}//release()//
|
||||
}//MultiAssociation//
|
||||
@@ -0,0 +1,326 @@
|
||||
/*
|
||||
* Copyright (c) 2006,2009 Declarative Engineering LLC.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Declarative Engineering LLC
|
||||
* verson 1 which accompanies this distribution, and is available at
|
||||
* http://declarativeengineering.com/legal/DE_Developer_License_v1.txt
|
||||
*/
|
||||
package com.foundation.view;
|
||||
|
||||
import com.common.debug.Debug;
|
||||
import com.common.util.IIterator;
|
||||
import com.common.util.LiteHashMap;
|
||||
import com.foundation.event.EventSupport;
|
||||
|
||||
public class MultiAssociationContainer extends AssociationContainer {
|
||||
/** The associations that make up the root of the tree. */
|
||||
private MultiAssociation[] associations = null;
|
||||
/** The map of RowDataContainer's indexed by the initial row value. */
|
||||
private LiteHashMap associationData = new LiteHashMap(1000);
|
||||
|
||||
/**
|
||||
* The row data base class.
|
||||
*/
|
||||
public abstract static class RowData {
|
||||
private MultiAssociation association = null;
|
||||
|
||||
/**
|
||||
* RowData constructor.
|
||||
* @param association The association that is being serviced by the row data.
|
||||
*/
|
||||
public RowData(MultiAssociation association) {
|
||||
this.association = association;
|
||||
}//RowData()//
|
||||
/**
|
||||
* Gets the association that is being serviced by the row data.
|
||||
* @return The serviced association.
|
||||
*/
|
||||
public MultiAssociation getAssociation() {
|
||||
return association;
|
||||
}//getAssociation()//
|
||||
}//RowData//
|
||||
|
||||
public static class RowDataContainer {
|
||||
private MultiAssociation.RowData first = null;
|
||||
private Object initialRowValue = null;
|
||||
/** The event support used for this row. */
|
||||
private EventSupport eventSupport = null;
|
||||
/** The user defined row related data. */
|
||||
private Object data;
|
||||
|
||||
/**
|
||||
* RowDataContainer constructor.
|
||||
* @param initialRowValue The initial row value that the container represents data for.
|
||||
* @param data The user defined row related data.
|
||||
*/
|
||||
public RowDataContainer(Object initialRowValue, Object data) {
|
||||
this.initialRowValue = initialRowValue;
|
||||
this.data = data;
|
||||
}//RowDataContainer()//
|
||||
/**
|
||||
* Gets the event support object for this row.
|
||||
* @return The support object that manages event registrations.
|
||||
*/
|
||||
public EventSupport getEventSupport() {
|
||||
return eventSupport != null ? eventSupport : (eventSupport = new EventSupport(null));
|
||||
}//getEventSupport()//
|
||||
/**
|
||||
* Gets the first node to be navigated to in the tree for the initial row value.
|
||||
* @return The first navigated multi association for the row value.
|
||||
*/
|
||||
protected MultiAssociation.RowData getFirstNode() {
|
||||
return first;
|
||||
}//getFirstNode()//
|
||||
/**
|
||||
* Gets the first node to be navigated to in the tree for the initial row value.
|
||||
* @return The first navigated multi association for the row value.
|
||||
*/
|
||||
protected MultiAssociation.RowData getLastNode() {
|
||||
MultiAssociation.RowData next = first;
|
||||
|
||||
while(next.getNext() != null) {
|
||||
next = next.getNext();
|
||||
}//while//
|
||||
|
||||
return next;
|
||||
}//getFirstNode()//
|
||||
/**
|
||||
* Gets the row value that the MultiAssociationContainer was originally passed and which generated this container.
|
||||
* @return The row value in the control which the association is getting a result for.
|
||||
*/
|
||||
public Object getInitialRowValue() {
|
||||
return initialRowValue;
|
||||
}//getInitialRowValue()//
|
||||
}//RowDataContainer//
|
||||
/**
|
||||
* AssociationContainer constructor.
|
||||
* @param associations The set of associations used by the container.
|
||||
*/
|
||||
public MultiAssociationContainer(MultiAssociation[] associations) {
|
||||
this.associations = associations;
|
||||
}//AssociationContainer()//
|
||||
/**
|
||||
* Registers a row with the associations.
|
||||
* @param row The row to be registered.
|
||||
* @param data The user data passed when sending event notifications.
|
||||
*/
|
||||
public void register(Object row, Object data) {
|
||||
//TODO: We could optimize this for the case where there is only one level of association and association.getAssociationNumber() == -1 or -2 (indicating that the row is the result [-1], or there is a fixed value for the result [-2]).
|
||||
MultiAssociation association = null;
|
||||
MultiAssociation.RowData rowData = null;
|
||||
|
||||
//Find the first applicable association.//
|
||||
for(int index = 0; association == null && index < associations.length; index++) {
|
||||
if(associations[index].isApplicable(row)) {
|
||||
association = associations[index];
|
||||
}//if//
|
||||
}//for//
|
||||
|
||||
//If an applicable association was found then register the row with it.//
|
||||
if(association != null) {
|
||||
RowDataContainer rowDataContainer = new RowDataContainer(row, data);
|
||||
|
||||
associationData.put(row, rowDataContainer);
|
||||
rowData = association.register(row, rowDataContainer);
|
||||
rowDataContainer.first = rowData;
|
||||
}//if//
|
||||
|
||||
//Register with the child associations until we find a target association.//
|
||||
while((association != null) && (!association.isTargetAssociation())) {
|
||||
MultiAssociation next = null;
|
||||
MultiAssociation.RowData nextRowData = null;
|
||||
Object result = rowData.getResult();
|
||||
|
||||
for(int index = 0; next == null && index < associations.length; index++) {
|
||||
if(associations[index].isApplicable(result)) {
|
||||
next = associations[index];
|
||||
}//if//
|
||||
}//for//
|
||||
|
||||
if(next != null) {
|
||||
nextRowData = next.register(result, rowData.getContainer());
|
||||
rowData.setNext(nextRowData);
|
||||
nextRowData.setPrevious(rowData);
|
||||
}//if//
|
||||
|
||||
rowData = nextRowData;
|
||||
association = next;
|
||||
}//while//
|
||||
|
||||
//Notify the MultiResourceAssociation.//
|
||||
((MultiResourceAssociation) getResourceAssociation()).modelChanged(row, data, false, 0);
|
||||
}//register()//
|
||||
/**
|
||||
* Unregisters a row with the associations.
|
||||
* @param row The row to be unregistered.
|
||||
*/
|
||||
public void unregister(Object row) {
|
||||
RowDataContainer container = (RowDataContainer) associationData.remove(row);
|
||||
|
||||
if(container != null) {
|
||||
MultiAssociation.RowData rowData = container.getFirstNode();
|
||||
|
||||
if(rowData != null) {
|
||||
unregister(rowData, row);
|
||||
}//if//
|
||||
|
||||
if(container.eventSupport != null) {
|
||||
container.eventSupport.unregisterAll();
|
||||
}//if//
|
||||
}//if//
|
||||
}//unregister()//
|
||||
/**
|
||||
* Recursively unregisters associations.
|
||||
* @param rowData The association row data to unregister.
|
||||
* @param value The previous association's value which is the given association's input.
|
||||
*/
|
||||
protected void unregister(MultiAssociation.RowData rowData, Object value) {
|
||||
if(rowData.getNext() != null) {
|
||||
unregister(rowData.getNext(), rowData.getResult());
|
||||
rowData.getNext().setPrevious(null);
|
||||
rowData.setNext(null);
|
||||
}//if//
|
||||
|
||||
rowData.getAssociation().unregister(rowData);
|
||||
}//unregister()//
|
||||
/**
|
||||
* Gets the last association node in the chain to the result.
|
||||
* @return The last association which produces the result. This will be null if there is no input, or no path to a target association.
|
||||
*/
|
||||
public MultiAssociation.RowData getEndAssociation(Object row) {
|
||||
MultiAssociation.RowData result = null;
|
||||
RowDataContainer rowDataContainer = (RowDataContainer) associationData.get(row);
|
||||
|
||||
if(rowDataContainer != null) {
|
||||
result = rowDataContainer.getLastNode();
|
||||
}//if//
|
||||
|
||||
return result;
|
||||
}//getEndAssociation()//
|
||||
/**
|
||||
* Gets the value in the model.
|
||||
* @param row The row whose model value is required.
|
||||
* @return The value the row resolves to. This will be NO_VALUE if the row does not resolve to a target association.
|
||||
*/
|
||||
public Object getValue(Object row) {
|
||||
Object result = NO_VALUE;
|
||||
RowDataContainer rowDataContainer = (RowDataContainer) associationData.get(row);
|
||||
|
||||
//The row data container will be null if the row did not result in an association match.//
|
||||
if(rowDataContainer != null) {
|
||||
MultiAssociation.RowData rowData = rowDataContainer.getLastNode();
|
||||
|
||||
if(rowData.getAssociation().isTargetAssociation()) {
|
||||
result = rowData.getResult();
|
||||
}//if//
|
||||
}//if//
|
||||
|
||||
return result;
|
||||
}//getValue()//
|
||||
/**
|
||||
* Sets the value in the model.
|
||||
* @param row The row whose value will be assigned.
|
||||
* @param value The value to be set in the model.
|
||||
*/
|
||||
public void setValue(Object row, Object value) {
|
||||
RowDataContainer rowDataContainer = (RowDataContainer) associationData.get(row);
|
||||
MultiAssociation.RowData rowData = rowDataContainer.getLastNode();
|
||||
|
||||
if(rowData.getAssociation().isTargetAssociation()) {
|
||||
rowData.getAssociation().setResult(rowData, value);
|
||||
}//if//
|
||||
}//setValue()//
|
||||
/**
|
||||
* Gets the original result from the target association.
|
||||
* @param row The row whose target association will provide an original value (the value before the current user changed it without applying the changes).
|
||||
* @return The original value for the association and the given row object.
|
||||
*/
|
||||
public Object getOriginalResult(Object row) {
|
||||
Object result = null;
|
||||
RowDataContainer rowDataContainer = (RowDataContainer) associationData.get(row);
|
||||
MultiAssociation.RowData rowData = rowDataContainer.getLastNode();
|
||||
|
||||
if(rowData.getAssociation().isTargetAssociation()) {
|
||||
result = rowData.getAssociation().getOriginalResult(rowData.getRow());
|
||||
}//if//
|
||||
|
||||
return result;
|
||||
}//getOriginalResult()//
|
||||
/**
|
||||
* Initializes the container of associations.
|
||||
*/
|
||||
public void initialize() {
|
||||
for(int index = 0; index < associations.length; index++) {
|
||||
associations[index].initialize(this);
|
||||
}//for//
|
||||
}//initialize()//
|
||||
/**
|
||||
* Releases the container of associations.
|
||||
*/
|
||||
public void release() {
|
||||
IIterator iterator = associationData.keyIterator();
|
||||
|
||||
//TODO: We may want to release the event support defined by the row data containers as a precaution.
|
||||
|
||||
//Unregister all the previously registered rows.//
|
||||
while(iterator.hasNext()) {
|
||||
unregister(iterator.next());
|
||||
}//while//
|
||||
|
||||
//Release all the associations.//
|
||||
for(int index = 0; index < associations.length; index++) {
|
||||
associations[index].release();
|
||||
}//for//
|
||||
}//release()//
|
||||
/**
|
||||
* Updates the associations after one of the associations alters its value.
|
||||
* @param rowData The association row data that altered its value.
|
||||
* @param oldValue The previous value for the given association.
|
||||
* @param eventFlags The event flags that for the event that triggered the update. This will only be non-zero if an event triggered the call and the event was either an original value changed or cleared event.
|
||||
*/
|
||||
public void updateAssociations(MultiAssociation.RowData rowData, Object oldValue, int eventFlags) {
|
||||
Object row = null;
|
||||
Object rowContainerData = rowData.getContainer().data;
|
||||
|
||||
if(!EventSupport.isOriginalValueChanged(eventFlags) && !EventSupport.isOriginalValueCleared(eventFlags)) {
|
||||
if(rowData.getNext() != null) {
|
||||
unregister(rowData.getNext(), oldValue);
|
||||
}//if//
|
||||
|
||||
//Register with the child associations until we find a target association.//
|
||||
while((rowData != null) && (!rowData.getAssociation().isTargetAssociation())) {
|
||||
MultiAssociation next = null;
|
||||
MultiAssociation.RowData nextRowData = null;
|
||||
Object result = rowData.getResult();
|
||||
|
||||
for(int index = 0; next == null && index < associations.length; index++) {
|
||||
if(associations[index].isApplicable(result)) {
|
||||
next = associations[index];
|
||||
}//if//
|
||||
}//for//
|
||||
|
||||
if(next != null) {
|
||||
nextRowData = next.register(result, rowData.getContainer());
|
||||
rowData.setNext(nextRowData);
|
||||
nextRowData.setPrevious(rowData);
|
||||
}//if//
|
||||
|
||||
rowData = nextRowData;
|
||||
}//while//
|
||||
}//if//
|
||||
|
||||
if(rowData != null) {
|
||||
//Find the row data for the first association in the chain - so we can get the row that will be recognized by the resource association and control.//
|
||||
while(rowData.getPrevious() != null) {
|
||||
rowData = rowData.getPrevious();
|
||||
}//while//
|
||||
|
||||
//Get the row that will identify the data to be updated by the control & resource association.//
|
||||
row = rowData.getRow();
|
||||
}//if//
|
||||
|
||||
//Note: The rowData may be null here if a target association couldn't be found for the resource association. The target's row container data will always be the same as the one returned by the rowData reference passed to this method.//
|
||||
//Notify the MultiResourceAssociation.//
|
||||
((MultiResourceAssociation) getResourceAssociation()).modelChanged(row, rowContainerData, true, eventFlags);
|
||||
}//updateAssociations()//
|
||||
}//AssociationContainer//
|
||||
549
Foundation/src/com/foundation/view/MultiResourceAssociation.java
Normal file
549
Foundation/src/com/foundation/view/MultiResourceAssociation.java
Normal file
@@ -0,0 +1,549 @@
|
||||
/*
|
||||
* Copyright (c) 2005,2009 Declarative Engineering LLC.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Declarative Engineering LLC
|
||||
* verson 1 which accompanies this distribution, and is available at
|
||||
* http://declarativeengineering.com/legal/DE_Developer_License_v1.txt
|
||||
*/
|
||||
package com.foundation.view;
|
||||
|
||||
import com.common.comparison.Comparator;
|
||||
import com.common.util.IIterator;
|
||||
import com.common.util.LiteHashMap;
|
||||
import com.foundation.controller.IDecorationListener;
|
||||
import com.foundation.event.EventSupport;
|
||||
import com.foundation.metadata.Attribute;
|
||||
|
||||
public class MultiResourceAssociation extends ResourceAssociation implements IMultiResourceAssociation {
|
||||
/** The container of association metadata. */
|
||||
private MultiAssociationContainer associationContainer = null;
|
||||
/** The listener which receives change notifications. */
|
||||
private IMultiResourceAssociationChangeListener changeListener = null;
|
||||
/** A mapping of item data objects indexed by the item they are representing. */
|
||||
private LiteHashMap itemDataByItemMap = new LiteHashMap(256);
|
||||
/** A flag indicating that the association is in the process of refreshing and should ignore update events. Update events may occur due to the model loading attribute values on demand. */
|
||||
private boolean isRefreshing = false;
|
||||
/** The initial value for the each item. */
|
||||
private Object initialValue = null;
|
||||
/** Whether this association should be linked to decorations adorning the object and attribute referencing the association result. */
|
||||
private boolean linkToDecorations = false;
|
||||
|
||||
/**
|
||||
* Encapsulates the resource/value association data for a single item.
|
||||
*/
|
||||
private class ItemData implements IDecorationListener {
|
||||
/** The item that this item data describes. */
|
||||
private Object item = null;
|
||||
/** The item's data object which will be passed with events. This can be any user defined value. */
|
||||
private Object data = null;
|
||||
/** The number of times the item has been registered. */
|
||||
private int registrationCounter = 1;
|
||||
/** The model's current value. */
|
||||
private Object currentValue = null;
|
||||
/** Whether the current value attribute is valid. This will be false if a target association was never found for the item. The current value may still be valid if there is a default value. */
|
||||
private boolean hasValue = false;
|
||||
/** The last known decoration object. */
|
||||
private Object decorationObject = null;
|
||||
/** The last known decoration attribute. */
|
||||
private Attribute decorationAttribute = null;
|
||||
|
||||
/**
|
||||
* ItemData constructor.
|
||||
* @param item The item being represented.
|
||||
* @param data The user defined data that is passed around to the event notifications.
|
||||
* @param initialValue The initial value for the item.
|
||||
*/
|
||||
public ItemData(Object item, Object data, Object initialValue) {
|
||||
this.item = item;
|
||||
this.data = data;
|
||||
this.currentValue = initialValue;
|
||||
}//ItemData()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.controller.IDecorationListener#addDecoration(com.foundation.view.AbstractDecoration)
|
||||
*/
|
||||
public void addDecoration(AbstractDecoration decoration) {
|
||||
MultiResourceAssociation.this.addDecoration(this, decoration);
|
||||
}//addDecoration()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.controller.IDecorationListener#removeDecoration(com.foundation.view.AbstractDecoration)
|
||||
*/
|
||||
public void removeDecoration(AbstractDecoration decoration) {
|
||||
MultiResourceAssociation.this.removeDecoration(this, decoration);
|
||||
}//removeDecoration()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.controller.IDecorationListener#getDecoratedAttribute()
|
||||
*/
|
||||
public Attribute getDecoratedAttribute() {
|
||||
return MultiResourceAssociation.this.getDecoratedAttribute(this);
|
||||
}//getDecoratedAttribute()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.controller.IDecorationListener#getDecoratedObject()
|
||||
*/
|
||||
public Object getDecoratedObject() {
|
||||
return MultiResourceAssociation.this.getDecoratedObject(this);
|
||||
}//getDecoratedObject()//
|
||||
}//ItemData//
|
||||
/**
|
||||
* MultiResourceAssociation constructor.
|
||||
* @param component The component that is using the resource association. This is used for debugging and for searching for value holders.
|
||||
* @param changeListener The listener which receives change notifications. This is often the component that is using this association.
|
||||
* @param viewContext The view context under which this association is being created. This will be used to access the view's resources.
|
||||
* @param valueType The type of value that the association is holding. This must be one of the TYPE_XXX identifiers defined in this class.
|
||||
* @param canSetValue Whether an altered value should be propegated back to the underlying object via the attribute if available, or set method if available.
|
||||
* @param initialValue The initial value which should be identical to the value that is default in the control. Example: Boolean.TRUE for the isVisible resource since a component is by default visible in the view.
|
||||
*/
|
||||
public MultiResourceAssociation(IAbstractComponent component, IMultiResourceAssociationChangeListener changeListener, IViewContext viewContext, int valueType, boolean canSetValue, Object initialValue) {
|
||||
super(component, viewContext, valueType, canSetValue, initialValue);
|
||||
|
||||
this.changeListener = changeListener;
|
||||
this.initialValue = initialValue;
|
||||
}//MultiResourceAssociation()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.view.ResourceAssociation#initialize()
|
||||
*/
|
||||
public void initialize() {
|
||||
initialize(false);
|
||||
}//initialize()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.view.ResourceAssociation#initialize()
|
||||
*/
|
||||
public void initialize(boolean linkToDecorations) {
|
||||
this.linkToDecorations = linkToDecorations;
|
||||
|
||||
if(associationContainer != null) {
|
||||
associationContainer.initialize();
|
||||
}//if//
|
||||
|
||||
super.initialize();
|
||||
}//initialize()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.view.ResourceAssociation#release()
|
||||
*/
|
||||
public void release() {
|
||||
if(linkToDecorations) {
|
||||
IIterator iterator = itemDataByItemMap.valueIterator();
|
||||
|
||||
while(iterator.hasNext()) {
|
||||
ItemData itemData = (ItemData) iterator.next();
|
||||
|
||||
if(itemData.decorationObject != null) {
|
||||
changeListener.getDecorationManager().unregisterDecorationListener(itemData, null, null);
|
||||
}//if//
|
||||
}//while//
|
||||
}//if//
|
||||
|
||||
if(associationContainer != null) {
|
||||
associationContainer.release();
|
||||
}//if//
|
||||
|
||||
super.release();
|
||||
}//release()//
|
||||
/**
|
||||
* Sets the associations.
|
||||
* @param associationContainer The container of associations.
|
||||
*/
|
||||
public void setAssociations(MultiAssociationContainer associationContainer) {
|
||||
//Note: Added this code to prevent changing the associations after initialization because allowing this makes the code way too complex and prevents use of the VariableResourceAssociation.//
|
||||
//TODO: Allow this... It will require sending control notifications for each row I think.
|
||||
if(isInitialized()) {
|
||||
throw new RuntimeException("Cannot set associations after the resource association has been initialized.");
|
||||
}//if//
|
||||
|
||||
if(this.associationContainer != null) {
|
||||
//TODO: Unregister all rows...
|
||||
this.associationContainer.release();
|
||||
}//if//
|
||||
|
||||
this.associationContainer = associationContainer;
|
||||
associationContainer.setResourceAssociation(this);
|
||||
|
||||
if(isInitialized()) {
|
||||
associationContainer.initialize();
|
||||
}//if//
|
||||
}//setAssociations()//
|
||||
/**
|
||||
* Determines whether the item is currently registered with the association.
|
||||
* @param item The item to test.
|
||||
* @return Whether the item is currently registered.
|
||||
*/
|
||||
public boolean isRegistered(Object item) {
|
||||
return !hasAssociations() || itemDataByItemMap.containsKey(item);
|
||||
}//isRegistered()//
|
||||
/**
|
||||
* Registers an item with the association.
|
||||
* <p>Items must be registered prior to getting their association value. When an item is no longer used it must be unregistered to free up memory.</p>
|
||||
* <p>Note: The same item may be registered multiple times, but the unregister method must be called an equal number of times to avoid memory leaks.</p>
|
||||
* @param item The user's item to be registered.
|
||||
* @deprecated
|
||||
*/
|
||||
public void registerItem(Object item) {
|
||||
registerItem(item, null);
|
||||
}//registerItem()//
|
||||
/**
|
||||
* Registers an item with the association.
|
||||
* <p>Items must be registered prior to getting their association value. When an item is no longer used it must be unregistered to free up memory.</p>
|
||||
* <p>Note: The same item may be registered multiple times, but the unregister method must be called an equal number of times to avoid memory leaks.</p>
|
||||
* @param item The user's item to be registered.
|
||||
* @param data The item's data object which will be passed with events. This can be any user defined value.
|
||||
*/
|
||||
public void registerItem(Object item, Object data) {
|
||||
//Optimize the case where no associations were defined.//
|
||||
if(hasAssociations()) {
|
||||
ItemData itemData = (ItemData) itemDataByItemMap.get(item);
|
||||
|
||||
if(itemData == null) {
|
||||
//Setup a new item data which will be accessed when the user refreshes or gets the value for the item.//
|
||||
itemData = new ItemData(item, data, initialValue);
|
||||
itemDataByItemMap.put(item, itemData);
|
||||
associationContainer.register(item, itemData);
|
||||
}//if//
|
||||
else {
|
||||
//Increment the counter so we know when to remove the data when unregistering.//
|
||||
itemData.registrationCounter++;
|
||||
}//else//
|
||||
}//if//
|
||||
}//registerItem()//
|
||||
/**
|
||||
* Unregisters an item from the association.
|
||||
* <p>The user must call this method once for every call to register an item.</p>
|
||||
* @param item The user's item to be unregistered.
|
||||
*/
|
||||
public void unregisterItem(Object item) {
|
||||
//Optimize the case where no associations were defined.//
|
||||
if(hasAssociations()) {
|
||||
ItemData itemData = (ItemData) itemDataByItemMap.get(item);
|
||||
|
||||
//Might occur while shutting down a view.//
|
||||
if(itemData != null) {
|
||||
//Reduce the counter by one so we can track how many times an items is un/registered.//
|
||||
itemData.registrationCounter--;
|
||||
|
||||
if(itemData.registrationCounter == 0) {
|
||||
//Remove the item data so we can GC it.//
|
||||
itemDataByItemMap.remove(item);
|
||||
associationContainer.unregister(item);
|
||||
itemData.item = null;
|
||||
itemData.currentValue = null;
|
||||
}//if//
|
||||
}//if//
|
||||
}//if//
|
||||
}//unregisterItem()//
|
||||
/**
|
||||
* Unregisters all items from the association.
|
||||
*/
|
||||
public void unregisterAllItems() {
|
||||
//Optimize the case where no associations were defined.//
|
||||
if(hasAssociations()) {
|
||||
IIterator iterator = itemDataByItemMap.valueIterator();
|
||||
|
||||
//Clean up all the items.//
|
||||
while(iterator.hasNext()) {
|
||||
ItemData itemData = (ItemData) iterator.next();
|
||||
|
||||
associationContainer.unregister(itemData.item);
|
||||
itemData.item = null;
|
||||
itemData.currentValue = null;
|
||||
}//while//
|
||||
|
||||
//Remove all the items.//
|
||||
itemDataByItemMap.removeAll();
|
||||
}//if//
|
||||
}//unregisterItem()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.view.ResourceAssociation#internalRelease()
|
||||
*/
|
||||
protected void internalRelease() {
|
||||
unregisterAllItems();
|
||||
}//internalRelease()//
|
||||
/**
|
||||
* Gets the listener that is receiving change notifications. This is normally the component using the resource association.
|
||||
* @return The listener which must be notified when the associated value is altered externally.
|
||||
*/
|
||||
protected IMultiResourceAssociationChangeListener getChangeListener() {
|
||||
return changeListener;
|
||||
}//getChangeListener()//
|
||||
/**
|
||||
* Gets the item data container for the given item.
|
||||
* @param item The item whose resource association data is required.
|
||||
* @return The item data for the item, or null if one could not be found.
|
||||
*/
|
||||
private ItemData getItemData(Object item) {
|
||||
return (ItemData) itemDataByItemMap.get(item);
|
||||
}//getItemData()//
|
||||
/**
|
||||
* Gets the value for the association.
|
||||
* @param item The item identifying which item data to use.
|
||||
* @return The association's result value. This could be null if the value is null.
|
||||
*/
|
||||
public final Object getValue(Object item) {
|
||||
Object result = initialValue;
|
||||
|
||||
//Optimize the case where no associations were defined.//
|
||||
if(hasAssociations()) {
|
||||
ItemData itemData = getItemData(item);
|
||||
|
||||
if(itemData != null) {
|
||||
result = itemData.currentValue;
|
||||
}//if//
|
||||
else {
|
||||
result = getDefaultValue();
|
||||
}//else//
|
||||
}//if//
|
||||
else if(getDefaultValue() != null) {
|
||||
result = getDefaultValue();
|
||||
}//else if//
|
||||
|
||||
return result;
|
||||
}//getValue()//
|
||||
/**
|
||||
* Determines whether the association has resolved to a value.
|
||||
* @param item The item identifying which item data to use.
|
||||
* @return Whether a value was determined for the association. This may be false if refresh was never called, or if the input did not resolve to a target value.
|
||||
*/
|
||||
public final boolean hasValue(Object item) {
|
||||
boolean result = false;
|
||||
|
||||
//Optimize the case where no associations were defined.//
|
||||
if(hasAssociations()) {
|
||||
result = getItemData(item).hasValue;
|
||||
}//if//
|
||||
else if(getDefaultValue() != null) {
|
||||
result = true;
|
||||
}//else if//
|
||||
|
||||
return result;
|
||||
}//hasValue()//
|
||||
/**
|
||||
* Sets the association value by passing the value to the model linked by the association. If there isn't a link then the value will just be stored locally.
|
||||
* @param item The item identifying which item data to use.
|
||||
* @param value The association's result value. This could be null if the value is null.
|
||||
*/
|
||||
public final void setValue(Object item, Object value) {
|
||||
if(hasAssociations()) {
|
||||
ItemData itemData = getItemData(item);
|
||||
IMultiResourceAssociationChangeListener listener = getChangeListener();
|
||||
|
||||
if(listener != null) {
|
||||
listener.addMessageHold();
|
||||
}//if//
|
||||
|
||||
//If the previous value was from a resource OR the current value does not equal the new value THEN update the current value in the model.//
|
||||
if(!Comparator.equals(value, itemData.currentValue)) {
|
||||
//Store the value as the current value for this resource association.//
|
||||
setValue(itemData, value);
|
||||
//Save the value in the attribute/method if there is a bound attribute or setter method and the values differ.//
|
||||
synchronizeValue(itemData, value);
|
||||
}//if//
|
||||
|
||||
if(listener != null) {
|
||||
listener.removeMessageHold();
|
||||
}//if//
|
||||
}//if//
|
||||
}//setValue()//
|
||||
/**
|
||||
* Sets the value for the association.
|
||||
* @param itemData The item data for the item in question.
|
||||
* @param value The static value as a string, the resource URL containing the value, or the properly typed static value.
|
||||
*/
|
||||
private void setValue(ItemData itemData, Object value) {
|
||||
//Set the new value.//
|
||||
itemData.currentValue = value;
|
||||
}//setResourceValue()//
|
||||
/**
|
||||
* Synchronizes the value with the model.
|
||||
* @param itemData The item data for the item in question.
|
||||
* @param value The value to be sent to the model.
|
||||
*/
|
||||
private void synchronizeValue(ItemData itemData, Object value) {
|
||||
if(canSetValue()) {
|
||||
associationContainer.setValue(itemData.item, value);
|
||||
}//if//
|
||||
}//synchronizeValue()//
|
||||
/**
|
||||
* Determines whether there are any associations possible.
|
||||
* @return Whether there is any association metadata defined.
|
||||
*/
|
||||
public boolean hasAssociations() {
|
||||
return associationContainer != null;
|
||||
}//hasAssociations()//
|
||||
/**
|
||||
* Determines whether there are any associations related to the given item.
|
||||
* If there are no associations then any call to refresh and getValues will never result in any data.
|
||||
* @param item The item to be checked.
|
||||
* @return Whether the given item will ever result in data.
|
||||
*/
|
||||
public boolean hasAssociations(Object item) {
|
||||
boolean result = false;
|
||||
|
||||
//Optimize the case where no associations were defined.//
|
||||
if(hasAssociations()) {
|
||||
ItemData itemData = getItemData(item);
|
||||
|
||||
result = itemData != null && itemData.hasValue;
|
||||
}//if//
|
||||
|
||||
return result;
|
||||
}//hasAssociations()//
|
||||
/**
|
||||
* TODO: Update this commenting...
|
||||
* Refreshes the resource association which will take all the settings into account to set the resource's value.
|
||||
* This method normally is called by the component when the component is notified that the related association's value has been altered.
|
||||
* Refresh should also be called by the component in the component's refresh method.
|
||||
* <p>No notification will be sent to the component as this is not considered an external stimulus.</p>
|
||||
* @param item The item identifying which item data to use.
|
||||
* @return Whether the resource value was updated. If it was then a call to getValue() will return the latest value.
|
||||
* @see #getValue(java.lang.Object)
|
||||
*/
|
||||
public boolean refresh(Object item) {
|
||||
boolean result = false;
|
||||
|
||||
//Optimize the case where no associations were defined.//
|
||||
if(hasAssociations()) {
|
||||
ItemData itemData = getItemData(item);
|
||||
|
||||
if(itemData != null) {
|
||||
Object value = associationContainer.getValue(item);
|
||||
|
||||
//TODO: This makes no sense... if the value indicates there isn't a target association then why synchronize the value?
|
||||
if(value == AssociationContainer.NO_VALUE) {
|
||||
try {
|
||||
isRefreshing = true;
|
||||
synchronizeValue(itemData, value);
|
||||
}//try//
|
||||
finally {
|
||||
isRefreshing = false;
|
||||
}//finally//
|
||||
}//if//
|
||||
|
||||
//Use the default value if one is available.//
|
||||
if((value == AssociationContainer.NO_VALUE || value == null) && getDefaultValue() != null) {
|
||||
value = getDefaultValue();
|
||||
}//if//
|
||||
|
||||
if(value == AssociationContainer.NO_VALUE) {
|
||||
//If the value indicates there isn't a value and the item data says there used to be a value then clear it and return true since the value has changed.//
|
||||
if(itemData.hasValue) {
|
||||
result = true;
|
||||
itemData.currentValue = null;
|
||||
itemData.hasValue = false;
|
||||
}//if//
|
||||
}//if//
|
||||
else if(!Comparator.equals(value, itemData.currentValue)) {
|
||||
result = true;
|
||||
itemData.currentValue = value;
|
||||
itemData.hasValue = true;
|
||||
}//else if//
|
||||
}//if//
|
||||
}//if//
|
||||
else if(getDefaultValue() != null) {
|
||||
//Return true always in this case since we really don't want to maintain item data for each value in the collection control.//
|
||||
result = true;
|
||||
}//else if//
|
||||
|
||||
return result;
|
||||
}//refresh()//
|
||||
/**
|
||||
* Called by the associations when the model has been altered.
|
||||
* @param row The row that has changed.
|
||||
* @param data The ItemData that was passed when registering the row.
|
||||
* @param updateView Whether the view should be updated.
|
||||
* @param eventFlags The event flags that for the event that triggered the update. This will only be non-zero if an event triggered the call and the event was either an original value changed or cleared event.
|
||||
*/
|
||||
public void modelChanged(Object row, Object data, boolean updateView, int eventFlags) {
|
||||
if(!isRefreshing) {
|
||||
ItemData itemData = (ItemData) data;
|
||||
MultiAssociation.RowData endAssociation = associationContainer.getEndAssociation(row);
|
||||
|
||||
//The itemData might be null if the modelChanged is called by an attribute changed event (usually happens) and a previous event handler uses the event to remove the row from the control (could happen depending on the order of event handler registration). Removing would remove the event handler, but since the event system copies the handler list when beginning an event firing, it wouldn't affect the set of events being notified by the active thread.//
|
||||
if(itemData != null) {
|
||||
if(itemData.decorationObject != null) {
|
||||
changeListener.getDecorationManager().unregisterDecorationListener(itemData, null, null);
|
||||
}//if//
|
||||
|
||||
if((linkToDecorations) && ((itemData.decorationObject != null) || (endAssociation != null && endAssociation.getAssociation().useDecorations())) && ((!Comparator.equals(itemData.decorationObject, endAssociation == null ? null : endAssociation.getRow())) || (!Comparator.equals(itemData.decorationAttribute, endAssociation == null ? null : endAssociation.getAssociation().getDecoratedAttribute())))) {
|
||||
Object oldDecorationObject = itemData.decorationObject;
|
||||
Attribute oldDecorationAttribute = itemData.decorationAttribute;
|
||||
|
||||
if((endAssociation.getAssociation() != null) && (endAssociation.getAssociation().useDecorations())) {
|
||||
itemData.decorationObject = endAssociation.getRow();
|
||||
itemData.decorationAttribute = endAssociation.getAssociation().getDecoratedAttribute();
|
||||
}//if//
|
||||
else {
|
||||
itemData.decorationObject = null;
|
||||
itemData.decorationAttribute = null;
|
||||
}//else//
|
||||
|
||||
if((oldDecorationObject != null) || (itemData.decorationObject != null)) {
|
||||
if(oldDecorationObject == null) {
|
||||
changeListener.getDecorationManager().registerDecorationListener(itemData);
|
||||
}//if//
|
||||
else if(itemData.decorationObject == null) {
|
||||
changeListener.getDecorationManager().unregisterDecorationListener(itemData, oldDecorationObject, oldDecorationAttribute);
|
||||
}//else if//
|
||||
else {
|
||||
changeListener.getDecorationManager().updateDecorationListener(itemData, oldDecorationObject, oldDecorationAttribute);
|
||||
}//else//
|
||||
}//if//
|
||||
}//if//
|
||||
|
||||
if(updateView) {
|
||||
//If the original value changed then the user has made changes to the model that over road another user's changes (made external to this view). We should notify the user of this via the control.//
|
||||
if(EventSupport.isOriginalValueChanged(eventFlags) || EventSupport.isOriginalValueCleared(eventFlags)) {
|
||||
getChangeListener().onModelExternallyChanged(this, row, itemData.data, EventSupport.isOriginalValueCleared(eventFlags), associationContainer.getOriginalResult(row));
|
||||
}//if//
|
||||
else {
|
||||
getChangeListener().onValueChanged(this, row, itemData.data, EventSupport.isReflectionUpdateChangeEvent(eventFlags));
|
||||
}//else//
|
||||
}//if//
|
||||
}//if//
|
||||
}//if//
|
||||
}//modelChanged()//
|
||||
/**
|
||||
* Notifies the listener that a relevant decoration has been added.
|
||||
* @param decoration The decoration added.
|
||||
*/
|
||||
public void addDecoration(ItemData itemData, AbstractDecoration decoration) {
|
||||
changeListener.addDecoration(this, itemData.item, itemData.data, decoration);
|
||||
}//addDecoration()//
|
||||
/**
|
||||
* Notifies the listener that a relevant decoration has been removed.
|
||||
* @param decoration The decoration removed.
|
||||
*/
|
||||
public void removeDecoration(ItemData itemData, AbstractDecoration decoration) {
|
||||
changeListener.removeDecoration(this, itemData.item, itemData.data, decoration);
|
||||
}//removeDecoration()//
|
||||
/**
|
||||
* Gets the object being decorated.
|
||||
* @return The object decorated.
|
||||
*/
|
||||
public Object getDecoratedObject(ItemData itemData) {
|
||||
MultiAssociation.RowData associationRowData = associationContainer.getEndAssociation(itemData.item);
|
||||
Object result = null;
|
||||
|
||||
if((associationRowData != null) && (associationRowData.getAssociation().useDecorations())) {
|
||||
result = associationRowData.getRow();
|
||||
}//if//
|
||||
|
||||
return result;
|
||||
}//getDecoratedObject()//
|
||||
/**
|
||||
* Gets the decorated attribute.
|
||||
* @return The attribute decorated, or null if the object will be decorated.
|
||||
*/
|
||||
public Attribute getDecoratedAttribute(ItemData itemData) {
|
||||
MultiAssociation.RowData associationRowData = associationContainer.getEndAssociation(itemData.item);
|
||||
Attribute result = null;
|
||||
|
||||
if((associationRowData != null) && (associationRowData.getAssociation().useDecorations())) {
|
||||
result = associationRowData.getAssociation().getDecoratedAttribute();
|
||||
}//if//
|
||||
|
||||
return result;
|
||||
}//getDecoratedAttribute()//
|
||||
/* (non-Javadoc)
|
||||
* @see java.lang.Object#toString()
|
||||
*/
|
||||
public String toString() {
|
||||
return "MultiResourceAssociation";
|
||||
}//toString()//
|
||||
}//MultiResourceAssociation//
|
||||
358
Foundation/src/com/foundation/view/ResourceAssociation.java
Normal file
358
Foundation/src/com/foundation/view/ResourceAssociation.java
Normal file
@@ -0,0 +1,358 @@
|
||||
/*
|
||||
* Copyright (c) 2005,2009 Declarative Engineering LLC.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Declarative Engineering LLC
|
||||
* verson 1 which accompanies this distribution, and is available at
|
||||
* http://declarativeengineering.com/legal/DE_Developer_License_v1.txt
|
||||
*/
|
||||
package com.foundation.view;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.text.DecimalFormat;
|
||||
import java.util.Collection;
|
||||
import java.util.Date;
|
||||
import com.common.debug.Debug;
|
||||
import com.common.util.ICollection;
|
||||
import com.foundation.view.resource.ResourceReference;
|
||||
|
||||
/**
|
||||
* The base class for an association between a control and the model.
|
||||
* This resource association manages the relationship by selecting the proper association based on the value-holder's held value, or the row data type.
|
||||
* The values are cached and events are passed through to the control, allowing the cache to be refreshed when necessary, and the view updated.
|
||||
*/
|
||||
public abstract class ResourceAssociation implements IResourceAssociationTypes, IResourceAssociation {
|
||||
/** A null value placeholder for the default value so we know if the user has set the default to null versus never being set. */
|
||||
private static final Object NULL_VALUE = new Object();
|
||||
/** A temporary formatting for numbers. */
|
||||
public final DecimalFormat defaultFormat = new DecimalFormat("###########################.###################");
|
||||
|
||||
/** The component reference which is used for debugging. */
|
||||
private IAbstractComponent component = null;
|
||||
/** The container from which the value holder searches will begin. */
|
||||
private IAbstractContainer container = null;
|
||||
/** The view context under which this association is being created. This will be used to access the view's resources. */
|
||||
private IViewContext viewContext = null;
|
||||
/** The optional default value used if there isn't an association or the value is null. */
|
||||
private Object defaultValue = NULL_VALUE;
|
||||
/** Whether the resource association has been initialized. This should occur in the component's initialization routine (after the attribute or default value has been set). */
|
||||
private boolean isInitialized = false;
|
||||
/** The type of resource. This will be one of the TYPE_ identifiers. */
|
||||
private int valueType = 0;
|
||||
/** Flags whether an altered value should be propegated back to the underlying object via the attribute if available, or set method if available. */
|
||||
private boolean canSetValue = true;
|
||||
/** The value to be used if there isn't a valid association and the default value was not set. */
|
||||
private Object noAssociationNoDefaultValue = null;
|
||||
/**
|
||||
* ResourceAssociation constructor.
|
||||
* @param component The component that is using the resource association. This is used for debugging and for searching for value holders.
|
||||
* @param viewContext The view context for the component creating the association.
|
||||
* @param valueType The type of value that the association will be getting or setting for the component.
|
||||
* @param canSetValue Whether the component will ever require the ability to set the value.
|
||||
*/
|
||||
public ResourceAssociation(IAbstractComponent component, IViewContext viewContext, int valueType, boolean canSetValue, Object noAssociationNoDefaultValue) {
|
||||
super();
|
||||
|
||||
if(valueType < TYPE_MIN || valueType > TYPE_MAX) {
|
||||
throw new RuntimeException("Invalid resource association value type.");
|
||||
}//if//
|
||||
else if(component == null) {
|
||||
throw new RuntimeException("Must pass a valid component reference.");
|
||||
}//else if//
|
||||
|
||||
this.component = component;
|
||||
this.container = (component instanceof IAbstractContainer) ? (IAbstractContainer) component : component.getContainer();
|
||||
this.viewContext = viewContext;
|
||||
this.valueType = valueType;
|
||||
this.canSetValue = canSetValue;
|
||||
this.noAssociationNoDefaultValue = noAssociationNoDefaultValue;
|
||||
}//ResourceAssociation()//
|
||||
/**
|
||||
* Gets the component that created the resource association.
|
||||
* @return The component that is using the resource association. This is used for debugging only.
|
||||
*/
|
||||
public IAbstractComponent getComponent() {
|
||||
return component;
|
||||
}//getComponent()//
|
||||
/**
|
||||
* Gets the container nearest to the component that created the resource association (it could be the component in some cases).
|
||||
* @return The container used to locate value holders.
|
||||
*/
|
||||
public IAbstractContainer getContainer() {
|
||||
return container;
|
||||
}//getContainer()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.view.IResourceAssociation#getDefaultValue()
|
||||
*/
|
||||
public Object getDefaultValue() {
|
||||
return defaultValue == NULL_VALUE ? noAssociationNoDefaultValue : defaultValue;
|
||||
}//getDefaultValue()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.view.IResourceAssociation#setDefaultValue(java.lang.Object)
|
||||
*/
|
||||
public void setDefaultValue(Object defaultValue) {
|
||||
defaultValue = convertValueToControl(defaultValue);
|
||||
|
||||
if(this.defaultValue != defaultValue) {
|
||||
this.defaultValue = defaultValue;
|
||||
|
||||
//TODO: If we have already initialized then we should update existing associations.
|
||||
//Note: Calling this after initialization is not currently supported.
|
||||
if(isInitialized()) {
|
||||
throw new RuntimeException("Cannot set the default value after the resource association has been initialized.");
|
||||
}//if//
|
||||
}//if//
|
||||
}//setDefaultValue()//
|
||||
/**
|
||||
* Gets the identifier for the type of value returned by this resource association.
|
||||
* @return The resource association's value type.
|
||||
*/
|
||||
public int getValueType() {
|
||||
return valueType;
|
||||
}//getValueType()//
|
||||
/**
|
||||
* Determines whether the association can push an altered value to the model.
|
||||
* @return Whether the association has the ability to modify the model when the value is changed via the view (or defaults to the default value).
|
||||
*/
|
||||
public boolean canSetValue() {
|
||||
return canSetValue;
|
||||
}//canSetValue()//
|
||||
/**
|
||||
* Converts the given value into a properly typed value for the control, otherwise returns null if that was not possible.
|
||||
* <p>Note: The default value for the association is converted in the ResourceAssociation class.</p>
|
||||
* @param value The value which may not match the display value type identifier.
|
||||
* @return The matching value for the display value type.
|
||||
*/
|
||||
protected Object convertValueToControl(Object value) {
|
||||
Object currentValue = null;
|
||||
|
||||
if(value instanceof ResourceReference) {
|
||||
currentValue = value;
|
||||
}//if//
|
||||
else {
|
||||
switch(getValueType()) {
|
||||
case TYPE_TEXT: {
|
||||
if(value instanceof Number) {
|
||||
currentValue = defaultFormat.format(value);
|
||||
}//if//
|
||||
else {
|
||||
currentValue = value != null ? value.toString() : null;
|
||||
}//else//
|
||||
break;
|
||||
}//case//
|
||||
case TYPE_BOOLEAN: {
|
||||
if(value instanceof Boolean) {
|
||||
currentValue = (Boolean) value;
|
||||
}//if//
|
||||
else if((value instanceof Byte) || (value instanceof Short) || (value instanceof Integer) || (value instanceof Long)) {
|
||||
currentValue = ((Number) value).longValue() == 0 ? Boolean.FALSE : Boolean.TRUE;
|
||||
}//else if//
|
||||
else if((value instanceof Float) || (value instanceof Double)) {
|
||||
currentValue = ((Number) value).doubleValue() == 0 ? Boolean.FALSE : Boolean.TRUE;
|
||||
}//else if//
|
||||
else if(value instanceof ICollection) {
|
||||
currentValue = ((ICollection) value).getSize() > 0 ? Boolean.TRUE : Boolean.FALSE;
|
||||
}//else if//
|
||||
else if(value instanceof Collection) {
|
||||
currentValue = ((Collection) value).size() > 0 ? Boolean.TRUE : Boolean.FALSE;
|
||||
}//else if//
|
||||
else {
|
||||
currentValue = value == null ? Boolean.FALSE : Boolean.TRUE;
|
||||
}//else//
|
||||
break;
|
||||
}//case//
|
||||
case TYPE_COLOR: {
|
||||
if(value instanceof JefColor) {
|
||||
currentValue = (JefColor) value;
|
||||
}//if//
|
||||
else if(value instanceof String) {
|
||||
currentValue = new JefColor((String) value);
|
||||
}//else if//
|
||||
break;
|
||||
}//case//
|
||||
case TYPE_FONT: {
|
||||
if(value instanceof JefFont[]) {
|
||||
currentValue = (JefFont[]) value;
|
||||
}//if//
|
||||
else if(value instanceof JefFont) {
|
||||
currentValue = new JefFont[] {(JefFont) value};
|
||||
}//else if//
|
||||
else if(value instanceof String) {
|
||||
currentValue = JefFont.getJefFonts((String) value);
|
||||
}//else if//
|
||||
break;
|
||||
}//case//
|
||||
case TYPE_IMAGE: {
|
||||
if(value instanceof JefImage) {
|
||||
currentValue = (JefImage) value;
|
||||
}//if//
|
||||
else if(value instanceof String) {
|
||||
currentValue = new JefImage((String) value);
|
||||
}//else if//
|
||||
break;
|
||||
}//case//
|
||||
case TYPE_IMAGES: {
|
||||
if(value instanceof JefImage[]) {
|
||||
currentValue = (JefImage[]) value;
|
||||
}//if//
|
||||
else if(value instanceof JefImage) {
|
||||
currentValue = new JefImage[] {(JefImage) value};
|
||||
}//else if//
|
||||
break;
|
||||
}//case//
|
||||
case TYPE_INTEGER: {
|
||||
if(value instanceof Integer) {
|
||||
currentValue = (Integer) value;
|
||||
}//if//
|
||||
else if(value instanceof String) {
|
||||
try {
|
||||
currentValue = Integer.valueOf((String) value);
|
||||
}//try//
|
||||
catch(Throwable e) {
|
||||
Debug.log(new RuntimeException("Could not convert the string '" + value.toString() + "' to an integer value."));
|
||||
}//catch//
|
||||
}//else if//
|
||||
else if(value instanceof Number) {
|
||||
currentValue = new Integer(((Number) value).intValue());
|
||||
}//else if//
|
||||
break;
|
||||
}//case//
|
||||
case TYPE_LONG: {
|
||||
if(value instanceof Long) {
|
||||
currentValue = (Long) value;
|
||||
}//if//
|
||||
else if(value instanceof String) {
|
||||
try {
|
||||
currentValue = Long.valueOf((String) value);
|
||||
}//try//
|
||||
catch(Throwable e) {
|
||||
Debug.log(new RuntimeException("Could not convert the string '" + value.toString() + "' to an long value."));
|
||||
}//catch//
|
||||
}//else if//
|
||||
else if(value instanceof Number) {
|
||||
currentValue = new Long(((Number) value).longValue());
|
||||
}//else if//
|
||||
break;
|
||||
}//case//
|
||||
case TYPE_OBJECT: {
|
||||
currentValue = value;
|
||||
break;
|
||||
}//case//
|
||||
case TYPE_FLOAT: {
|
||||
if(value instanceof Float) {
|
||||
currentValue = (Float) value;
|
||||
}//if//
|
||||
else if(value instanceof String) {
|
||||
try {
|
||||
currentValue = Float.valueOf((String) value);
|
||||
}//try//
|
||||
catch(Throwable e) {
|
||||
Debug.log(new RuntimeException("Could not convert the string '" + value.toString() + "' to a float value."));
|
||||
}//catch//
|
||||
}//else if//
|
||||
else if(value instanceof Number) {
|
||||
currentValue = new Float(((Number) value).floatValue());
|
||||
}//else if//
|
||||
break;
|
||||
}//case//
|
||||
case TYPE_DOUBLE: {
|
||||
if(value instanceof Double) {
|
||||
currentValue = (Double) value;
|
||||
}//if//
|
||||
else if(value instanceof String) {
|
||||
try {
|
||||
currentValue = Double.valueOf((String) value);
|
||||
}//try//
|
||||
catch(Throwable e) {
|
||||
Debug.log(new RuntimeException("Could not convert the string '" + value.toString() + "' to a double value."));
|
||||
}//catch//
|
||||
}//else if//
|
||||
else if(value instanceof Number) {
|
||||
currentValue = new Double(((Number) value).doubleValue());
|
||||
}//else if//
|
||||
break;
|
||||
}//case//
|
||||
case TYPE_DATE: {
|
||||
if(value instanceof Date) {
|
||||
currentValue = (Date) value;
|
||||
}//if//
|
||||
break;
|
||||
}//case//
|
||||
case TYPE_GRADIENT: {
|
||||
if(value instanceof JefColor) {
|
||||
currentValue = new JefGradient((JefColor) value, null, JefGradient.DIAGONAL);
|
||||
}//if//
|
||||
else if(value instanceof String) {
|
||||
currentValue = new JefGradient((String) value);
|
||||
}//else if//
|
||||
else if(value instanceof JefGradient) {
|
||||
currentValue = (JefGradient) value;
|
||||
}//else if//
|
||||
break;
|
||||
}//case//
|
||||
case TYPE_BIG_DECIMAL: {
|
||||
if(value instanceof BigDecimal) {
|
||||
currentValue = (BigDecimal) value;
|
||||
}//if//
|
||||
else {
|
||||
try {
|
||||
currentValue = new BigDecimal(value.toString());
|
||||
}//try//
|
||||
catch(Throwable e) {
|
||||
Debug.log(new RuntimeException("Could not convert the string '" + value.toString() + "' to a BigDecimal value."));
|
||||
}//catch//
|
||||
}//else//
|
||||
break;
|
||||
}//case//
|
||||
default: {
|
||||
throw new RuntimeException("Error: Invalid value type.");
|
||||
}//default//
|
||||
}//switch//
|
||||
}//else//
|
||||
|
||||
return currentValue;
|
||||
}//convertValueToControl()//
|
||||
/**
|
||||
* Gets the context for the view that created this association.
|
||||
* @return The view's context which can be used to obtain access to the view's resources.
|
||||
*/
|
||||
protected IViewContext getViewContext() {
|
||||
return viewContext;
|
||||
}//getViewContext()//
|
||||
/**
|
||||
* Determines whether the resource association has been initialized yet.
|
||||
* @return Whether the initialize method has been called. This will be reset to false after calling the release method.
|
||||
*/
|
||||
protected boolean isInitialized() {
|
||||
return isInitialized;
|
||||
}//isInitialized()//
|
||||
/**
|
||||
* Initializes the resource association which will take all the settings into account to initialize the resource's value.
|
||||
* <p>No notification will be sent to the component as this is not considered an external stimulus.</p>
|
||||
*/
|
||||
public void initialize() {
|
||||
if(!isInitialized) {
|
||||
//Set the flag indicating we have finished initializing.//
|
||||
isInitialized = true;
|
||||
}//if//
|
||||
}//initialize()//
|
||||
/**
|
||||
* Releases the resource association which will unregister any listeners and cleanup all resources.
|
||||
* <p>No notification will be sent to the component as this is not considered an external stimulus.</p>
|
||||
*/
|
||||
public void release() {
|
||||
if(isInitialized) {
|
||||
isInitialized = false;
|
||||
internalRelease();
|
||||
}//if//
|
||||
}//release()//
|
||||
/**
|
||||
* Releases the resource association listeners and resources.
|
||||
*/
|
||||
protected abstract void internalRelease();
|
||||
/**
|
||||
* Ensures that the current thread is the view system thread.
|
||||
*/
|
||||
public void verifyThread() {
|
||||
component.verifyThread();
|
||||
}//verifyThread()//
|
||||
}//ResourceAssociation//
|
||||
483
Foundation/src/com/foundation/view/SingleAssociation.java
Normal file
483
Foundation/src/com/foundation/view/SingleAssociation.java
Normal file
@@ -0,0 +1,483 @@
|
||||
/*
|
||||
* Copyright (c) 2006,2009 Declarative Engineering LLC.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Declarative Engineering LLC
|
||||
* verson 1 which accompanies this distribution, and is available at
|
||||
* http://declarativeengineering.com/legal/DE_Developer_License_v1.txt
|
||||
*/
|
||||
package com.foundation.view;
|
||||
|
||||
import com.common.util.IHashSet;
|
||||
import com.common.util.IIterator;
|
||||
import com.common.util.LiteHashSet;
|
||||
import com.foundation.event.EventSupport;
|
||||
import com.foundation.event.IEventEmitter;
|
||||
import com.foundation.event.IEventHandler;
|
||||
import com.foundation.metadata.Attribute;
|
||||
import com.foundation.util.IManagedCollection;
|
||||
import com.foundation.view.EventAssociation;
|
||||
import com.foundation.view.IAssociationHandler;
|
||||
import com.foundation.view.IEventAssociation;
|
||||
import com.foundation.view.IEventAssociationChangeListener;
|
||||
import com.foundation.view.IValueHolderListener;
|
||||
|
||||
/**
|
||||
* Models an association that results in one value.
|
||||
* This association can model target associations and link associations, and it can handle both attribute and method (indirect) associations.
|
||||
*/
|
||||
public class SingleAssociation extends Association implements IEventAssociationChangeListener, IEventHandler, IValueHolderListener {
|
||||
/** A simple flag that turns thread verification on and off. */
|
||||
private static final boolean VERIFY_THREAD = true;
|
||||
|
||||
/** The row input value that the result correponds to. */
|
||||
private Object row = null;
|
||||
/** The last known result for this association. */
|
||||
private Object result = null;
|
||||
/** The set of events that the association registers for when the input changes. If any of these events are triggered it indicates that this association's value has changed. */
|
||||
private EventAssociation[] events = null;
|
||||
/** The support object that tracks events registered via the AssociationNodes. This will only be non-null if association attribute and nodes were provided. */
|
||||
private EventSupport nodeEventSupport = null;
|
||||
/** Whether the association produces a target result. If this is false then the result will be feed back into the pool of associations in the container. */
|
||||
private boolean isTarget = false;
|
||||
/** The next association in the chain. This should always be null if this is a target association. */
|
||||
private SingleAssociation next = null;
|
||||
/** The attribute to be used to access decorations for the association. If this is null then the object its self will have the decorations. */
|
||||
private Attribute decoratedAttribute;
|
||||
/** Whether decorations should be accessed for the association. */
|
||||
private boolean useDecorations;
|
||||
/** The optional fixed value. This will only be used if the association identifier is -2. */
|
||||
private Object fixedValue;
|
||||
/**
|
||||
* SingleAssociation constructor.
|
||||
* @param rowType The type of row the association applies to. In the case of a root association in a single association this is the type held by the value holder.
|
||||
* @param fixedValue The value to use as the association result.
|
||||
* @param isTarget Whether this is a target association.
|
||||
*/
|
||||
public SingleAssociation(Class rowType, Object fixedValue, boolean isTarget) {
|
||||
this(rowType, -2, null, false, null, null, null, null, isTarget, null, false, fixedValue);
|
||||
}//SingleAssociation()//
|
||||
/**
|
||||
* SingleAssociation constructor.
|
||||
* @param rowType The type of row the association applies to. In the case of a root association in a single association this is the type held by the value holder.
|
||||
* @param associationNumber The association number which will be passed to the handler when the assocation is invoked. This number allows fast indexing of the methods that the handler handles. This value may be -1 if the input value should be used as the output value for the association (short circuiting). The value should be -2 for a fixed value result.
|
||||
* @param handler The object that handles the method invocations to get and set the value.
|
||||
* @param invertLogic Whether the result of the association will be inverted. This is ignored unless the result is a Boolean value.
|
||||
* @param attributes The attributes to listen to and whose values are passed to the nodes. This may be null.
|
||||
* @param nodes The nodes of the arbitrary tree of listeners for value change events for this association's result value. This may be null.
|
||||
* @param events The non-null array of events that will be used to listen to the supplied row object for changes in the result.
|
||||
* @param valueHolderName The name of the optional value holder whose value holds the getter and setter methods used by the association. If no value holder is defined then the row object passed to the association must define the getter and setter methods.
|
||||
* @param isTarget Whether this is a target association.
|
||||
*/
|
||||
public SingleAssociation(Class rowType, int associationNumber, IAssociationHandler handler, boolean invertLogic, AssociationAttribute[] attributes, AssociationNode[] nodes, EventAssociation[] events, String valueHolderName, boolean isTarget) {
|
||||
this(rowType, associationNumber, handler, invertLogic, attributes, nodes, events, valueHolderName, isTarget, null, false, null);
|
||||
|
||||
if(events == null) {
|
||||
throw new IllegalArgumentException("The events parameter may not be null.");
|
||||
}//if//
|
||||
}//SingleAssociation()//
|
||||
/**
|
||||
* SingleAssociation constructor.
|
||||
* @param rowType The type of row the association applies to. In the case of a root association in a single association this is the type held by the value holder.
|
||||
* @param associationNumber The association number which will be passed to the handler when the assocation is invoked. This number allows fast indexing of the methods that the handler handles. This value may be -1 if the input value should be used as the output value for the association (short circuiting). The value should be -2 for a fixed value result.
|
||||
* @param handler The object that handles the method invocations to get and set the value.
|
||||
* @param invertLogic Whether the result of the association will be inverted. This is ignored unless the result is a Boolean value.
|
||||
* @param attributes The attributes to listen to and whose values are passed to the nodes. This may be null.
|
||||
* @param nodes The nodes of the arbitrary tree of listeners for value change events for this association's result value. This may be null.
|
||||
* @param events The non-null array of events that will be used to listen to the supplied row object for changes in the result.
|
||||
* @param valueHolderName The name of the optional value holder whose value holds the getter and setter methods used by the association. If no value holder is defined then the row object passed to the association must define the getter and setter methods.
|
||||
* @param isTarget Whether this is a target association.
|
||||
* @param decoratedAttribute The attribute that the decorations will be attached to, or null if the object its self will be decorated.
|
||||
*/
|
||||
public SingleAssociation(Class rowType, int associationNumber, IAssociationHandler handler, boolean invertLogic, AssociationAttribute[] attributes, AssociationNode[] nodes, EventAssociation[] events, String valueHolderName, boolean isTarget, Attribute decoratedAttribute) {
|
||||
this(rowType, associationNumber, handler, invertLogic, attributes, nodes, events, valueHolderName, isTarget, decoratedAttribute, true, null);
|
||||
|
||||
if(events == null) {
|
||||
throw new IllegalArgumentException("The events parameter may not be null.");
|
||||
}//if//
|
||||
}//SingleAssociation()//
|
||||
/**
|
||||
* SingleAssociation constructor.
|
||||
* @param rowType The type of row the association applies to. In the case of a root association in a single association this is the type held by the value holder.
|
||||
* @param associationNumber The association number which will be passed to the handler when the assocation is invoked. This number allows fast indexing of the methods that the handler handles. This value may be -1 if the input value should be used as the output value for the association (short circuiting). The value should be -2 for a fixed value result.
|
||||
* @param handler The object that handles the method invocations to get and set the value.
|
||||
* @param invertLogic Whether the result of the association will be inverted. This is ignored unless the result is a Boolean value.
|
||||
* @param attributes The attributes to listen to and whose values are passed to the nodes. This may be null.
|
||||
* @param nodes The nodes of the arbitrary tree of listeners for value change events for this association's result value. This may be null.
|
||||
* @param children The child associations in search order. The children will be searched for the first match for this association's result. This should always be null for target associations, and should never be null for non-target associations.
|
||||
* @param events The non-null array of events that will be used to listen to the supplied row object for changes in the result.
|
||||
* @param valueHolderName The name of the optional value holder whose value holds the getter and setter methods used by the association. If no value holder is defined then the row object passed to the association must define the getter and setter methods.
|
||||
* @param decoratedAttribute The attribute that the decorations will be attached to, or null if the object its self will be decorated.
|
||||
* @param useDecorations Whether the association should link to the decorations mappings. This is required because the decorated attribute may be null and the association may still link to the decorations.
|
||||
* @param fixedValue The fixed value to use as a result. This is only utilized if the associationNumber == -2.
|
||||
*/
|
||||
protected SingleAssociation(Class rowType, int associationNumber, IAssociationHandler handler, boolean invertLogic, AssociationAttribute[] attributes, AssociationNode[] nodes, EventAssociation[] events, String valueHolderName, boolean isTarget, Attribute decoratedAttribute, boolean useDecorations, Object fixedValue) {
|
||||
super(rowType, associationNumber, handler, invertLogic, attributes, nodes, valueHolderName);
|
||||
|
||||
this.fixedValue = fixedValue;
|
||||
this.isTarget = isTarget;
|
||||
this.events = events;
|
||||
this.decoratedAttribute = decoratedAttribute;
|
||||
this.useDecorations = useDecorations;
|
||||
|
||||
if(events != null) {
|
||||
for(int index = 0; index < events.length; index++) {
|
||||
events[index].setChangeListener(this);
|
||||
}//for//
|
||||
}//if//
|
||||
}//SingleAssociation()//
|
||||
/**
|
||||
* Gets the row object which is referencing the association's result.
|
||||
* @return The object referencing the result.
|
||||
*/
|
||||
public Object getRow() {
|
||||
return row;
|
||||
}//getRow()//
|
||||
/**
|
||||
* Gets the attribute whose decorations are referenced by this association.
|
||||
* @return The decorated attribute for this association, or null if the row object its self will be decorated.
|
||||
*/
|
||||
public Attribute getDecoratedAttribute() {
|
||||
return decoratedAttribute;
|
||||
}//getDecoratedAttribute()//
|
||||
/**
|
||||
* Gets whether the association should listen for decorations.
|
||||
* @return Whether the association is linked to the model for accessing decorations.
|
||||
*/
|
||||
public boolean useDecorations() {
|
||||
return useDecorations;
|
||||
}//useDecorations()//
|
||||
/**
|
||||
* Gets the next association in the chain.
|
||||
* @return The next association in the chain.
|
||||
*/
|
||||
public SingleAssociation getNext() {
|
||||
return next;
|
||||
}//getNext()//
|
||||
/**
|
||||
* Sets the next association in the chain.
|
||||
* @param next The next association in the chain.
|
||||
*/
|
||||
public void setNext(SingleAssociation next) {
|
||||
this.next = next;
|
||||
}//setNext()//
|
||||
/**
|
||||
* Determines whether this is a target association meaning that its result is the value assigned to the initial association input.
|
||||
* @return Whether this association is a target and the result is not placed as input to another association.
|
||||
*/
|
||||
public boolean isTargetAssociation() {
|
||||
return isTarget;
|
||||
}//isTargetAssociation()//
|
||||
/**
|
||||
* Gets the latest result for the association.
|
||||
* @return The association's current result.
|
||||
*/
|
||||
public Object getResult() {
|
||||
return result;
|
||||
}//getResult()//
|
||||
/**
|
||||
* Gets the latest result for the association.
|
||||
* @return The association's current result.
|
||||
*/
|
||||
public void setResult(Object result) {
|
||||
int associationNumber = getAssociationNumber();
|
||||
|
||||
//Ignore the short-circuit and fixed value setups.//
|
||||
if(associationNumber != -1 && associationNumber != -2) {
|
||||
result = convertValueToModel(result);
|
||||
|
||||
if(getIsValueHolderAssociated()) {
|
||||
if(twoArray == null) {
|
||||
twoArray = new Object[2];
|
||||
}//if//
|
||||
|
||||
twoArray[0] = row;
|
||||
twoArray[1] = result;
|
||||
result = getHandler().invokeMethod(associationNumber, getValueHolder().getValue(), twoArray, IAssociationHandler.INVOKE_SETTER_METHOD_FLAG);
|
||||
twoArray[0] = null;
|
||||
twoArray[1] = null;
|
||||
}//if//
|
||||
else {
|
||||
if(oneArray == null) {
|
||||
oneArray = new Object[1];
|
||||
}//if//
|
||||
|
||||
oneArray[0] = result;
|
||||
getHandler().invokeMethod(associationNumber, row, oneArray, IAssociationHandler.INVOKE_SETTER_METHOD_FLAG);
|
||||
oneArray[0] = null;
|
||||
}//else//
|
||||
}//if//
|
||||
}//setResult()//
|
||||
/**
|
||||
* Gets the current result of the association from the model.
|
||||
* @param row The row object that is the input to the association.
|
||||
* @return The result of the association.
|
||||
*/
|
||||
protected Object getResult(Object row) {
|
||||
Object result;
|
||||
int associationNumber = getAssociationNumber();
|
||||
|
||||
if(getIsValueHolderAssociated()) {
|
||||
if(associationNumber == -1) {
|
||||
result = getValueHolder().getValue();
|
||||
}//if//
|
||||
else if(associationNumber == -2) {
|
||||
result = fixedValue;
|
||||
}//else if//
|
||||
else {
|
||||
if(oneArray == null) {
|
||||
oneArray = new Object[1];
|
||||
}//if//
|
||||
|
||||
oneArray[0] = row;
|
||||
result = getHandler().invokeMethod(associationNumber, getValueHolder().getValue(), oneArray, IAssociationHandler.INVOKE_GETTER_METHOD_FLAG);
|
||||
oneArray[0] = null;
|
||||
}//else//
|
||||
}//if//
|
||||
else {
|
||||
if(associationNumber == -1) {
|
||||
result = row;
|
||||
}//if//
|
||||
else if(associationNumber == -2) {
|
||||
result = fixedValue;
|
||||
}//else if//
|
||||
else {
|
||||
result = getHandler().invokeMethod(associationNumber, row, null, IAssociationHandler.INVOKE_GETTER_METHOD_FLAG);
|
||||
}//else//
|
||||
}//else//
|
||||
|
||||
if(isTargetAssociation()) {
|
||||
result = convertValueToControl(result);
|
||||
}//if//
|
||||
|
||||
return result;
|
||||
}//getResult()//
|
||||
/**
|
||||
* Gets the original value for the association.
|
||||
* @return The value that the user has replaced via the view.
|
||||
*/
|
||||
public Object getOriginalResult() {
|
||||
Object result = null;
|
||||
int associationNumber = getAssociationNumber();
|
||||
|
||||
//Ignore non-target associations, short-circuit and fixed value setups.//
|
||||
if(isTargetAssociation() && associationNumber != -1 && associationNumber != -2) {
|
||||
if(getIsValueHolderAssociated()) {
|
||||
if(oneArray == null) {
|
||||
oneArray = new Object[1];
|
||||
}//if//
|
||||
|
||||
oneArray[0] = row;
|
||||
result = getHandler().invokeMethod(associationNumber, getValueHolder().getValue(), oneArray, IAssociationHandler.INVOKE_ORIGINAL_VALUE_GETTER_METHOD_FLAG);
|
||||
oneArray[0] = null;
|
||||
}//if//
|
||||
else {
|
||||
result = getHandler().invokeMethod(associationNumber, row, null, IAssociationHandler.INVOKE_ORIGINAL_VALUE_GETTER_METHOD_FLAG);
|
||||
}//else//
|
||||
|
||||
result = convertValueToControl(result);
|
||||
}//if//
|
||||
|
||||
return result;
|
||||
}//getOriginalResult()//
|
||||
/**
|
||||
* Registers a value with the association for use by a single association.
|
||||
* @param row The next value in the chain of associations for the single association.
|
||||
*/
|
||||
public void register(Object row) {
|
||||
register(row, getResult(row));
|
||||
}//register()//
|
||||
/**
|
||||
* Registers a value with the association for use by a single association.
|
||||
* @param row The next value in the chain of associations for the single association.
|
||||
* @param result The result for the row.
|
||||
*/
|
||||
protected void register(Object row, Object result) {
|
||||
//Store the result.//
|
||||
this.result = result;
|
||||
this.row = row;
|
||||
|
||||
if(events != null) {
|
||||
//Register all events.//
|
||||
for(int index = 0; index < events.length; index++) {
|
||||
EventAssociation eventAssociation = (EventAssociation) events[index];
|
||||
|
||||
if(eventAssociation.getIsValueHolderAssociated()) {
|
||||
//Register for the value holder's value changing and for the event on the held value (uses the value holder's event support).//
|
||||
eventAssociation.register();
|
||||
}//if//
|
||||
else if(row instanceof IEventEmitter) {
|
||||
//Register with the event on the row object using the resource association's event support.//
|
||||
((SingleAssociationContainer) getAssociationContainer()).getEventSupport().register((IEventEmitter) row, eventAssociation.getEventNumber(), this, true);
|
||||
}//else//
|
||||
}//for//
|
||||
}//if//
|
||||
|
||||
//TODO: Move this to the initialize/release code.
|
||||
//Register the listener with the value holder.//
|
||||
if(getIsValueHolderAssociated()) {
|
||||
getValueHolder().registerListener(this);
|
||||
}//if//
|
||||
|
||||
registerListeners(row);
|
||||
}//register()//
|
||||
/**
|
||||
* Registers listeners with the row.
|
||||
* @param row The association's row to register all extended listeners with.
|
||||
*/
|
||||
protected void registerListeners(Object row) {
|
||||
AssociationAttribute[] attributes = getAttributes();
|
||||
|
||||
if(attributes != null) {
|
||||
IHashSet registeredValues = new LiteHashSet(100);
|
||||
|
||||
//Lazy load the event support.//
|
||||
if(nodeEventSupport == null) {
|
||||
nodeEventSupport = new EventSupport(null);
|
||||
}//if//
|
||||
|
||||
//Iterate over the attributes and register listeners with the attribute values.//
|
||||
for(int index = 0; index < attributes.length; index++) {
|
||||
Object attributeValue = attributes[index].getValue(row);
|
||||
|
||||
if(!registeredValues.containsValue(attributeValue)) {
|
||||
registeredValues.add(attributeValue);
|
||||
|
||||
//TODO: Should we support other collections? If so we would need to be notified when there are changes...
|
||||
if(attributeValue instanceof IManagedCollection && attributes[index].getNavigateCollectionValues()) {
|
||||
IIterator iterator = ((IManagedCollection) attributeValue).iterator();
|
||||
|
||||
//Register for changes in the collected values.//
|
||||
while(iterator.hasNext()) {
|
||||
registerListeners(nodeEventSupport, registeredValues, this, iterator.next());
|
||||
}//while//
|
||||
|
||||
//Register for changes in the collection.//
|
||||
nodeEventSupport.register((IManagedCollection) attributeValue, IManagedCollection.EVENT, this, true);
|
||||
}//if//
|
||||
|
||||
//Register for changes in the attribute value.//
|
||||
registerListeners(nodeEventSupport, registeredValues, this, attributeValue);
|
||||
}//if//
|
||||
}//for//
|
||||
}//if//
|
||||
}//registerListeners()//
|
||||
/**
|
||||
* Unregisters the previous registration.
|
||||
* @param row The optional row value. This should be null if this is the root association.
|
||||
*/
|
||||
public void unregister(Object row) {
|
||||
if(events != null) {
|
||||
//Unregister all events.//
|
||||
for(int index = 0; index < events.length; index++) {
|
||||
EventAssociation eventAssociation = (EventAssociation) events[index];
|
||||
|
||||
if(eventAssociation.getIsValueHolderAssociated()) {
|
||||
//Unregister with the value holder's value changing and for the event on the held value (uses the value holder's event support).//
|
||||
eventAssociation.unregister();
|
||||
}//if//
|
||||
else if(row instanceof IEventEmitter) {
|
||||
//Register with the event on the row object using the resource association's event support.//
|
||||
((SingleAssociationContainer) getAssociationContainer()).getEventSupport().unregister((IEventEmitter) row, eventAssociation.getEventNumber(), this);
|
||||
}//else//
|
||||
}//for//
|
||||
}//if//
|
||||
|
||||
//Unregister the listener with the value holder.//
|
||||
if(getIsValueHolderAssociated()) {
|
||||
getValueHolder().unregisterListener(this);
|
||||
}//if//
|
||||
|
||||
unregisterListeners(row);
|
||||
this.row = null;
|
||||
this.result = null;
|
||||
}//unregister()//
|
||||
/**
|
||||
* Unregisters listeners with the result.
|
||||
* @param row The association's result to unregister all extended listeners with.
|
||||
*/
|
||||
protected void unregisterListeners(Object row) {
|
||||
AssociationAttribute[] attributes = getAttributes();
|
||||
|
||||
if(attributes != null) {
|
||||
nodeEventSupport.unregisterAll();
|
||||
}//if//
|
||||
}//unregisterListeners()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.view.IEventAssociationChangeListener#onEventFired(com.foundation.view.IEventAssociation, java.lang.Object[])
|
||||
*/
|
||||
public void onEventFired(IEventAssociation eventAssociation, Object[] eventArguments) {
|
||||
if(VERIFY_THREAD) {
|
||||
verifyThread();
|
||||
}//if//
|
||||
|
||||
//Called when an event association registered via a value holder fires an event (value holder value changed or value holder value's event fired).//
|
||||
updateAssociation(0);
|
||||
}//onEventFired()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.event.IEventHandler#evaluate(com.foundation.event.IEventEmitter, int, java.lang.Object[], int)
|
||||
*/
|
||||
public void evaluate(IEventEmitter eventEmitter, int eventNumber, Object[] eventParameters, int flags) {
|
||||
if(VERIFY_THREAD) {
|
||||
verifyThread();
|
||||
}//if//
|
||||
|
||||
//Called when one of the events registered using a row object is fired.//
|
||||
updateAssociation(isTargetAssociation() ? flags : 0);
|
||||
}//evaluate()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.event.IHandler#evaluate(int, java.lang.Object[], int)
|
||||
*/
|
||||
public void evaluate(int eventNumber, Object[] eventParameters, int flags) {
|
||||
//Never called.//
|
||||
}//evaluate()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.view.IValueHolderListener#heldValueChanged()
|
||||
*/
|
||||
public void heldValueChanged() {
|
||||
if(VERIFY_THREAD) {
|
||||
verifyThread();
|
||||
}//if//
|
||||
|
||||
//Called when the associated value holder changes values. A value is only associated for indirect link associations.//
|
||||
updateAssociation(0);
|
||||
}//heldValueChanged()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.view.Association#initialize(com.foundation.view.AssociationContainer)
|
||||
*/
|
||||
public void initialize(AssociationContainer associationContainer) {
|
||||
super.initialize(associationContainer);
|
||||
}//initialize()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.view.Association#release()
|
||||
*/
|
||||
public void release() {
|
||||
super.release();
|
||||
}//release()//
|
||||
/**
|
||||
* Called by the event handlers to update the association state when the result may have altered.
|
||||
* <p>TODO: In some cases it is not necessary to unregister and re-register fully. For example if we are updating because the row's referenced value changes, then we don't need to un/re-register with the row value events, but we must un/re-register for those events down the line.
|
||||
* @param eventFlags The event flags that for the event that triggered the update. This will only be non-zero if an event triggered the call and the event was either an original value changed or cleared event.
|
||||
*/
|
||||
protected void updateAssociation(int eventFlags) {
|
||||
if(EventSupport.isOriginalValueChanged(eventFlags) || EventSupport.isOriginalValueCleared(eventFlags)) {
|
||||
((SingleAssociationContainer) getAssociationContainer()).updateAssociations(this, null, eventFlags);
|
||||
}//if//
|
||||
else {
|
||||
Object newResult = getResult(row);
|
||||
|
||||
if(newResult != result) {
|
||||
Object oldResult = result;
|
||||
|
||||
unregisterListeners(row);
|
||||
result = newResult;
|
||||
registerListeners(row);
|
||||
((SingleAssociationContainer) getAssociationContainer()).updateAssociations(this, oldResult, eventFlags);
|
||||
}//if//
|
||||
else {
|
||||
//Always re-register all the extended listeners since they cannot handle attribute changes on their own.//
|
||||
unregisterListeners(row);
|
||||
registerListeners(row);
|
||||
}//else//
|
||||
}//if//
|
||||
}//updateAssociation()//
|
||||
}//SingleAssociation//
|
||||
@@ -0,0 +1,276 @@
|
||||
/*
|
||||
* Copyright (c) 2006,2009 Declarative Engineering LLC.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Declarative Engineering LLC
|
||||
* verson 1 which accompanies this distribution, and is available at
|
||||
* http://declarativeengineering.com/legal/DE_Developer_License_v1.txt
|
||||
*/
|
||||
package com.foundation.view;
|
||||
|
||||
import com.common.debug.Debug;
|
||||
import com.common.util.IIterator;
|
||||
import com.foundation.event.EventSupport;
|
||||
|
||||
public class SingleAssociationContainer extends AssociationContainer implements IValueHolderListener {
|
||||
/** A simple flag that turns thread verification on and off. */
|
||||
private static final boolean VERIFY_THREAD = true;
|
||||
|
||||
private SingleAssociation[] associations = null;
|
||||
private SingleAssociation firstNode = null;
|
||||
private SingleAssociation lastNode = null;
|
||||
private Object row = null;
|
||||
private String valueHolderName = null;
|
||||
private EventSupport eventSupport = null;
|
||||
private IValueHolder valueHolder = null;
|
||||
/**
|
||||
* AssociationContainer constructor.
|
||||
* @param valueHolderName The name of the value holder whose value will be the input for the associations.
|
||||
* @param associations The associations that are the roots for the single resource association that will be using them.
|
||||
*/
|
||||
public SingleAssociationContainer(String valueHolderName, SingleAssociation[] associations) {
|
||||
this.valueHolderName = valueHolderName;
|
||||
this.associations = associations;
|
||||
}//AssociationContainer()//
|
||||
/**
|
||||
* Locates the value holder by looking in the component's parents (and the component its self if it is a container) until a value holder with the given name is found.
|
||||
* <p>Note: The name is not case sensitive.
|
||||
* @return The found value holder, or null if one could not be found.
|
||||
*/
|
||||
protected IValueHolder locateValueHolder(IAbstractComponent component) {
|
||||
IAbstractContainer container = null;
|
||||
IValueHolder result = null;
|
||||
|
||||
if(component instanceof IAbstractContainer) {
|
||||
container = (IAbstractContainer) component;
|
||||
}//if//
|
||||
else {
|
||||
container = component.getContainer();
|
||||
}//else//
|
||||
|
||||
while((result == null) && (container != null)) {
|
||||
IIterator iterator = container.getComponents().iterator();
|
||||
|
||||
while((result == null) && (iterator.hasNext())) {
|
||||
Object next = iterator.next();
|
||||
|
||||
if((next instanceof IValueHolder) && (((IValueHolder) next).getName().equalsIgnoreCase(valueHolderName))) {
|
||||
result = (IValueHolder) next;
|
||||
}//if//
|
||||
}//while//
|
||||
|
||||
container = container.getContainer();
|
||||
}//while//
|
||||
|
||||
return result;
|
||||
}//locateValueHolder()//
|
||||
/**
|
||||
* Initializes the container of associations.
|
||||
*/
|
||||
public void initialize() {
|
||||
if(associations != null) {
|
||||
for(int index = 0; index < associations.length; index++) {
|
||||
associations[index].initialize(this);
|
||||
}//for//
|
||||
}//if//
|
||||
}//initialize()//
|
||||
/**
|
||||
* Releases the container of associations.
|
||||
*/
|
||||
public void release() {
|
||||
if(associations != null) {
|
||||
for(int index = 0; index < associations.length; index++) {
|
||||
associations[index].release();
|
||||
}//for//
|
||||
}//if//
|
||||
}//release()//
|
||||
/**
|
||||
* Registers the associations.
|
||||
*/
|
||||
public void register() {
|
||||
SingleAssociation association = null;
|
||||
|
||||
if(valueHolderName != null) {
|
||||
if(valueHolder == null) {
|
||||
valueHolder = locateValueHolder(getResourceAssociation().getComponent());
|
||||
valueHolder.registerListener(this);
|
||||
}//if//
|
||||
|
||||
row = valueHolder.getValue();
|
||||
}//if//
|
||||
|
||||
//Find the first applicable association.//
|
||||
for(int index = 0; association == null && index < associations.length; index++) {
|
||||
if(associations[index].isApplicable(row)) {
|
||||
association = associations[index];
|
||||
}//if//
|
||||
}//for//
|
||||
|
||||
//If an applicable association was found then register the row with it.//
|
||||
if(association != null) {
|
||||
firstNode = association;
|
||||
association.register(row);
|
||||
}//if//
|
||||
|
||||
//Register with the child associations until we find a target association.//
|
||||
while((association != null) && (!association.isTargetAssociation())) {
|
||||
SingleAssociation next = null;
|
||||
Object result = association.getResult();
|
||||
|
||||
for(int index = 0; next == null && index < associations.length; index++) {
|
||||
if(associations[index].isApplicable(result)) {
|
||||
next = associations[index];
|
||||
}//if//
|
||||
}//for//
|
||||
|
||||
if(next != null) {
|
||||
association.setNext(next);
|
||||
next.register(result);
|
||||
}//if//
|
||||
|
||||
association = next;
|
||||
}//while//
|
||||
|
||||
if((association != null) && (association.isTargetAssociation())) {
|
||||
lastNode = association;
|
||||
}//if//
|
||||
}//register()//
|
||||
/**
|
||||
* Unregisters the associations.
|
||||
*/
|
||||
public void unregister() {
|
||||
if(firstNode != null) {
|
||||
unregister(firstNode, row);
|
||||
firstNode = null;
|
||||
lastNode = null;
|
||||
}//if//
|
||||
|
||||
row = null;
|
||||
|
||||
if(eventSupport != null) {
|
||||
eventSupport.unregisterAll();
|
||||
}//if//
|
||||
}//unregister()//
|
||||
/**
|
||||
* Recursively unregisters associations.
|
||||
* @param association The association to unregister with.
|
||||
* @param value The previous association's value which is the given association's input.
|
||||
*/
|
||||
protected void unregister(SingleAssociation association, Object value) {
|
||||
if(association.getNext() != null) {
|
||||
unregister(association.getNext(), association.getResult());
|
||||
association.setNext(null);
|
||||
}//if//
|
||||
|
||||
association.unregister(value);
|
||||
}//unregister()//
|
||||
/**
|
||||
* Gets the value in the model.
|
||||
* @return The value the row resolves to. This will be NO_VALUE if the row does not resolve to a target association.
|
||||
*/
|
||||
public Object getValue() {
|
||||
Object result = NO_VALUE;
|
||||
SingleAssociation association = firstNode;
|
||||
|
||||
if(association != null) {
|
||||
while(association.getNext() != null) {
|
||||
association = association.getNext();
|
||||
}//while//
|
||||
|
||||
if(association.isTargetAssociation()) {
|
||||
result = association.getResult();
|
||||
}//if//
|
||||
}//if//
|
||||
|
||||
return result;
|
||||
}//getValue()//
|
||||
/**
|
||||
* Gets the last association node in the chain to the result.
|
||||
* @return The last association which produces the result. This will be null if there is no input, or no path to a target association.
|
||||
*/
|
||||
public SingleAssociation getEndAssociation() {
|
||||
return lastNode;
|
||||
}//getEndAssociation()//
|
||||
/**
|
||||
* Sets the value in the model.
|
||||
* @param value The value to be set in the model.
|
||||
*/
|
||||
public void setValue(Object value) {
|
||||
SingleAssociation association = firstNode;
|
||||
|
||||
if(VERIFY_THREAD) {
|
||||
verifyThread();
|
||||
}//if//
|
||||
|
||||
if(firstNode == null) {
|
||||
Debug.log("Error");
|
||||
}//if//
|
||||
|
||||
while(association.getNext() != null) {
|
||||
association = association.getNext();
|
||||
}//while//
|
||||
|
||||
if(association.isTargetAssociation()) {
|
||||
association.setResult(value);
|
||||
}//if//
|
||||
}//setValue()//
|
||||
/**
|
||||
* Updates the associations after one of the associations alters its value.
|
||||
* @param association The association that altered its value.
|
||||
* @param oldValue The previous value for the given association.
|
||||
* @param eventFlags The event flags that for the event that triggered the update. This will only be non-zero if an event triggered the call and the event was either an original value changed or cleared event.
|
||||
*/
|
||||
public void updateAssociations(SingleAssociation association, Object oldValue, int eventFlags) {
|
||||
if(!EventSupport.isOriginalValueChanged(eventFlags) && !EventSupport.isOriginalValueCleared(eventFlags)) {
|
||||
if(association.getNext() != null) {
|
||||
unregister(association.getNext(), oldValue);
|
||||
}//if//
|
||||
|
||||
//Register with the child associations until we find a target association.//
|
||||
while((association != null) && (!association.isTargetAssociation())) {
|
||||
SingleAssociation next = null;
|
||||
Object result = association.getResult();
|
||||
|
||||
for(int index = 0; next == null && index < associations.length; index++) {
|
||||
if(associations[index].isApplicable(result)) {
|
||||
next = associations[index];
|
||||
}//if//
|
||||
}//for//
|
||||
|
||||
if(next != null) {
|
||||
association.setNext(next);
|
||||
next.register(result);
|
||||
}//if//
|
||||
|
||||
association = next;
|
||||
}//while//
|
||||
|
||||
//Update the last node reference.//
|
||||
if((association != null) && (association.isTargetAssociation())) {
|
||||
lastNode = association;
|
||||
}//if//
|
||||
}//if//
|
||||
|
||||
//Notify the SingleResourceAssociation.//
|
||||
((SingleResourceAssociation) getResourceAssociation()).modelChanged(eventFlags, false);
|
||||
}//updateAssociations()//
|
||||
/**
|
||||
* Gets the event support for the association.
|
||||
* @return The event support used by the association to manage event registrations.
|
||||
*/
|
||||
public EventSupport getEventSupport() {
|
||||
return eventSupport == null ? (eventSupport = new EventSupport(null)) : eventSupport;
|
||||
}//getEventSupport()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.view.IValueHolderListener#heldValueChanged()
|
||||
*/
|
||||
public void heldValueChanged() {
|
||||
if(VERIFY_THREAD) {
|
||||
verifyThread();
|
||||
}//if//
|
||||
|
||||
//Called when the value holder related to this container has changed its held value.//
|
||||
unregister();
|
||||
register();
|
||||
((SingleResourceAssociation) getResourceAssociation()).modelChanged(0, true);
|
||||
}//heldValueChanged()//
|
||||
}//AssociationContainer//
|
||||
@@ -0,0 +1,360 @@
|
||||
/*
|
||||
* Copyright (c) 2005,2009 Declarative Engineering LLC.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Declarative Engineering LLC
|
||||
* verson 1 which accompanies this distribution, and is available at
|
||||
* http://declarativeengineering.com/legal/DE_Developer_License_v1.txt
|
||||
*/
|
||||
package com.foundation.view;
|
||||
|
||||
import com.common.comparison.Comparator;
|
||||
import com.foundation.controller.IDecorationListener;
|
||||
import com.foundation.event.EventSupport;
|
||||
import com.foundation.metadata.Attribute;
|
||||
|
||||
/*
|
||||
* Gives the 'owner' access to a single value from a single association.
|
||||
* The resource association may have multiple related associations, but only one will be chosen to provide a value.
|
||||
* If the value is a collection, the 'owner' should consider registering with the collection for change events since this object will not be able to notify the 'owner' when the collection values change, only when the whole collection changes.
|
||||
*/
|
||||
public class SingleResourceAssociation extends ResourceAssociation implements ISingleResourceAssociation, IDecorationListener {
|
||||
private static final Object NULL_VALUE = new Object();
|
||||
|
||||
/** The container of association metadata. */
|
||||
private SingleAssociationContainer associationContainer = null;
|
||||
/** The listener which receives change notifications. */
|
||||
private ISingleResourceAssociationChangeListener changeListener = null;
|
||||
/** The resource's current value. */
|
||||
private Object currentValue = null;
|
||||
/** Whether the current value attribute is valid. This will be false if a target association was never found for the item. The current value may still be valid if there is a default value. */
|
||||
private boolean hasValue = false;
|
||||
/** A flag indicating that the association is in the process of refreshing and should ignore update events. Update events may occur due to the model loading attribute values on demand. */
|
||||
private boolean isRefreshing = false;
|
||||
/** The externally set value which will be null unless external code attempts to set the value. This is ignored if there are associations. This will be set to NULL_VALUE if the external code forces the value to null. */
|
||||
private Object externalSetValue = null;
|
||||
/** Whether this association should be linked to decorations adorning the object and attribute referencing the association result. */
|
||||
private boolean linkToDecorations = false;
|
||||
/** The last known decoration object. */
|
||||
private Object decorationObject = null;
|
||||
/** The last known decoration attribute. */
|
||||
private Attribute decorationAttribute = null;
|
||||
/**
|
||||
* SingleResourceAssociation constructor.
|
||||
* @param component The component that is using the resource association. This is used for debugging and for searching for value holders.
|
||||
* @param changeListener The listener which receives change notifications. This is often the component that is using this association.
|
||||
* @param viewContext The view context under which this association is being created. This will be used to access the view's resources.
|
||||
* @param valueType The type of value that the association is holding. This must be one of the TYPE_XXX identifiers defined in this class.
|
||||
* @param canSetValue Whether an altered value should be propegated back to the underlying object via the attribute if available, or set method if available.
|
||||
* @param initialValue The initial value which should be identical to the value that is default in the control. Example: Boolean.TRUE for the isVisible resource since a component is by default visible in the view.
|
||||
*/
|
||||
public SingleResourceAssociation(IAbstractComponent component, ISingleResourceAssociationChangeListener changeListener, IViewContext viewContext, int valueType, boolean canSetValue, Object initialValue) {
|
||||
super(component, viewContext, valueType, canSetValue, initialValue);
|
||||
|
||||
this.changeListener = changeListener;
|
||||
this.currentValue = initialValue;
|
||||
}//SingleResourceAssociation()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.view.ResourceAssociation#initialize()
|
||||
*/
|
||||
public void initialize() {
|
||||
initialize(false);
|
||||
}//initialize()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.view.ResourceAssociation#initialize()
|
||||
*/
|
||||
public void initialize(boolean linkToDecorations) {
|
||||
this.linkToDecorations = linkToDecorations;
|
||||
|
||||
if(associationContainer != null) {
|
||||
associationContainer.initialize();
|
||||
associationContainer.register();
|
||||
}//if//
|
||||
|
||||
super.initialize();
|
||||
|
||||
if(associationContainer != null) {
|
||||
modelChanged(0, false);
|
||||
}//if//
|
||||
}//initialize()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.view.ResourceAssociation#release()
|
||||
*/
|
||||
public void release() {
|
||||
if((linkToDecorations) && (decorationObject != null)) {
|
||||
changeListener.getDecorationManager().unregisterDecorationListener(this, null, null);
|
||||
}//if//
|
||||
|
||||
if(associationContainer != null) {
|
||||
associationContainer.unregister();
|
||||
associationContainer.release();
|
||||
}//if//
|
||||
|
||||
super.release();
|
||||
}//release()//
|
||||
/**
|
||||
* Sets the associations.
|
||||
* @param associationContainer The container of associations.
|
||||
*/
|
||||
public void setAssociations(SingleAssociationContainer associationContainer) {
|
||||
//Note: Added this code to prevent changing the associations after initialization because allowing this makes the code way too complex and prevents use of the VariableResourceAssociation.//
|
||||
//TODO: Allow this... It will require sending control notifications for each row I think.
|
||||
if(isInitialized()) {
|
||||
throw new RuntimeException("Cannot set associations after the resource association has been initialized.");
|
||||
}//if//
|
||||
|
||||
if(this.associationContainer != null) {
|
||||
//TODO: Unregister all rows...
|
||||
this.associationContainer.release();
|
||||
}//if//
|
||||
|
||||
this.associationContainer = associationContainer;
|
||||
associationContainer.setResourceAssociation(this);
|
||||
|
||||
if(isInitialized()) {
|
||||
associationContainer.initialize();
|
||||
}//if//
|
||||
}//setAssociations()//
|
||||
/**
|
||||
* Gets the listener that is receiving change notifications. This is normally the component using the resource association.
|
||||
* @return The listener which must be notified when the associated value is altered externally.
|
||||
*/
|
||||
protected ISingleResourceAssociationChangeListener getChangeListener() {
|
||||
return changeListener;
|
||||
}//getChangeListener()//
|
||||
/**
|
||||
* Gets the value for the association.
|
||||
* @return The association's result value. This could be null if the value is null.
|
||||
*/
|
||||
public final Object getValue() {
|
||||
return currentValue;
|
||||
}//getValue()//
|
||||
/**
|
||||
* Determines whether the association has resolved to a value.
|
||||
* @return Whether a value was determined for the association. This may be false if refresh was never called, or if the input did not resolve to a target value.
|
||||
*/
|
||||
public final boolean hasValue() {
|
||||
return hasValue;
|
||||
}//hasValue()//
|
||||
/**
|
||||
* Sets the association value by passing the value to the model linked by the association. If there isn't a link then the value will just be stored locally.
|
||||
* @param value The association's result value. This could be null if the value is null.
|
||||
*/
|
||||
public final void setValue(Object value) {
|
||||
if(hasAssociations()) {
|
||||
ISingleResourceAssociationChangeListener listener = getChangeListener();
|
||||
|
||||
if(listener != null) {
|
||||
listener.addMessageHold();
|
||||
}//if//
|
||||
|
||||
//If the previous value was from a resource OR the current value does not equal the new value THEN update the current value in the model.//
|
||||
if(!Comparator.equals(value, getValue())) {
|
||||
//Store the value as the current value for this resource association.//
|
||||
currentValue = value;
|
||||
//Save the value in the attribute/method if there is a bound attribute or setter method and the values differ.//
|
||||
synchronizeValue(value);
|
||||
}//if//
|
||||
|
||||
if(listener != null) {
|
||||
listener.removeMessageHold();
|
||||
}//if//
|
||||
}//if//
|
||||
else {
|
||||
currentValue = value;
|
||||
}//else//
|
||||
}//setValue()//
|
||||
/**
|
||||
* Sets the value from an external source and notifies the listener that the value may have changed.
|
||||
* @param value The externally set value which will be stored until refresh is called at which time it may be used as the current value.
|
||||
*/
|
||||
public final void externalSetValue(Object value) {
|
||||
if(!hasAssociations()) {
|
||||
ISingleResourceAssociationChangeListener listener = getChangeListener();
|
||||
|
||||
if(listener != null) {
|
||||
externalSetValue = value == null ? NULL_VALUE : value;
|
||||
listener.onValueChanged(this, ISingleResourceAssociationChangeListener.FLAG_NONE);
|
||||
}//if//
|
||||
}//if//
|
||||
}//externalSetValue()//
|
||||
/**
|
||||
* Synchronizes the value with the model.
|
||||
* @param value The value to be sent to the model.
|
||||
*/
|
||||
private void synchronizeValue(Object value) {
|
||||
if(canSetValue()) {
|
||||
associationContainer.setValue(value);
|
||||
}//if//
|
||||
}//synchronizeValue()//
|
||||
/**
|
||||
* Determines whether there are any associations possible.
|
||||
* @return Whether there is any association metadata defined.
|
||||
*/
|
||||
public boolean hasAssociations() {
|
||||
return associationContainer != null;
|
||||
}//hasAssociations()//
|
||||
/**
|
||||
* TODO: Update this commenting...
|
||||
* Refreshes the resource association which will take all the settings into account to set the resource's value.
|
||||
* This method normally is called by the component when the component is notified that the related association's value has been altered.
|
||||
* Refresh should also be called by the component in the component's refresh method.
|
||||
* <p>No notification will be sent to the component as this is not considered an external stimulus.</p>
|
||||
* @return Whether the resource value was updated. If it was then a call to getValue() will return the latest value.
|
||||
* @see #getValue()
|
||||
*/
|
||||
public boolean refresh() {
|
||||
boolean result = false;
|
||||
|
||||
hasValue = true;
|
||||
|
||||
if(externalSetValue != null) {
|
||||
if(externalSetValue == NULL_VALUE) {
|
||||
externalSetValue = null;
|
||||
}//if//
|
||||
|
||||
if(!Comparator.equals(currentValue, externalSetValue)) {
|
||||
result = true;
|
||||
currentValue = externalSetValue;
|
||||
}//if//
|
||||
|
||||
externalSetValue = null;
|
||||
}//if//
|
||||
else if(hasAssociations()) {
|
||||
Object newValue = associationContainer.getValue();
|
||||
|
||||
if(newValue == SingleAssociationContainer.NO_VALUE) {
|
||||
newValue = getDefaultValue();
|
||||
}//if//
|
||||
|
||||
if(!Comparator.equals(newValue, currentValue)) {
|
||||
currentValue = newValue;
|
||||
result = true;
|
||||
|
||||
if((currentValue == null) && (getDefaultValue() != null)) {
|
||||
currentValue = getDefaultValue();
|
||||
|
||||
try {
|
||||
isRefreshing = true;
|
||||
synchronizeValue(currentValue);
|
||||
}//try//
|
||||
finally {
|
||||
isRefreshing = false;
|
||||
}//finally//
|
||||
}//if//
|
||||
}//if//
|
||||
}//else if//
|
||||
else if(getDefaultValue() != null) {
|
||||
Object defaultValue = getDefaultValue();
|
||||
|
||||
if(!Comparator.equals(currentValue, defaultValue)) {
|
||||
result = true;
|
||||
currentValue = defaultValue;
|
||||
}//if//
|
||||
}//else if//
|
||||
else {
|
||||
//Indicate that the associations did not resolve in a target.//
|
||||
hasValue = false;
|
||||
currentValue = null;
|
||||
}//else//
|
||||
|
||||
return result;
|
||||
}//refresh()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.view.ResourceAssociation#internalRelease()
|
||||
*/
|
||||
protected void internalRelease() {
|
||||
//TODO: We should unregister the row value first...
|
||||
if(associationContainer != null) {
|
||||
associationContainer.release();
|
||||
}//if//
|
||||
|
||||
changeListener = null;
|
||||
currentValue = null;
|
||||
}//internalRelease()//
|
||||
/**
|
||||
* Called by the associations when the model has been altered.
|
||||
* @param eventFlags The event flags that for the event that triggered the update. This will only be non-zero if an event triggered the call and the event was either an original value changed or cleared event.
|
||||
* @param hasContainingModelChanged Whether the model that has the value has been changed. This is useful for the control to know so that it realizes that any changes it holds must be replaced with the new value. For example if you are viewing text on a Customer object and you change the text in the model (this flag is then false), but it was updated in the view, then the user should be warned, but the user's changes should not be replaced. If the Customer object being referenced changes (this flag is then true) then the text should always be replaced in the control.
|
||||
*/
|
||||
public void modelChanged(int eventFlags, boolean hasContainingModelChanged) {
|
||||
if(!isRefreshing) {
|
||||
//If the target association's attribute and value have changed and we are linking to them for decorations, then update the listeners.//
|
||||
//The add and remove decoration methods will be called via the listener being setup here.//
|
||||
if((linkToDecorations) && ((!Comparator.equals(decorationObject, associationContainer.getEndAssociation() == null ? null : associationContainer.getEndAssociation().getRow())) || (!Comparator.equals(decorationAttribute, associationContainer.getEndAssociation() == null ? null : associationContainer.getEndAssociation().getDecoratedAttribute())))) {
|
||||
Object oldDecorationObject = decorationObject;
|
||||
Attribute oldDecorationAttribute = decorationAttribute;
|
||||
|
||||
if((associationContainer.getEndAssociation() != null) && (associationContainer.getEndAssociation().useDecorations())) {
|
||||
decorationObject = associationContainer.getEndAssociation().getRow();
|
||||
decorationAttribute = associationContainer.getEndAssociation().getDecoratedAttribute();
|
||||
}//if//
|
||||
else {
|
||||
decorationObject = null;
|
||||
decorationAttribute = null;
|
||||
}//else//
|
||||
|
||||
if((oldDecorationObject == null) && (decorationObject != null)) {
|
||||
getChangeListener().getDecorationManager().registerDecorationListener(this);
|
||||
}//if//
|
||||
else if((oldDecorationObject != null) && (decorationObject == null)) {
|
||||
getChangeListener().getDecorationManager().unregisterDecorationListener(this, oldDecorationObject, oldDecorationAttribute);
|
||||
}//else if//
|
||||
else if((oldDecorationObject != null) && (decorationObject != null)) {
|
||||
getChangeListener().getDecorationManager().updateDecorationListener(this, oldDecorationObject, oldDecorationAttribute);
|
||||
}//else//
|
||||
}//if//
|
||||
|
||||
//If the original value changed then the user has made changes to the model that over road another user's changes (made external to this view). We should notify the user of this via the control.//
|
||||
if(EventSupport.isOriginalValueChanged(eventFlags) || EventSupport.isOriginalValueCleared(eventFlags)) {
|
||||
getChangeListener().onModelExternallyChanged(this, EventSupport.isOriginalValueCleared(eventFlags), associationContainer.getEndAssociation().getOriginalResult());
|
||||
}//if//
|
||||
else {
|
||||
getChangeListener().onValueChanged(this, EventSupport.isReflectionUpdateChangeEvent(eventFlags) ? ISingleResourceAssociationChangeListener.FLAG_IS_UPDATE : hasContainingModelChanged ? ISingleResourceAssociationChangeListener.FLAG_CONTAINING_MODEL_CHANGED : ISingleResourceAssociationChangeListener.FLAG_NONE);
|
||||
}//else//
|
||||
}//if//
|
||||
}//modelChanged()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.controller.IDecorationListener#addDecoration(com.foundation.view.AbstractDecoration)
|
||||
*/
|
||||
public void addDecoration(AbstractDecoration decoration) {
|
||||
changeListener.addDecoration(decoration);
|
||||
}//addDecoration()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.controller.IDecorationListener#removeDecoration(com.foundation.view.AbstractDecoration)
|
||||
*/
|
||||
public void removeDecoration(AbstractDecoration decoration) {
|
||||
changeListener.removeDecoration(decoration);
|
||||
}//removeDecoration()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.controller.IDecorationListener#getDecoratedAttribute()
|
||||
*/
|
||||
public Attribute getDecoratedAttribute() {
|
||||
SingleAssociation association = associationContainer.getEndAssociation();
|
||||
Attribute result = null;
|
||||
|
||||
if((association != null) && (association.useDecorations())) {
|
||||
result = association.getDecoratedAttribute();
|
||||
}//if//
|
||||
|
||||
return result;
|
||||
}//getDecoratedAttribute()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.controller.IDecorationListener#getDecoratedObject()
|
||||
*/
|
||||
public Object getDecoratedObject() {
|
||||
SingleAssociation association = associationContainer.getEndAssociation();
|
||||
Object result = null;
|
||||
|
||||
if((association != null) && (association.useDecorations())) {
|
||||
result = association.getRow();
|
||||
}//if//
|
||||
|
||||
return result;
|
||||
}//getDecoratedObject()//
|
||||
/* (non-Javadoc)
|
||||
* @see java.lang.Object#toString()
|
||||
*/
|
||||
public String toString() {
|
||||
return "SingleResourceAssociation";
|
||||
}//toString()//
|
||||
}//SingleResourceAssociation//
|
||||
136
Foundation/src/com/foundation/view/ValueHolderAssociation.java
Normal file
136
Foundation/src/com/foundation/view/ValueHolderAssociation.java
Normal file
@@ -0,0 +1,136 @@
|
||||
/*
|
||||
* Copyright (c) 2003,2007 Declarative Engineering LLC.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Declarative Engineering LLC
|
||||
* verson 1 which accompanies this distribution, and is available at
|
||||
* http://declarativeengineering.com/legal/DE_Developer_License_v1.txt
|
||||
*/
|
||||
package com.foundation.view;
|
||||
|
||||
import com.common.util.*;
|
||||
import com.common.debug.*;
|
||||
|
||||
/*
|
||||
* TODO: Refactor this code since Association is no longer a subclass.
|
||||
*/
|
||||
public class ValueHolderAssociation implements IValueHolderAssociation {
|
||||
/** The component that created the association. */
|
||||
private IAbstractComponent component = null;
|
||||
/** The name of the value holder which can be used to lookup related value holders. */
|
||||
private String valueHolderName = null;
|
||||
/** The actual value holder instance at runtime. */
|
||||
private IValueHolder valueHolder = null;
|
||||
/** The optional name of the type the association applies to. If there is a value holder name then this should be the value holder's type or a specialization thereof. */
|
||||
private Class valueHolderHeldType = null;
|
||||
/** The optional name of the type the association applies to. If this association is used by a MultiResourceAssociation then this defines whether the association is valid for each row. */
|
||||
private Class rowType = null;
|
||||
/**
|
||||
* ValueHolderAssociation constructor.
|
||||
* @param component The component the association is associating a value holder with.
|
||||
* @param valueHolderName The name of the value holder for the association. The value holder's held type defines the association if the value type is null.
|
||||
* @param valueHolderHeldType The optional class which defines the association's relationship to the value holder.
|
||||
* @param rowType The optional class which defines the association's relationship to a row object (such as seen in a list, table, etc).
|
||||
*/
|
||||
public ValueHolderAssociation(IAbstractComponent component, String valueHolderName, Class valueHolderHeldType, Class rowType) {
|
||||
this.valueHolderName = valueHolderName;
|
||||
this.component = component;
|
||||
this.valueHolderHeldType = valueHolderHeldType;
|
||||
this.rowType = rowType;
|
||||
}//ValueHolderAssociation()//
|
||||
/**
|
||||
* Gets whether the value holder association is connected to a value holder by name.
|
||||
* @return Whether there is a potential value holder connection.
|
||||
*/
|
||||
public boolean getIsValueHolderAssociated() {
|
||||
return valueHolderName != null;
|
||||
}//getIsValueHolderAssociated()//
|
||||
/**
|
||||
* Gets the value holder associated with the component.
|
||||
* @return The associated value holder, or null if one was not located with the proper name.
|
||||
*/
|
||||
public IValueHolder getValueHolder() {
|
||||
if(valueHolder == null) {
|
||||
valueHolder = locateValueHolder();
|
||||
|
||||
if(valueHolder == null) {
|
||||
Debug.log("Warning: ValueHolder named " + valueHolderName + " could not be found for the view component " + component);
|
||||
}//if//
|
||||
}//if//
|
||||
|
||||
return valueHolder;
|
||||
}//getValueHolder()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.view.IValueHolderAssociation#getValueHolderHeldType()
|
||||
*/
|
||||
public Class getValueHolderHeldType() {
|
||||
return valueHolderHeldType;
|
||||
}//getValueHolderHeldType()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.view.IValueHolderAssociation#getRowType()
|
||||
*/
|
||||
public Class getRowType() {
|
||||
return rowType;
|
||||
}//getRowType()//
|
||||
/**
|
||||
* Gets the component associated with the value holder.
|
||||
* @return The associated component.
|
||||
*/
|
||||
public IAbstractComponent getComponent() {
|
||||
return component;
|
||||
}//getComponent()//
|
||||
/**
|
||||
* Locates the value holder by looking in the component's parents (and the component its self if it is a container) until a value holder with the given name is found.
|
||||
* <p>Note: The name is not case sensitive.
|
||||
* @return The found value holder, or null if one could not be found.
|
||||
*/
|
||||
protected IValueHolder locateValueHolder() {
|
||||
IAbstractContainer container = null;
|
||||
IValueHolder result = null;
|
||||
|
||||
if(component instanceof IAbstractContainer) {
|
||||
container = (IAbstractContainer) component;
|
||||
}//if//
|
||||
else {
|
||||
container = component.getContainer();
|
||||
}//else//
|
||||
|
||||
while((result == null) && (container != null)) {
|
||||
IIterator iterator = container.getComponents().iterator();
|
||||
|
||||
while((result == null) && (iterator.hasNext())) {
|
||||
Object component = iterator.next();
|
||||
|
||||
if((component instanceof IValueHolder) && (((IValueHolder) component).getName().equalsIgnoreCase(valueHolderName))) {
|
||||
result = (IValueHolder) component;
|
||||
}//if//
|
||||
}//while//
|
||||
|
||||
container = container.getContainer();
|
||||
}//while//
|
||||
|
||||
return result;
|
||||
}//locateValueHolder()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.view.IValueHolderAssociation#isApplicable()
|
||||
*/
|
||||
public boolean isApplicable() {
|
||||
Object heldValue = getValueHolder() != null ? getValueHolder().getValue() : null;
|
||||
Class heldType = getValueHolderHeldType();
|
||||
|
||||
//True if the held type is null or if the value holder's held value is an instanceof the held type.//
|
||||
return ((getValueHolder() != null) && ((heldType == null) || ((heldValue != null) && (heldType.isAssignableFrom(heldValue.getClass())))));
|
||||
}//isApplicable()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.view.IValueHolderAssociation#isApplicable(java.lang.Object)
|
||||
*/
|
||||
public boolean isApplicable(Object value) {
|
||||
boolean result = true;
|
||||
|
||||
if(getIsValueHolderAssociated()) {
|
||||
result = isApplicable();
|
||||
}//if//
|
||||
|
||||
//True if the row type matches the value and if we have an associated value holder then also if it matches up with the held type.//
|
||||
return result && ((getRowType() == null) || (getRowType().isAssignableFrom(value.getClass())));
|
||||
}//isApplicable()//
|
||||
}//ValueHolderAssociation()//
|
||||
@@ -0,0 +1,233 @@
|
||||
/*
|
||||
* Copyright (c) 2006,2009 Declarative Engineering LLC.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Declarative Engineering LLC
|
||||
* verson 1 which accompanies this distribution, and is available at
|
||||
* http://declarativeengineering.com/legal/DE_Developer_License_v1.txt
|
||||
*/
|
||||
package com.foundation.view;
|
||||
|
||||
/**
|
||||
* This resource association looks like a MultiResourceAssociation, but can behave as a single.
|
||||
* The added associations must be consistant about referencing a row-type. They can all reference one or not, but cannot be mixed.
|
||||
* If all associations don't reference a row-type then a single association will be used which is more efficient.
|
||||
*/
|
||||
public class VariableResourceAssociation implements IResourceAssociation {
|
||||
private SingleResourceAssociation single = null;
|
||||
private MultiResourceAssociation multi = null;
|
||||
/**
|
||||
* VariableResourceAssociation constructor.
|
||||
*/
|
||||
protected VariableResourceAssociation() {
|
||||
}//VariableResourceAssociation()//
|
||||
/**
|
||||
* VariableResourceAssociation constructor.
|
||||
* @param changeListener The listener which receives change notifications. This is often the component that is using this association.
|
||||
* @param viewContext The view context under which this association is being created. This will be used to access the view's resources.
|
||||
* @param valueType The type of value that the association is holding. This must be one of the TYPE_XXX identifiers defined in this class.
|
||||
* @param canSetValue Whether an altered value should be propegated back to the underlying object via the attribute if available, or set method if available.
|
||||
* @param initialValue The initial value which should be identical to the value that is default in the control. Example: Boolean.TRUE for the isVisible resource since a component is by default visible in the view.
|
||||
*/
|
||||
public VariableResourceAssociation(IAbstractComponent component, IVariableResourceAssociationChangeListener changeListener, IViewContext viewContext, int valueType, boolean canSetValue, Object initialValue) {
|
||||
single = new SingleResourceAssociation(component, changeListener, viewContext, valueType, canSetValue, initialValue);
|
||||
multi = new MultiResourceAssociation(component, changeListener, viewContext, valueType, canSetValue, initialValue);
|
||||
}//VariableResourceAssociation()//
|
||||
/**
|
||||
* Determines whether this variable resource association is generating a value for each 'row' in the user control, instead of one value for each user control.
|
||||
* @return Whether this variable resource association is behaving and should be treated as a MultiResourceAssociation.
|
||||
*/
|
||||
public boolean isMulti() {
|
||||
return multi != null;
|
||||
}//isMulti()//
|
||||
/**
|
||||
* Determines whether this variable resource association is generating a single value per user control, instead of multiple values (one per 'row').
|
||||
* @return Whether this variable resource association is behaving and should be treated as a SingleResourceAssociation.
|
||||
*/
|
||||
public boolean isSingle() {
|
||||
return multi == null;
|
||||
}//isSingle()//
|
||||
/**
|
||||
* Determines whether the single association is part of this variable association.
|
||||
* @param single The single association to compare with.
|
||||
* @return Whether the single association is used by this variable association.
|
||||
*/
|
||||
public boolean hasAssociation(SingleResourceAssociation single) {
|
||||
return this.single == single;
|
||||
}//hasAssociation()//
|
||||
/**
|
||||
* Determines whether the multi association is part of this variable association.
|
||||
* @param multi The multi association to compare with.
|
||||
* @return Whether the multi association is used by this variable association.
|
||||
*/
|
||||
public boolean hasAssociation(MultiResourceAssociation multi) {
|
||||
return this.multi == multi;
|
||||
}//hasAssociation()//
|
||||
/**
|
||||
* Sets the associations.
|
||||
* @param associationContainer The container of associations.
|
||||
*/
|
||||
public void setAssociations(AssociationContainer associationContainer) {
|
||||
if(associationContainer instanceof SingleAssociationContainer) {
|
||||
setAssociations((SingleAssociationContainer) associationContainer);
|
||||
}//if//
|
||||
else if(associationContainer instanceof MultiAssociationContainer) {
|
||||
setAssociations((MultiAssociationContainer) associationContainer);
|
||||
}//else if//
|
||||
else {
|
||||
throw new IllegalArgumentException();
|
||||
}//if//
|
||||
}//setAssociations()//
|
||||
/**
|
||||
* Sets the associations.
|
||||
* @param associationContainer The container of associations.
|
||||
*/
|
||||
public void setAssociations(SingleAssociationContainer associationContainer) {
|
||||
single.setAssociations(associationContainer);
|
||||
//TODO: Don't set to null to indicate that it isn't used. This would allow us to set new containers while running...
|
||||
multi.release();
|
||||
multi = null;
|
||||
}//setAssociations()//
|
||||
/**
|
||||
* Sets the associations.
|
||||
* @param associationContainer The container of associations.
|
||||
*/
|
||||
public void setAssociations(MultiAssociationContainer associationContainer) {
|
||||
multi.setAssociations(associationContainer);
|
||||
//TODO: Don't set to null to indicate that it isn't used. This would allow us to set new containers while running...
|
||||
single.release();
|
||||
single = null;
|
||||
}//setAssociations()//
|
||||
/**
|
||||
* Initializes the resource association which will take all the settings into account to initialize the resource's value.
|
||||
* <p>No notification will be sent to the component as this is not considered an external stimulus.</p>
|
||||
*/
|
||||
public void initialize() {
|
||||
if(multi != null && multi.hasAssociations()) {
|
||||
single = null;
|
||||
multi.initialize();
|
||||
}//if//
|
||||
else {
|
||||
multi = null;
|
||||
single.initialize();
|
||||
}//else//
|
||||
}//initialize()//
|
||||
/**
|
||||
* Releases the resource association which will unregister any listeners and cleanup all resources.
|
||||
* <p>No notification will be sent to the component as this is not considered an external stimulus.</p>
|
||||
*/
|
||||
public void release() {
|
||||
if(multi != null) {
|
||||
multi.release();
|
||||
}//if//
|
||||
else {
|
||||
single.release();
|
||||
}//else//
|
||||
}//release()//
|
||||
/**
|
||||
* Determines whether the association can push an altered value to the model.
|
||||
* @return Whether the association has the ability to modify the model when the value is changed via the view (or defaults to the default value).
|
||||
*/
|
||||
public boolean canSetValue() {
|
||||
return multi != null ? multi.canSetValue() : single.canSetValue();
|
||||
}//canSetValue()//
|
||||
/**
|
||||
* Gets the identifier for the type of value returned by this resource association.
|
||||
* @return The resource association's value type.
|
||||
*/
|
||||
public int getValueType() {
|
||||
return multi != null ? multi.getValueType() : single.getValueType();
|
||||
}//getValueType()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.view.IResourceAssociation#getDefaultValue()
|
||||
*/
|
||||
public Object getDefaultValue() {
|
||||
return multi != null ? multi.getDefaultValue() : single.getDefaultValue();
|
||||
}//getDefaultValue()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.view.IResourceAssociation#setDefaultValue(java.lang.Object)
|
||||
*/
|
||||
public void setDefaultValue(Object defaultValue) {
|
||||
if(multi != null) {
|
||||
multi.setDefaultValue(defaultValue);
|
||||
}//if//
|
||||
|
||||
if(single != null) {
|
||||
single.setDefaultValue(defaultValue);
|
||||
}//if//
|
||||
}//setDefaultValue()//
|
||||
/**
|
||||
* Gets the value for the association.
|
||||
* @param item The item identifying which item data to use.
|
||||
* @return The static or resource value (depending on whether the value was set via a valid resource URL, was converted from a string, or was a properly typed value). This could be null if the value is null.
|
||||
*/
|
||||
public final Object getValue(Object item) {
|
||||
return multi != null ? multi.getValue(item) : single.getValue();
|
||||
}//getValue()//
|
||||
/**
|
||||
* Sets the association value by passing the value to the model linked by the association. If there isn't a link then the value will just be stored locally.
|
||||
* @param item The item identifying which item data to use.
|
||||
* @param value The static value as a string, the resource URL containing the value, or the properly typed static value.
|
||||
*/
|
||||
public final void setValue(Object item, Object value) {
|
||||
if(multi != null) {
|
||||
multi.setValue(item, value);
|
||||
}//if//
|
||||
else {
|
||||
single.setValue(value);
|
||||
}//else//
|
||||
}//setValue()//
|
||||
/**
|
||||
* Refreshes the resource association which will take all the settings into account to set the resource's value.
|
||||
* This method normally is called by the component when the component is notified that the related association's value has been altered.
|
||||
* Refresh should also be called by the component in the component's refresh method.
|
||||
* <p>No notification will be sent to the component as this is not considered an external stimulus.</p>
|
||||
* @param item The item identifying which item data to use.
|
||||
* @return Whether the resource value was updated. If it was then a call to getValue() will return the latest value.
|
||||
* @see #getValue(java.lang.Object)
|
||||
*/
|
||||
public boolean refresh(Object item) {
|
||||
return multi != null ? multi.refresh(item) : single.refresh();
|
||||
}//refresh()//
|
||||
/**
|
||||
* Registers an item with the association.
|
||||
* <p>Items must be registered prior to getting their association value. When an item is no longer used it must be unregistered to free up memory.</p>
|
||||
* <p>Note: The same item may be registered multiple times, but the unregister method must be called an equal number of times to avoid memory leaks.</p>
|
||||
* @param item The user's item to be registered.
|
||||
* @deprecated
|
||||
*/
|
||||
public void registerItem(Object item) {
|
||||
if(multi != null) {
|
||||
multi.registerItem(item, null);
|
||||
}//if//
|
||||
}//registerItem()//
|
||||
/**
|
||||
* Registers an item with the association.
|
||||
* <p>Items must be registered prior to getting their association value. When an item is no longer used it must be unregistered to free up memory.</p>
|
||||
* <p>Note: The same item may be registered multiple times, but the unregister method must be called an equal number of times to avoid memory leaks.</p>
|
||||
* @param item The user's item to be registered.
|
||||
* @param data The item's data object which will be passed with events. This can be any user defined value.
|
||||
*/
|
||||
public void registerItem(Object item, Object data) {
|
||||
if(multi != null) {
|
||||
multi.registerItem(item, data);
|
||||
}//if//
|
||||
}//registerItem()//
|
||||
/**
|
||||
* Unregisters an item from the association.
|
||||
* <p>The user must call this method once for every call to register an item.</p>
|
||||
* @param item The user's item to be unregistered.
|
||||
*/
|
||||
public void unregisterItem(Object item) {
|
||||
if(multi != null) {
|
||||
multi.unregisterItem(item);
|
||||
}//if//
|
||||
}//unregisterItem()//
|
||||
/**
|
||||
* Unregisters all items from the association.
|
||||
*/
|
||||
public void unregisterAllItems() {
|
||||
if(multi != null) {
|
||||
multi.unregisterAllItems();
|
||||
}//if//
|
||||
}//unregisterAllItems()//
|
||||
}//VariableResourceAssociation//
|
||||
69
Foundation/src/com/foundation/view/ViewSystemMetadata.java
Normal file
69
Foundation/src/com/foundation/view/ViewSystemMetadata.java
Normal file
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Copyright (c) 2005,2009 Declarative Engineering LLC.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Declarative Engineering LLC
|
||||
* verson 1 which accompanies this distribution, and is available at
|
||||
* http://declarativeengineering.com/legal/DE_Developer_License_v1.txt
|
||||
*/
|
||||
package com.foundation.view;
|
||||
|
||||
import java.io.Externalizable;
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectInput;
|
||||
import java.io.ObjectOutput;
|
||||
|
||||
import com.common.io.IExternalizable;
|
||||
import com.common.io.IObjectInputStream;
|
||||
import com.common.io.IObjectOutputStream;
|
||||
import com.common.util.LiteHashMap;
|
||||
|
||||
/**
|
||||
* A collection of information about the client which the view controllers can use to layout the views.
|
||||
*/
|
||||
public class ViewSystemMetadata implements IExternalizable {
|
||||
private static final int DISPLAY_METADATA = 1;
|
||||
|
||||
/** The metadata for each display. */
|
||||
private DisplayMetadata[] displayMetadata = null;
|
||||
/**
|
||||
* ClientMetadata constructor.
|
||||
*/
|
||||
public ViewSystemMetadata() {
|
||||
super();
|
||||
}//ClientMetadata()//
|
||||
/**
|
||||
* ClientMetadata constructor.
|
||||
* @param displayMetadata The metadata for each display.
|
||||
*/
|
||||
public ViewSystemMetadata(DisplayMetadata[] displayMetadata) {
|
||||
this.displayMetadata = displayMetadata;
|
||||
}//ClientMetadata()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.common.io.IExternalizable#readExternal(com.common.io.IObjectInputStream)
|
||||
*/
|
||||
public Object readExternal(IObjectInputStream in) throws IOException, ClassNotFoundException {
|
||||
int count = in.readInt();
|
||||
|
||||
while(count-- > 0) {
|
||||
switch(in.readInt()) {
|
||||
case DISPLAY_METADATA: {
|
||||
displayMetadata = (DisplayMetadata[]) in.readObject();
|
||||
break;
|
||||
}//case//
|
||||
default: {
|
||||
break;
|
||||
}//default//
|
||||
}//switch//
|
||||
}//while//
|
||||
|
||||
return null;
|
||||
}//readExternal()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.common.io.IExternalizable#writeExternal(com.common.io.IObjectOutputStream)
|
||||
*/
|
||||
public void writeExternal(IObjectOutputStream out) throws IOException {
|
||||
out.writeInt(1);
|
||||
out.writeInt(DISPLAY_METADATA);
|
||||
out.writeObject(displayMetadata);
|
||||
}//writeExternal()//
|
||||
}//ClientMetadata//
|
||||
@@ -0,0 +1,671 @@
|
||||
/*
|
||||
* Copyright (c) 2006,2009 Declarative Engineering LLC.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Declarative Engineering LLC
|
||||
* verson 1 which accompanies this distribution, and is available at
|
||||
* http://declarativeengineering.com/legal/DE_Developer_License_v1.txt
|
||||
*/
|
||||
package com.foundation.view.resource;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.zip.GZIPInputStream;
|
||||
|
||||
import com.common.debug.Debug;
|
||||
import com.common.io.StreamSupport;
|
||||
import com.common.util.*;
|
||||
import com.foundation.application.Application;
|
||||
import com.foundation.util.xml.DocumentBuilder;
|
||||
import com.foundation.util.xml.IDocument;
|
||||
import com.foundation.util.xml.IElement;
|
||||
import com.foundation.util.xml.INode;
|
||||
import com.foundation.view.JefColor;
|
||||
import com.foundation.view.JefFont;
|
||||
import com.foundation.view.JefGradient;
|
||||
|
||||
/**
|
||||
* This is the base class for the resource service.
|
||||
* The resource service provides the views (and potentially the application) with access to resources built with the application.
|
||||
* Resources cannot be changed during the running of the application, but the current set of resources can be altered based on the application state.
|
||||
* Resources are grouped into ResourcePackage's to allow multiple applications to coexist in one VM under one Application class.
|
||||
* Resources are furthor grouped into ResourceGrouping's to allow for logical organization of resources.
|
||||
* Each ResourceGrouping can have multiple 'versions' which we are calling categories, which contain the same resource names and types, but potentially different values.
|
||||
* A resource is referenced via a ResourceReference or resource URL (a slash delimited string beginning with res:// and containing the package name, then the group name, and finally the resource name).
|
||||
* The resource service can be told which categories are the current categories.
|
||||
* The service then builds the mapping of current resources based on the categories and notifies all listeners that the resource values may have changed.
|
||||
* This system allows for resources to be altered based on language and look and feel for the application.
|
||||
*/
|
||||
public abstract class AbstractResourceService {
|
||||
public static final String RESOURCES_FILE_NAME_POSTFIX = ".res";
|
||||
public static final String GROUP_FILE_NAME_PREFIX = "group.";
|
||||
public static final String RESOURCE_FILE_NAME_PREFIX = "resource.";
|
||||
public static final String PACKAGE_NODE_NAME = "package";
|
||||
public static final String PACKAGE_NAME_ATTRIBUTE = "name";
|
||||
public static final String PACKAGE_PATH_ATTRIBUTE = "path";
|
||||
public static final String PACKAGE_HASH_ATTRIBUTE = "hash";
|
||||
public static final String GROUP_NODE_NAME = "group";
|
||||
public static final String GROUP_NAME_ATTRIBUTE = "name";
|
||||
public static final String CATEGORY_NODE_NAME = "category";
|
||||
public static final String CATEGORY_NAME_ATTRIBUTE = "name";
|
||||
public static final String CATEGORY_PATH_ATTRIBUTE = "path";
|
||||
public static final String CATEGORY_HASH_ATTRIBUTE = "hash";
|
||||
public static final String RESOURCE_NODE_NAME = "resource";
|
||||
public static final String RESOURCE_NAME_ATTRIBUTE = "name";
|
||||
public static final String RESOURCE_TYPE_ATTRIBUTE = "type";
|
||||
public static final String RESOURCE_VALUE_ATTRIBUTE = "value";
|
||||
|
||||
/** The set of Resource instances indexed by their resource URL. These are the resources that the users of the service will access. */
|
||||
private IHashMap currentResources = new LiteHashMap(500);
|
||||
/** The set of current categories that built the current resources set. */
|
||||
private String[] currentCategories = null;
|
||||
/** The set of all registered listeners for changes to the set of current resources. */
|
||||
private LiteHashSet listeners = new LiteHashSet(100);
|
||||
/** The optional loader to be used to retrieve the resource contents given a path. */
|
||||
private IResourceLoader resourceLoader = null;
|
||||
/**
|
||||
* AbstractResourceService constructor.
|
||||
*/
|
||||
public AbstractResourceService(IResourceLoader resourceLoader) {
|
||||
super();
|
||||
this.resourceLoader = resourceLoader;
|
||||
}//AbstractResourceService()//
|
||||
/**
|
||||
* Gets the map of current resources.
|
||||
* @return The mapping of Resource instances indexed by their resource URL.
|
||||
*/
|
||||
protected IHashMap getCurrentResources() {
|
||||
return currentResources;
|
||||
}//getCurrentResources()//
|
||||
/**
|
||||
* Gets the current categories.
|
||||
* <p>Warning: This method makes no attempt to hinder modification of the categories since they are for informational purposes only and changes have no affect on the service.</p>
|
||||
* @return The same exact set of categories that was passed to the set method.
|
||||
*/
|
||||
public String[] getCurrentCategories() {
|
||||
return currentCategories;
|
||||
}//getCurrentCategories()//
|
||||
/**
|
||||
* Sets the categories of resources to be loaded as current.
|
||||
* Each category is a chain of slash delimited category parts which are used to search each grouping of resources for resource values.
|
||||
* @param currentCategories The category strings which are slash delimited category chains.
|
||||
*/
|
||||
public void setCurrentCategories(String[] categories) {
|
||||
LiteList categoryLevels = new LiteList(10, 20);
|
||||
StringBuffer resourceUrlBuffer = new StringBuffer(500);
|
||||
IIterator listenerIterator = listeners.iterator();
|
||||
|
||||
getCurrentResources().removeAll();
|
||||
this.currentCategories = categories;
|
||||
|
||||
/*
|
||||
The following code creates a tree of the categories as such...
|
||||
|
||||
Example:
|
||||
categories = new String[] {"a/b/c", "a/z/y", "t/r"}
|
||||
|
||||
The result will be a list of lists (represented here in sudo code for simplicity and clarity):
|
||||
LiteList { LiteList {"a", "t"}, LiteList {"a/b", "a/z", "t/r"}, LiteList {"a/b/c", "a/z/y"} }
|
||||
|
||||
The lists of lists are to be navigated in this order (last to first for the outer list, first to last for the inner list):
|
||||
"a/b/c"
|
||||
"a/z/y"
|
||||
"a/b"
|
||||
"a/z"
|
||||
"t/r"
|
||||
"a"
|
||||
"t"
|
||||
<default category>
|
||||
*/
|
||||
//Build a tree for the categories using a list of lists. This makes it easy to navigate all the end nodes of the tree back toward the root nodes in the order that they were in the passed string array.//
|
||||
for(int categoryIndex = 0; categoryIndex < categories.length; categoryIndex++) {
|
||||
String category = categories[categoryIndex];
|
||||
int nextSlashIndex;
|
||||
int levelIndex = 0;
|
||||
boolean done = false;
|
||||
|
||||
category = category.replace('\\', '/');
|
||||
nextSlashIndex = category.indexOf('/');
|
||||
|
||||
while(!done) {
|
||||
LiteList level = (LiteList) categoryLevels.get(levelIndex);
|
||||
|
||||
if(level == null) {
|
||||
level = new LiteList(10, 20);
|
||||
categoryLevels.set(levelIndex, level);
|
||||
}//if//
|
||||
|
||||
if(nextSlashIndex != -1) {
|
||||
level.add(category.substring(0, nextSlashIndex));
|
||||
levelIndex++;
|
||||
}//if//
|
||||
else {
|
||||
level.add(category);
|
||||
done = true;
|
||||
}//else//
|
||||
}//while//
|
||||
}//for//
|
||||
|
||||
//Navigate the tree and collect the resources.//
|
||||
for(int outerIndex = categoryLevels.getSize() - 1; outerIndex >= 0; outerIndex--) {
|
||||
LiteList levelCategories = (LiteList) categoryLevels.get(outerIndex);
|
||||
|
||||
for(int innerIndex = 0; innerIndex < levelCategories.getSize(); innerIndex++) {
|
||||
String category = (String) levelCategories.get(innerIndex);
|
||||
|
||||
//Add the resources for the category.//
|
||||
addToCurrentResources(category, resourceUrlBuffer);
|
||||
}//for//
|
||||
}//for//
|
||||
|
||||
//Add the default category.//
|
||||
addToCurrentResources("", resourceUrlBuffer);
|
||||
|
||||
//Fire an event notifying all listeners that the resource values may have been altered.//
|
||||
while(listenerIterator.hasNext()) {
|
||||
((IResourceListener) listenerIterator.next()).resourcesChanged();
|
||||
}//while//
|
||||
}//setCurrentCategories()//
|
||||
/**
|
||||
* Adds the resources associated with the given category to the set of current resources.
|
||||
* @param category The category to be searched for.
|
||||
* @param resourceUrlBuffer The reusable buffer for building the resource URL's.
|
||||
*/
|
||||
private void addToCurrentResources(String category, StringBuffer resourceUrlBuffer) {
|
||||
IList resourcePackages = getResourcePackages();
|
||||
|
||||
for(int packageIndex = 0; packageIndex < resourcePackages.getSize(); packageIndex++) {
|
||||
ResourcePackage resourcePackage = (ResourcePackage) resourcePackages.get(packageIndex);
|
||||
//TODO: Find all resource groups that contain this category within the package... add all resources to the group that have not already been filled with a value.
|
||||
IList resourceGroups = getResourceGroups(resourcePackage, category);
|
||||
|
||||
//Iterate over the groups and load the resources.//
|
||||
for(int groupIndex = 0; groupIndex < resourceGroups.getSize(); groupIndex++) {
|
||||
ResourceGroup resourceGroup = (ResourceGroup) resourceGroups.get(groupIndex);
|
||||
ResourceCategory resourceCategory = resourceGroup.getCategory(category);
|
||||
|
||||
//If there is a set of resources for the given category in this group then process them.//
|
||||
if(resourceCategory != null) {
|
||||
IList resources = getResources(resourceCategory);
|
||||
|
||||
//Iterate over the resources and add them the current set of available resources.//
|
||||
for(int resourceIndex = 0; resourceIndex < resources.getSize(); resourceIndex++) {
|
||||
Resource resource = (Resource) resources.get(resourceIndex);
|
||||
String resourceUrl;
|
||||
|
||||
resourceUrlBuffer.setLength(0);
|
||||
resourceUrlBuffer.append(ResourceReference.RESOURCE_URL_PREFIX);
|
||||
resourceUrlBuffer.append(resourcePackage.getName());
|
||||
resourceUrlBuffer.append('/');
|
||||
resourceUrlBuffer.append(resourceGroup.getName());
|
||||
resourceUrlBuffer.append('/');
|
||||
resourceUrlBuffer.append(resource.getName());
|
||||
resourceUrl = resourceUrlBuffer.toString();
|
||||
|
||||
//Only add the resource to the current mapping if one is not already in the mapping.//
|
||||
if(!getCurrentResources().containsKey(resourceUrl)) {
|
||||
getCurrentResources().put(resourceUrl, resource);
|
||||
}//if//
|
||||
}//for//
|
||||
}//if//
|
||||
}//for//
|
||||
}//for//
|
||||
}//addToCurrentResources()//
|
||||
/**
|
||||
* Gets the value for the associated resource reference.
|
||||
* @param resourceReference The reference to the resource.
|
||||
* @return The value for the referenced resource. This will default to null for invalid references.
|
||||
*/
|
||||
public Object getResourceValue(ResourceReference resourceReference) {
|
||||
Resource resource = (Resource) currentResources.get(resourceReference.getResourceUrl());
|
||||
|
||||
return resource != null ? resource.getValue() : null;
|
||||
}//getResourceValue()//
|
||||
/**
|
||||
* Determines whether the resource reference is valid for the current set of resources.
|
||||
* @param resourceReference The reference to the resource.
|
||||
* @return Whether the resource reference can be resolved to a value.
|
||||
*/
|
||||
public boolean isResourceReferenceValid(ResourceReference resourceReference) {
|
||||
return currentResources.containsKey(resourceReference.getResourceUrl());
|
||||
}//isResourceReferenceValid()//
|
||||
/**
|
||||
* Adds a listener to the set of resource change listeners.
|
||||
* @param listener The listener to be notified when changes to the current set of resources is complete.
|
||||
*/
|
||||
public void addListener(IResourceListener listener) {
|
||||
listeners.add(listener);
|
||||
}//addListener()//
|
||||
/**
|
||||
* Removes a listener from the set of resource change listeners.
|
||||
* @param listener The listener to be removed.
|
||||
*/
|
||||
public void removeListener(IResourceListener listener) {
|
||||
listeners.remove(listener);
|
||||
}//removeListener()//
|
||||
/**
|
||||
* Loads the path as an input stream and doesn't single out the compressed resources at the beginning of the file.
|
||||
* <p>Note: This is intended to allow access to the images (and other large resources) stored at the end of the resource file in an uncompressed format.</p>
|
||||
* @param path The path to the resource to be loaded.
|
||||
* @return The input stream containing the data for the resource.
|
||||
* @throws IOException If a problem occurs.
|
||||
*/
|
||||
protected InputStream getRawResourceStream(File path) throws IOException {
|
||||
InputStream result = null;
|
||||
|
||||
if(resourceLoader != null) {
|
||||
result = resourceLoader.load(path);
|
||||
}//if//
|
||||
else {
|
||||
File prefix = getResourcePathPrefix();
|
||||
File file = prefix == null ? path : new File(prefix, path.toString());
|
||||
|
||||
result = new FileInputStream(file);
|
||||
}//else//
|
||||
|
||||
return result;
|
||||
}//getRawResourceStream()//
|
||||
/**
|
||||
* Loads the path as an input stream over the zipped header. Note that the header contains the resource structure and all small resource values.
|
||||
* @param path The path to the resource to be loaded.
|
||||
* @return The input stream containing the data for the resource.
|
||||
* @throws IOException If a problem occurs.
|
||||
*/
|
||||
protected InputStream getResourceStream(File path) throws IOException {
|
||||
InputStream stream;
|
||||
int headerSize;
|
||||
byte[] bytes;
|
||||
|
||||
if(resourceLoader != null) {
|
||||
stream = resourceLoader.load(path);
|
||||
}//if//
|
||||
else {
|
||||
File prefix = getResourcePathPrefix();
|
||||
File file = prefix == null ? path : new File(prefix, path.toString());
|
||||
|
||||
stream = new FileInputStream(file);
|
||||
}//else//
|
||||
|
||||
headerSize = StreamSupport.readInt(stream);
|
||||
bytes = new byte[headerSize];
|
||||
StreamSupport.readBytes(stream, bytes);
|
||||
stream.close();
|
||||
|
||||
return new GZIPInputStream(new ByteArrayInputStream(bytes));
|
||||
}//getResourceStream()//
|
||||
/**
|
||||
* Gets the prefix for the resource path.
|
||||
* @return The prefix to use with the resource path. This should be null if the path is all that is necessary.
|
||||
*/
|
||||
protected File getResourcePathPrefix() {
|
||||
return null;
|
||||
}//getResourcePathPrefix()//
|
||||
/**
|
||||
* Loads the given file and decompresses it.
|
||||
* @param path The relative location of the resources to be loaded.
|
||||
* @return The decompressed file data.
|
||||
* @throws IOException
|
||||
*/
|
||||
protected byte[] loadResourceValue(File path, long offset, int size) throws IOException {
|
||||
byte[] bytes = new byte[size];
|
||||
InputStream in = null;
|
||||
int count = 0;
|
||||
|
||||
try {
|
||||
in = getRawResourceStream(path);
|
||||
//Skip the compressed XML resource data, and the first four bytes that are the size of the compressed XML resource data, and shift to the correct offset.//
|
||||
in.skip(offset + StreamSupport.readInt(in));
|
||||
|
||||
while(count < bytes.length) {
|
||||
count += in.read(bytes, count, bytes.length - count);
|
||||
}//while//
|
||||
}//try//
|
||||
finally {
|
||||
try {
|
||||
in.close();
|
||||
}//try//
|
||||
catch(Throwable e) {}
|
||||
}//finally//
|
||||
|
||||
return bytes;
|
||||
}//loadResourceFile()//
|
||||
/**
|
||||
* Loads the packages metadata from the stream.
|
||||
* @param resourcePackagesPath The path to the file containing the package metadata.
|
||||
* @throws IOException
|
||||
* @throws ClassNotFoundException
|
||||
*/
|
||||
protected IList loadPackages() throws IOException, ClassNotFoundException {
|
||||
String resourcePackagesPath = Application.PACKAGE_RESOURCES_FILE_NAME;
|
||||
LiteList result = new LiteList(20, 100);
|
||||
DocumentBuilder builder = new DocumentBuilder();
|
||||
InputStream in = getResourceStream(new File(resourcePackagesPath));
|
||||
IDocument document = builder.readDocument(in, "UTF8");
|
||||
IIterator iterator = document.getElements().iterator();
|
||||
|
||||
try {
|
||||
in.close();
|
||||
}//try//
|
||||
catch(Throwable e) {}
|
||||
|
||||
while(iterator.hasNext()) {
|
||||
IElement next = (IElement) iterator.next();
|
||||
|
||||
if(next.isNode()) {
|
||||
INode nextNode = (INode) next;
|
||||
|
||||
if(nextNode.getName().equals(PACKAGE_NODE_NAME)) {
|
||||
boolean isValid = true;
|
||||
String name = nextNode.getAttributeValue(PACKAGE_NAME_ATTRIBUTE);
|
||||
String path = nextNode.getAttributeValue(PACKAGE_PATH_ATTRIBUTE);
|
||||
String hash = nextNode.getAttributeValue(PACKAGE_HASH_ATTRIBUTE);
|
||||
|
||||
if((name != null) && (path != null) && (hash != null)) {
|
||||
name = name.trim();
|
||||
path = path.trim();
|
||||
hash = hash.trim();
|
||||
|
||||
if((name.length() > 0) && (path.length() > 0) && (hash.length() > 0)) {
|
||||
result.add(new ResourcePackage(name, path, hash));
|
||||
}//if//
|
||||
}//if//
|
||||
else {
|
||||
isValid = false;
|
||||
}//else//
|
||||
|
||||
if(!isValid) {
|
||||
Debug.log("Warning: Invalid package tag in the resource metadata. All package tags must provide valid name, path, and hash attributes.");
|
||||
}//if//
|
||||
}//if//
|
||||
}//if//
|
||||
}//while//
|
||||
|
||||
return result;
|
||||
}//loadPackages()//
|
||||
/**
|
||||
* Loads the packages metadata from the stream.
|
||||
* @param resourceMetadata The package resource metadata stream.
|
||||
* @throws IOException
|
||||
* @throws ClassNotFoundException
|
||||
*/
|
||||
protected IList loadGroups(ResourcePackage resourcePackage) throws IOException, ClassNotFoundException {
|
||||
LiteList result = new LiteList(20, 100);
|
||||
DocumentBuilder builder = new DocumentBuilder();
|
||||
InputStream in = getResourceStream(new File(resourcePackage.getPath()));
|
||||
IDocument document = builder.readDocument(in, "UTF8");
|
||||
IIterator groupIterator = document.getElements().iterator();
|
||||
|
||||
try {
|
||||
in.close();
|
||||
}//try//
|
||||
catch(Throwable e) {}
|
||||
|
||||
while(groupIterator.hasNext()) {
|
||||
IElement nextGroup = (IElement) groupIterator.next();
|
||||
|
||||
if(nextGroup.isNode()) {
|
||||
INode nextGroupNode = (INode) nextGroup;
|
||||
|
||||
if(nextGroupNode.getName().equals(GROUP_NODE_NAME)) {
|
||||
boolean isGroupValid = true;
|
||||
String groupName = nextGroupNode.getAttributeValue(GROUP_NAME_ATTRIBUTE);
|
||||
|
||||
if(groupName != null) {
|
||||
groupName = groupName.trim();
|
||||
|
||||
if(groupName.length() > 0) {
|
||||
LiteList categories = new LiteList(20, 100);
|
||||
ResourceCategory[] categoryArray;
|
||||
IIterator categoryIterator = nextGroupNode.getElements().iterator();
|
||||
|
||||
while(categoryIterator.hasNext()) {
|
||||
IElement nextCategory = (IElement) categoryIterator.next();
|
||||
|
||||
if(nextCategory.isNode()) {
|
||||
INode nextCategoryNode = (INode) nextCategory;
|
||||
|
||||
if(nextCategoryNode.getName().equals(CATEGORY_NODE_NAME)) {
|
||||
boolean isCategoryValid = true;
|
||||
String categoryName = nextCategoryNode.getAttributeValue(CATEGORY_NAME_ATTRIBUTE);
|
||||
String categoryPath = nextCategoryNode.getAttributeValue(CATEGORY_PATH_ATTRIBUTE);
|
||||
String categoryHash = nextCategoryNode.getAttributeValue(CATEGORY_HASH_ATTRIBUTE);
|
||||
|
||||
if((categoryName != null) && (categoryPath != null) && (categoryHash != null)) {
|
||||
categoryName = categoryName.trim();
|
||||
categoryPath = categoryPath.trim();
|
||||
categoryHash = categoryHash.trim();
|
||||
|
||||
if((categoryPath.length() > 0) && (categoryHash.length() > 0)) {
|
||||
categories.add(new ResourceCategory(categoryName, categoryPath, categoryHash));
|
||||
}//if//
|
||||
}//if//
|
||||
else {
|
||||
isCategoryValid = false;
|
||||
}//else//
|
||||
|
||||
if(!isCategoryValid) {
|
||||
Debug.log("Warning: Invalid category tag in the resource metadata. All category tags must provide valid name, path, and hash attributes.");
|
||||
}//if//
|
||||
}//if//
|
||||
}//if//
|
||||
}//while//
|
||||
|
||||
categoryArray = new ResourceCategory[categories.getSize()];
|
||||
categories.toArray(categoryArray);
|
||||
result.add(new ResourceGroup(groupName, categoryArray));
|
||||
}//if//
|
||||
}//if//
|
||||
else {
|
||||
isGroupValid = false;
|
||||
}//else//
|
||||
|
||||
if(!isGroupValid) {
|
||||
Debug.log("Warning: Invalid group tag in the resource metadata. All group tags must provide valid name attribute.");
|
||||
}//if//
|
||||
}//if//
|
||||
}//if//
|
||||
}//while//
|
||||
|
||||
return result;
|
||||
}//loadGroups()//
|
||||
/**
|
||||
* Loads the resources from file. This does not load images though - they are left in the file and lazily loaded later.
|
||||
* <p>TODO: The file should be split up and the images should be loaded from a separate but related file. Right now we load all the image data into memory just to read everything else.</p>
|
||||
* @param resourceMetadata The package resource metadata stream.
|
||||
* @throws IOException
|
||||
* @throws ClassNotFoundException
|
||||
*/
|
||||
protected IList loadResources(ResourceCategory resourceCategory) throws IOException, ClassNotFoundException {
|
||||
LiteList result = new LiteList(20, 100);
|
||||
DocumentBuilder builder = new DocumentBuilder();
|
||||
InputStream in = getResourceStream(new File(resourceCategory.getPath()));
|
||||
IDocument document = builder.readDocument(in, "UTF8");
|
||||
IIterator resourceIterator = document.getElements().iterator();
|
||||
|
||||
try {
|
||||
in.close();
|
||||
}//try//
|
||||
catch(Throwable e) {}
|
||||
|
||||
while(resourceIterator.hasNext()) {
|
||||
IElement next = (IElement) resourceIterator.next();
|
||||
|
||||
if(next.isNode()) {
|
||||
INode nextNode = (INode) next;
|
||||
|
||||
if(nextNode.getName().equals(RESOURCE_NODE_NAME)) {
|
||||
boolean isValid = true;
|
||||
String resourceName = nextNode.getAttributeValue(RESOURCE_NAME_ATTRIBUTE);
|
||||
String resourceType = nextNode.getAttributeValue(RESOURCE_TYPE_ATTRIBUTE);
|
||||
String resourceValue = nextNode.getAttributeValue(RESOURCE_VALUE_ATTRIBUTE);
|
||||
|
||||
if((resourceName != null) && (resourceType != null) && (resourceValue != null)) {
|
||||
resourceName = resourceName.trim();
|
||||
resourceType = resourceType.trim();
|
||||
|
||||
if((resourceName.length() > 0) && (resourceType.length() > 0)) {
|
||||
try {
|
||||
int type = Integer.parseInt(resourceType);
|
||||
Object value = resourceValue;
|
||||
Long offset = null;
|
||||
int[] lengths = null;
|
||||
|
||||
switch(type) {
|
||||
case IResource.TYPE_BOOLEAN: {
|
||||
value = Boolean.parseBoolean(resourceValue) ? Boolean.TRUE : Boolean.FALSE;
|
||||
break;
|
||||
}//case//
|
||||
case IResource.TYPE_INTEGER: {
|
||||
value = new Integer(resourceValue);
|
||||
break;
|
||||
}//case//
|
||||
case IResource.TYPE_POSITIVE_INTEGER: {
|
||||
value = new Integer(resourceValue);
|
||||
break;
|
||||
}//case//
|
||||
case IResource.TYPE_FLOAT: {
|
||||
value = new Float(resourceValue);
|
||||
break;
|
||||
}//case//
|
||||
case IResource.TYPE_POSITIVE_FLOAT: {
|
||||
value = new Float(resourceValue);
|
||||
break;
|
||||
}//case//
|
||||
case IResource.TYPE_LONG: {
|
||||
value = new Long(resourceValue);
|
||||
break;
|
||||
}//case//
|
||||
case IResource.TYPE_POSITIVE_LONG: {
|
||||
value = new Long(resourceValue);
|
||||
break;
|
||||
}//case//
|
||||
case IResource.TYPE_DOUBLE: {
|
||||
value = new Double(resourceValue);
|
||||
break;
|
||||
}//case//
|
||||
case IResource.TYPE_POSITIVE_DOUBLE: {
|
||||
value = new Double(resourceValue);
|
||||
break;
|
||||
}//case//
|
||||
case IResource.TYPE_STRING: {
|
||||
value = resourceValue;
|
||||
break;
|
||||
}//case//
|
||||
case IResource.TYPE_FONT: {
|
||||
value = JefFont.getJefFonts(resourceValue);
|
||||
break;
|
||||
}//case//
|
||||
case IResource.TYPE_IMAGE: {
|
||||
byte[] bytes = StringSupport.fromHexString(resourceValue);
|
||||
|
||||
offset = new Long(StreamSupport.readLong(bytes, 0));
|
||||
lengths = new int[] {StreamSupport.readInt(bytes, 16)};
|
||||
value = null;
|
||||
break;
|
||||
}//case//
|
||||
case IResource.TYPE_IMAGES: {
|
||||
byte[] bytes = StringSupport.fromHexString(resourceValue);
|
||||
int byteOffset = 16;
|
||||
int index = 0;
|
||||
|
||||
offset = new Long(StreamSupport.readLong(bytes, 0));
|
||||
lengths = new int[(bytes.length - 16) / 8];
|
||||
|
||||
while(byteOffset < bytes.length) {
|
||||
lengths[index++] = StreamSupport.readInt(bytes, byteOffset);
|
||||
byteOffset += 8;
|
||||
}//while//
|
||||
|
||||
value = null;
|
||||
break;
|
||||
}//case//
|
||||
case IResource.TYPE_COLOR: {
|
||||
value = new JefColor(resourceValue);
|
||||
break;
|
||||
}//case//
|
||||
case IResource.TYPE_GRADIENT: {
|
||||
value = new JefGradient(resourceValue);
|
||||
break;
|
||||
}//case//
|
||||
case IResource.TYPE_CHARACTER: {
|
||||
value = resourceValue.length() > 0 ? new Character(resourceValue.charAt(0)) : null;
|
||||
break;
|
||||
}//case//
|
||||
default: {
|
||||
Debug.log(new RuntimeException("Unexpected IResource type code: " + type));
|
||||
isValid = false;
|
||||
break;
|
||||
}//default//
|
||||
}//switch//
|
||||
|
||||
if(isValid) {
|
||||
result.add(new Resource(resourceName, new Integer(type), value, resourceCategory.getPath(), offset, lengths, this));
|
||||
}//if//
|
||||
}//try//
|
||||
catch(NumberFormatException e) {
|
||||
isValid = false;
|
||||
}//catch//
|
||||
}//if//
|
||||
}//if//
|
||||
else {
|
||||
isValid = false;
|
||||
}//else//
|
||||
|
||||
if(!isValid) {
|
||||
Debug.log("Warning: Invalid resource tag. All resource tags must provide valid name, type, and value attributes.");
|
||||
}//if//
|
||||
}//if//
|
||||
}//if//
|
||||
}//while//
|
||||
|
||||
return result;
|
||||
}//loadResources()//
|
||||
/**
|
||||
* Gets the collection of all known resource packages.
|
||||
* @return The set of known resource packages in any order.
|
||||
*/
|
||||
protected abstract IList getResourcePackages();
|
||||
/**
|
||||
* Gets the set of all ResourceGroup instances that have resources under the given category (exact match) and exist in the given resource package.
|
||||
* @param resourcePackage The resource package that the groups must exist in.
|
||||
* @param category The category that the resource group must define resources under.
|
||||
* @return The collection of all matching ResourceGroup instances.
|
||||
*/
|
||||
protected abstract IList getResourceGroups(ResourcePackage resourcePackage, String category);
|
||||
/**
|
||||
* Gets the set of all Resource instances that exist in the given resource package and resource group.
|
||||
* @param resourceCategory The package's group's category whose resources we are looking for.
|
||||
* @return The collection of all matching ResourceGroup instances.
|
||||
*/
|
||||
protected abstract IList getResources(ResourceCategory resourceCategory);
|
||||
/**
|
||||
* Gets the compressed and serialized contents of the resource package file.
|
||||
* @return The compressed and serialized resource package data.
|
||||
*/
|
||||
public abstract byte[] getResourcePackageData();
|
||||
/**
|
||||
* Gets the compressed and serialized contents of the resource group file for the given resource package.
|
||||
* @param resourcePackage The package whose group data is to be retreived.
|
||||
* @return The compressed and serialized resource group data.
|
||||
*/
|
||||
public abstract byte[] getResourceGroupData(ResourcePackage resourcePackage);
|
||||
/**
|
||||
* Gets the compressed and serialized contents of the resource file for the given resource category.
|
||||
* @param resourceCategory The group category whose resource data is to be retreived.
|
||||
* @return The compressed and serialized resource data.
|
||||
*/
|
||||
public abstract byte[] getResourceData(ResourceCategory resourceCategory);
|
||||
/**
|
||||
* Gets the name and hashcode pairs for each code resource required on the client.
|
||||
* @return The array containing names and hashcodes (name, hashcode, name, ...) for the code resources required by the client.
|
||||
*/
|
||||
public abstract String[] getCodeMetadata();
|
||||
/**
|
||||
* Gets the stream of bytes for the requested code resource.
|
||||
* @param name The name of the code resource required by the client.
|
||||
* @return The code resource bytes.
|
||||
*/
|
||||
public abstract byte[] getCodeData(String name);
|
||||
}//AbstractResourceService//
|
||||
47
Foundation/src/com/foundation/view/resource/IResource.java
Normal file
47
Foundation/src/com/foundation/view/resource/IResource.java
Normal file
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright (c) 2005,2009 Declarative Engineering LLC.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Declarative Engineering LLC
|
||||
* verson 1 which accompanies this distribution, and is available at
|
||||
* http://declarativeengineering.com/legal/DE_Developer_License_v1.txt
|
||||
*/
|
||||
package com.foundation.view.resource;
|
||||
|
||||
import com.common.comparison.Comparator;
|
||||
import com.common.util.LiteHashMap;
|
||||
|
||||
/**
|
||||
* Defines some resource constants used by the ResourceValue and ResourceMetadata classes.
|
||||
*/
|
||||
public interface IResource {
|
||||
public static final int TYPE_BOOLEAN = 0;
|
||||
public static final int TYPE_INTEGER = 1;
|
||||
public static final int TYPE_POSITIVE_INTEGER = 2;
|
||||
public static final int TYPE_FLOAT = 3;
|
||||
public static final int TYPE_POSITIVE_FLOAT = 4;
|
||||
public static final int TYPE_LONG = 5;
|
||||
public static final int TYPE_POSITIVE_LONG = 6;
|
||||
public static final int TYPE_DOUBLE = 7;
|
||||
public static final int TYPE_POSITIVE_DOUBLE = 8;
|
||||
public static final int TYPE_STRING = 9;
|
||||
/** A single ImageData instance. */
|
||||
public static final int TYPE_IMAGE = 10;
|
||||
/** Stores an array of JefFont instances (JefFont[]). */
|
||||
public static final int TYPE_FONT = 11;
|
||||
/** Stores a single JefColor instance. */
|
||||
public static final int TYPE_COLOR = 12;
|
||||
public static final int TYPE_CHARACTER = 13;
|
||||
public static final int TYPE_DATE = 14;
|
||||
/** Stores a single JefGradient instance. */
|
||||
public static final int TYPE_GRADIENT = 15;
|
||||
/** Stores an ICollection of ImageData instances. */
|
||||
public static final int TYPE_IMAGES = 16;
|
||||
|
||||
/** Names that map to the type numbers defined in this interface. */
|
||||
public static final String[] TYPE_NAMES = new String[] {
|
||||
"boolean", "integer", "positive-integer", "float", "positive-float", "long", "positive-long", "double", "positive-double",
|
||||
"string", "image", "font", "color", "character", "date", "gradient", "images"
|
||||
};
|
||||
|
||||
public static final LiteHashMap typeNameToNumberMap = new LiteHashMap(TYPE_NAMES.length, LiteHashMap.DEFAULT_LOAD_FACTOR, Comparator.getLogicalComparator(), Comparator.getLogicalComparator(), TYPE_NAMES, null, false);
|
||||
}//IResource//
|
||||
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
* Copyright (c) 2005,2006 Declarative Engineering LLC.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Declarative Engineering LLC
|
||||
* verson 1 which accompanies this distribution, and is available at
|
||||
* http://declarativeengineering.com/legal/DE_Developer_License_v1.txt
|
||||
*/
|
||||
package com.foundation.view.resource;
|
||||
|
||||
/**
|
||||
* Identifies a listener to resource changes. Normally this is a view component which populates its properties using a resource.
|
||||
*/
|
||||
public interface IResourceListener {
|
||||
/**
|
||||
* Called when the set of current resources is altered and resources may have changed their values.
|
||||
*/
|
||||
public void resourcesChanged();
|
||||
}//IResourceListener//
|
||||
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
* Copyright (c) 2008,2009 Declarative Engineering LLC.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Declarative Engineering LLC
|
||||
* verson 1 which accompanies this distribution, and is available at
|
||||
* http://declarativeengineering.com/legal/DE_Developer_License_v1.txt
|
||||
*/
|
||||
package com.foundation.view.resource;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.InputStream;
|
||||
|
||||
public interface IResourceLoader {
|
||||
/**
|
||||
* Loads an input stream for the given path to the resource file.
|
||||
* @param path The path to the resource file (relative).
|
||||
* @return A stream of the contents of the file.
|
||||
*/
|
||||
public InputStream load(File path);
|
||||
}//IResourceLoader//
|
||||
142
Foundation/src/com/foundation/view/resource/Resource.java
Normal file
142
Foundation/src/com/foundation/view/resource/Resource.java
Normal file
@@ -0,0 +1,142 @@
|
||||
/*
|
||||
* Copyright (c) 2005,2009 Declarative Engineering LLC.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Declarative Engineering LLC
|
||||
* verson 1 which accompanies this distribution, and is available at
|
||||
* http://declarativeengineering.com/legal/DE_Developer_License_v1.txt
|
||||
*/
|
||||
package com.foundation.view.resource;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import com.common.debug.Debug;
|
||||
import com.foundation.metadata.Attribute;
|
||||
import com.foundation.model.Model;
|
||||
import com.foundation.view.JefImage;
|
||||
|
||||
/**
|
||||
* This class is not immutable. The value may be loaded on demand for large values. This should should not be a problem since the expected uses are within a single threaded context.
|
||||
*/
|
||||
public class Resource extends Model implements IResource {
|
||||
/** The resource's name which is used to reference the resource. */
|
||||
public static final Attribute NAME = registerAttribute(Resource.class, "name");
|
||||
/** The resource type which defines the value's type. */
|
||||
public static final Attribute TYPE = registerAttribute(Resource.class, "type");
|
||||
/** The actual value for the resource. */
|
||||
public static final Attribute VALUE = registerAttribute(Resource.class, "value", AO_LAZY);
|
||||
/** The offset from the start of the file to where the normally uncompressed value starts. This will be null for most resource types, but may be used for very large values (images currently). */
|
||||
public static final Attribute OFFSET = registerAttribute(Resource.class, "offset");
|
||||
/** The byte length for the value(s). This will be null for most resource types, but may be used for very large values. */
|
||||
public static final Attribute LENGTHS = registerAttribute(Resource.class, "lengths");
|
||||
/** The file containing the value. This is only non-null for large values where an index and length are given. */
|
||||
public static final Attribute PATH = registerAttribute(Resource.class, "path");
|
||||
/** The resource service used to load a file to lazy load the value. */
|
||||
private AbstractResourceService resourceService = null;
|
||||
/**
|
||||
* Resource constructor.
|
||||
*/
|
||||
public Resource() {
|
||||
super();
|
||||
}//Resource()//
|
||||
/**
|
||||
* Resource constructor.
|
||||
* @param name The unique (within the resource group) name of the resource.
|
||||
* @param type The resource type which defines the value's type.
|
||||
* @param value The resource's stored value.
|
||||
* @param file The optional file containing the value for types that may have large values.
|
||||
* @param index The optional index to the first byte of extended data for the value for types that may have large values.
|
||||
* @param lengths The optional number of bytes of extended data for types that may have large values.
|
||||
*/
|
||||
public Resource(String name, Integer type, Object value, String path, Long offset, int[] lengths, AbstractResourceService resourceService) {
|
||||
super();
|
||||
setAttributeValue(NAME, name);
|
||||
setAttributeValue(TYPE, type);
|
||||
|
||||
if(offset == null) {
|
||||
setAttributeValue(VALUE, value);
|
||||
}//if//
|
||||
else {
|
||||
this.resourceService = resourceService;
|
||||
setAttributeValue(PATH, path);
|
||||
setAttributeValue(OFFSET, offset);
|
||||
setAttributeValue(LENGTHS, lengths);
|
||||
}//else//
|
||||
}//Resource()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.common.Entity#lazyLoadAttribute(com.foundation.metadata.Attribute)
|
||||
*/
|
||||
protected Object lazyLoadAttribute(Attribute attribute) {
|
||||
Object result = null;
|
||||
|
||||
if(attribute == VALUE) {
|
||||
if((getType().intValue() == IResource.TYPE_IMAGE || getType().intValue() == IResource.TYPE_IMAGES) && (getIsAttributeRealized(OFFSET) && getIsAttributeRealized(LENGTHS))) {
|
||||
try {
|
||||
int[] lengths = getLengths();
|
||||
long offset = getOffset().longValue();
|
||||
|
||||
if(lengths.length > 1) {
|
||||
result = new JefImage[lengths.length];
|
||||
|
||||
for(int index = 0; index < lengths.length; index++) {
|
||||
((JefImage[]) result)[index] = new JefImage(resourceService.loadResourceValue(new File(getPath()), offset, lengths[index]));
|
||||
offset += lengths[index];
|
||||
}//for//
|
||||
}//if//
|
||||
else if(lengths.length == 1) {
|
||||
result = new JefImage(resourceService.loadResourceValue(new File(getPath()), offset, lengths[0]));
|
||||
}//else if//
|
||||
}//try//
|
||||
catch(Throwable e) {
|
||||
Debug.log(e);
|
||||
}//catch//
|
||||
}//if//
|
||||
}//if//
|
||||
else {
|
||||
result = super.lazyLoadAttribute(attribute);
|
||||
}//else//
|
||||
|
||||
return result;
|
||||
}//lazyLoadAttribute()//
|
||||
/**
|
||||
* Gets the resource's name which is used to reference the resource from within the application.
|
||||
* @return The unique (within the resource group) name of the resource.
|
||||
*/
|
||||
public String getName() {
|
||||
return (String) getAttributeValue(NAME);
|
||||
}//getName()//
|
||||
/**
|
||||
* Gets the type code for the resource's value.
|
||||
* @return The resource type which defines the value's type.
|
||||
*/
|
||||
public Integer getType() {
|
||||
return (Integer) getAttributeValue(TYPE);
|
||||
}//getType()//
|
||||
/**
|
||||
* Gets the resource's value.
|
||||
* @return The value for the resource.
|
||||
*/
|
||||
public Object getValue() {
|
||||
return getAttributeValue(VALUE);
|
||||
}//getValue()//
|
||||
/**
|
||||
* Gets the relative path to the system resource containing the resource value.
|
||||
* @return The location of the resource.
|
||||
*/
|
||||
private String getPath() {
|
||||
return (String) getAttributeValue(PATH);
|
||||
}//getFile()//
|
||||
/**
|
||||
* Gets the file offset (from the beginning of the file) of the start of the resource value.
|
||||
* @return The file offset of the value.
|
||||
*/
|
||||
private Long getOffset() {
|
||||
return (Long) getAttributeValue(OFFSET);
|
||||
}//getOffset()//
|
||||
/**
|
||||
* Gets the length of the data in the file containing the resource.
|
||||
* @return The byte length of the data for the resource value.
|
||||
*/
|
||||
private int[] getLengths() {
|
||||
return (int[]) getAttributeValue(LENGTHS);
|
||||
}//getLength()//
|
||||
}//Resource//
|
||||
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Copyright (c) 2006,2009 Declarative Engineering LLC.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Declarative Engineering LLC
|
||||
* verson 1 which accompanies this distribution, and is available at
|
||||
* http://declarativeengineering.com/legal/DE_Developer_License_v1.txt
|
||||
*/
|
||||
package com.foundation.view.resource;
|
||||
|
||||
import com.foundation.metadata.Attribute;
|
||||
import com.foundation.model.Model;
|
||||
|
||||
/**
|
||||
* The category divides a group of resources such that a resource can have multiple values for different L&F's and languages.
|
||||
* <p>This class is immutable.</p>
|
||||
*/
|
||||
public class ResourceCategory extends Model {
|
||||
/** The resource category's name. */
|
||||
public static final Attribute NAME = registerAttribute(ResourceCategory.class, "name");
|
||||
/** The path where the compressed group's category data is stored. */
|
||||
public static final Attribute PATH = registerAttribute(ResourceCategory.class, "path");
|
||||
/** The hash of the compressed group's category data. */
|
||||
public static final Attribute HASH = registerAttribute(ResourceCategory.class, "hash");
|
||||
/**
|
||||
* ResourceCategory constructor.
|
||||
* <p>Warning: This constructor is only public to allow for serialization and cloning.</p>
|
||||
*/
|
||||
public ResourceCategory() {
|
||||
super();
|
||||
}//ResourceCategory()//
|
||||
/**
|
||||
* ResourceCategory constructor.
|
||||
* @param name The category name.
|
||||
* @param path The path to the group's category data.
|
||||
* @param hash The hash of the group's category data.
|
||||
*/
|
||||
public ResourceCategory(String name, String path, String hash) {
|
||||
super();
|
||||
setAttributeValue(NAME, name);
|
||||
setAttributeValue(PATH, path);
|
||||
setAttributeValue(HASH, hash);
|
||||
}//ResourceCategory()//
|
||||
/**
|
||||
* Gets the resource package's name which is used to reference the package from within the application.
|
||||
* @return The unique name of the resource package.
|
||||
*/
|
||||
public String getName() {
|
||||
return (String) getAttributeValue(NAME);
|
||||
}//getName()//
|
||||
/**
|
||||
* Gets the path where the compressed resource group data for this category is stored.
|
||||
* @return The resource group's category's data storage path. This is relative to the resource root path held by the resource service.
|
||||
*/
|
||||
public String getPath() {
|
||||
return (String) getAttributeValue(PATH);
|
||||
}//getPath()//
|
||||
/**
|
||||
* Gets the hash of the compressed resource group data for this category.
|
||||
* @return The resource group's category's data hash.
|
||||
*/
|
||||
public String getHash() {
|
||||
return (String) getAttributeValue(HASH);
|
||||
}//getHash()//
|
||||
}//ResourceCategory//
|
||||
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Copyright (c) 2005,2006 Declarative Engineering LLC.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Declarative Engineering LLC
|
||||
* verson 1 which accompanies this distribution, and is available at
|
||||
* http://declarativeengineering.com/legal/DE_Developer_License_v1.txt
|
||||
*/
|
||||
package com.foundation.view.resource;
|
||||
|
||||
import com.foundation.metadata.Attribute;
|
||||
import com.foundation.model.Model;
|
||||
|
||||
/**
|
||||
* A grouping of resources which are bundled together.
|
||||
* The grouping may be defined for multiple application defined categories which allows for rapid switching between languages and L&F's.
|
||||
*/
|
||||
public class ResourceGroup extends Model {
|
||||
/** The resource package's name which is used to reference the package. */
|
||||
public static final Attribute NAME = registerAttribute(ResourceGroup.class, "name");
|
||||
/** The categories that define data for this group. */
|
||||
public static final Attribute CATEGORIES = registerAttribute(ResourceGroup.class, "categories");
|
||||
/**
|
||||
* ResourceGroup constructor.
|
||||
*/
|
||||
public ResourceGroup() {
|
||||
super();
|
||||
}//ResourceGroup()//
|
||||
/**
|
||||
* ResourceGroup constructor.
|
||||
* @param name The unique name of the resource group.
|
||||
* @param categories The categories that define data for this group.
|
||||
*/
|
||||
public ResourceGroup(String name, ResourceCategory[] categories) {
|
||||
super();
|
||||
setAttributeValue(NAME, name);
|
||||
setAttributeValue(CATEGORIES, categories);
|
||||
}//ResourceGroup()//
|
||||
/**
|
||||
* Gets the resource group's name which is used to reference the resource from within the application.
|
||||
* @return The unique name of the resource group.
|
||||
*/
|
||||
public String getName() {
|
||||
return (String) getAttributeValue(NAME);
|
||||
}//getName()//
|
||||
/**
|
||||
* Gets the set of all categories defined for this group.
|
||||
* @return The category metadata for the categories defined for this resource group.
|
||||
*/
|
||||
public ResourceCategory[] getCategories() {
|
||||
return (ResourceCategory[]) getAttributeValue(CATEGORIES);
|
||||
}//getCategories()//
|
||||
/**
|
||||
* Gets the category metadata for the category name.
|
||||
* @param category The name of the category to find under this resource group.
|
||||
* @return The category that matches the given category name.
|
||||
*/
|
||||
public ResourceCategory getCategory(String category) {
|
||||
ResourceCategory[] categories = getCategories();
|
||||
ResourceCategory result = null;
|
||||
|
||||
for(int index = 0; (result == null) && (index < categories.length); index++) {
|
||||
if(categories[index].getName().equals(category)) {
|
||||
result = categories[index];
|
||||
}//if//
|
||||
}//for//
|
||||
|
||||
return result;
|
||||
}//getCategory()//
|
||||
}//ResourceGroup//
|
||||
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* Copyright (c) 2005,2009 Declarative Engineering LLC.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Declarative Engineering LLC
|
||||
* verson 1 which accompanies this distribution, and is available at
|
||||
* http://declarativeengineering.com/legal/DE_Developer_License_v1.txt
|
||||
*/
|
||||
package com.foundation.view.resource;
|
||||
|
||||
import java.io.IOException;
|
||||
import com.common.io.*;
|
||||
|
||||
/*
|
||||
* Describes a grouping of resources in an application.
|
||||
* <p>Note: This metadata is not sent to remote clients, it is purely used for setting up the resource structure, determining when to send resources, and for tools that will allow resources to be initialized.</p>
|
||||
*/
|
||||
public class ResourceGroupMetadata {
|
||||
/** The resource group's name which is used to reference the resource. */
|
||||
private String name = null;
|
||||
/** The description of the resource group. */
|
||||
private String description = null;
|
||||
/** Whether the resource group will be cached for remote clients. */
|
||||
private boolean isCached = false;
|
||||
/**
|
||||
* ResourceGroupMetadata constructor.
|
||||
*/
|
||||
public ResourceGroupMetadata() {
|
||||
super();
|
||||
}//ResourceGroupMetadata()//
|
||||
/**
|
||||
* ResourceGroupMetadata constructor.
|
||||
* @param name The unique name of the resource group.
|
||||
* @param description The human readable description which gives an indication of the resource group's usage.
|
||||
* @param isCached Whether the resource group will be cached.
|
||||
*/
|
||||
public ResourceGroupMetadata(String name, String description, boolean isCached) {
|
||||
super();
|
||||
this.name = name;
|
||||
this.description = description;
|
||||
this.isCached = isCached;
|
||||
}//ResourceGroupMetadata()//
|
||||
/**
|
||||
* Gets the resource group's name which is used to reference the resource from within the application.
|
||||
* @return The unique name of the resource group.
|
||||
*/
|
||||
public String getName() {
|
||||
return name;
|
||||
}//getName()//
|
||||
/**
|
||||
* Gets the description for the resource group.
|
||||
* @return The human readable description which gives an indication of the resource group's usage.
|
||||
*/
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}//getDescription()//
|
||||
/**
|
||||
* Determines whether the resource may be cached for remote clients.
|
||||
* @return Whether the resource group should be cached.
|
||||
*/
|
||||
public boolean isCached() {
|
||||
return isCached;
|
||||
}//isCached()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.common.io.IExternalizable#readExternal(com.common.io.IObjectInputStream)
|
||||
*/
|
||||
public void readExternal(IObjectInputStream in) throws IOException, ClassNotFoundException {
|
||||
/*byte version = */in.readByte();
|
||||
|
||||
name = (String) in.readObject();
|
||||
description = (String) in.readObject();
|
||||
isCached = in.readBoolean();
|
||||
}//readExternal()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.common.io.IExternalizable#writeExternal(com.common.io.IObjectOutputStream)
|
||||
*/
|
||||
public void writeExternal(IObjectOutputStream out) throws IOException {
|
||||
out.writeByte(0);
|
||||
out.writeObject(name);
|
||||
out.writeObject(description);
|
||||
out.writeBoolean(isCached);
|
||||
}//writeExternal()//
|
||||
}//ResourceGroupMetadata//
|
||||
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* Copyright (c) 2005,2006 Declarative Engineering LLC.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Declarative Engineering LLC
|
||||
* verson 1 which accompanies this distribution, and is available at
|
||||
* http://declarativeengineering.com/legal/DE_Developer_License_v1.txt
|
||||
*/
|
||||
package com.foundation.view.resource;
|
||||
|
||||
import java.io.IOException;
|
||||
import com.common.io.*;
|
||||
|
||||
/*
|
||||
* Describes a single resource in an application.
|
||||
* <p>Note: This metadata is not sent to remote clients, it is purely used for setting up the resource structure, determining when to send resources, and for tools that will allow resources to be initialized.</p>
|
||||
*/
|
||||
public class ResourceMetadata implements IExternalizable, IResource {
|
||||
/** The resource's name which is used to reference the resource. */
|
||||
private String name = null;
|
||||
/** The description of the resource. */
|
||||
private String description = null;
|
||||
/** The resource value's type. */
|
||||
private int type = TYPE_STRING;
|
||||
/**
|
||||
* ResourceMetadata constructor.
|
||||
*/
|
||||
public ResourceMetadata() {
|
||||
super();
|
||||
}//ResourceMetadata()//
|
||||
/**
|
||||
* ResourceMetadata constructor.
|
||||
* @param name The unique (within the resource group) name of the resource.
|
||||
* @param description The human readable description which gives an indication of the resource's usage.
|
||||
* @param type The resource value's type which must be one of the type constants defined by this class.
|
||||
*/
|
||||
public ResourceMetadata(String name, String description, int type) {
|
||||
super();
|
||||
this.name = name;
|
||||
this.description = description;
|
||||
this.type = type;
|
||||
}//ResourceMetadata()//
|
||||
/**
|
||||
* Gets the resource's name which is used to reference the resource from within the application.
|
||||
* @return The unique (within the resource group) name of the resource.
|
||||
*/
|
||||
public String getName() {
|
||||
return name;
|
||||
}//getName()//
|
||||
/**
|
||||
* Gets the description for the resource.
|
||||
* @return The human readable description which gives an indication of the resource's usage.
|
||||
*/
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}//getDescription()//
|
||||
/**
|
||||
* Gets the type of the resource value.
|
||||
* @return The resource value's type which will be one of the type constants defined by this class.
|
||||
*/
|
||||
public int getType() {
|
||||
return type;
|
||||
}//getType()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.common.io.IExternalizable#readExternal(com.common.io.IObjectInputStream)
|
||||
*/
|
||||
public Object readExternal(IObjectInputStream in) throws IOException, ClassNotFoundException {
|
||||
/*byte version = */in.readByte();
|
||||
|
||||
name = (String) in.readObject();
|
||||
description = (String) in.readObject();
|
||||
type = in.readInt();
|
||||
|
||||
return null;
|
||||
}//readExternal()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.common.io.IExternalizable#writeExternal(com.common.io.IObjectOutputStream)
|
||||
*/
|
||||
public void writeExternal(IObjectOutputStream out) throws IOException {
|
||||
out.writeByte(0);
|
||||
out.writeObject(name);
|
||||
out.writeObject(description);
|
||||
out.writeInt(type);
|
||||
}//writeExternal()//
|
||||
}//ResourceMetadata//
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Copyright (c) 2006,2009 Declarative Engineering LLC.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Declarative Engineering LLC
|
||||
* verson 1 which accompanies this distribution, and is available at
|
||||
* http://declarativeengineering.com/legal/DE_Developer_License_v1.txt
|
||||
*/
|
||||
package com.foundation.view.resource;
|
||||
|
||||
import com.foundation.metadata.Attribute;
|
||||
import com.foundation.model.Model;
|
||||
|
||||
/**
|
||||
* Represents a package of resources organized by groupings and categories.
|
||||
* A package contains a set of groupings which can have different resource values based on the selected category.
|
||||
* This allows for L&F and language settings to change which resources are used for each grouping.
|
||||
* <p>This class is immutable.</p>
|
||||
*/
|
||||
public class ResourcePackage extends Model {
|
||||
/** The resource package's name which is used to reference the package. */
|
||||
public static final Attribute NAME = registerAttribute(ResourcePackage.class, "name");
|
||||
/** The path where the compressed package data is stored. */
|
||||
public static final Attribute PATH = registerAttribute(ResourcePackage.class, "path");
|
||||
/** The hash of the compressed package data as a hex string. */
|
||||
public static final Attribute HASH = registerAttribute(ResourcePackage.class, "hash");
|
||||
/**
|
||||
* ResourcePackage constructor.
|
||||
* <p>Warning: This constructor is only public to allow for serialization and cloning.</p>
|
||||
*/
|
||||
public ResourcePackage() {
|
||||
super();
|
||||
}//ResourcePackage()//
|
||||
/**
|
||||
* ResourcePackage constructor.
|
||||
* @param name The package name.
|
||||
* @param path The path to the package data.
|
||||
* @param hash The hash of the package data.
|
||||
*/
|
||||
public ResourcePackage(String name, String path, String hash) {
|
||||
super();
|
||||
setAttributeValue(NAME, name);
|
||||
setAttributeValue(PATH, path);
|
||||
setAttributeValue(HASH, hash);
|
||||
}//ResourcePackage()//
|
||||
/**
|
||||
* Gets the resource package's name which is used to reference the package from within the application.
|
||||
* @return The unique name of the resource package.
|
||||
*/
|
||||
public String getName() {
|
||||
return (String) getAttributeValue(NAME);
|
||||
}//getName()//
|
||||
/**
|
||||
* Gets the path where the compressed resource package data is stored.
|
||||
* @return The resource package's data storage path. This is relative to the resource root path held by the resource service.
|
||||
*/
|
||||
public String getPath() {
|
||||
return (String) getAttributeValue(PATH);
|
||||
}//getPath()//
|
||||
/**
|
||||
* Gets the hash of the compressed resource package data.
|
||||
* @return The resource package's data hash.
|
||||
*/
|
||||
public String getHash() {
|
||||
return (String) getAttributeValue(HASH);
|
||||
}//getHash()//
|
||||
}//ResourcePackage//
|
||||
@@ -0,0 +1,175 @@
|
||||
/*
|
||||
* Copyright (c) 2006,2007 Declarative Engineering LLC.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Declarative Engineering LLC
|
||||
* verson 1 which accompanies this distribution, and is available at
|
||||
* http://declarativeengineering.com/legal/DE_Developer_License_v1.txt
|
||||
*/
|
||||
package com.foundation.view.resource;
|
||||
|
||||
import java.io.Externalizable;
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectInput;
|
||||
import java.io.ObjectOutput;
|
||||
|
||||
/*
|
||||
* Encapsulates a reference to a resource which resides in a group in a package.
|
||||
*/
|
||||
public class ResourceReference implements Externalizable, Cloneable {
|
||||
/** The case insensitive prefix for a resource url. */
|
||||
public static final String RESOURCE_URL_PREFIX = "res://";
|
||||
public static final String RESOURCE_URL_PREFIX_ALT = "res:\\\\";
|
||||
|
||||
private String resourceUrl = null;
|
||||
private String packageName = null;
|
||||
private String groupName = null;
|
||||
private String resourceName = null;
|
||||
/**
|
||||
* JefResourceReference constructor.
|
||||
* <p>Warning: This constructor provided for serialization and cloning only.</p>
|
||||
*/
|
||||
public ResourceReference() {
|
||||
super();
|
||||
}//JefResourceReference()//
|
||||
/**
|
||||
* JefResourceReference constructor.
|
||||
* @param resourceUrl The URL of the resource being referenced.
|
||||
*/
|
||||
public ResourceReference(String resourceUrl) {
|
||||
super();
|
||||
this.resourceUrl = resourceUrl;
|
||||
|
||||
if(isResourceUrl(resourceUrl)) {
|
||||
resourceUrl = resourceUrl.replace('\\', '/').substring(RESOURCE_URL_PREFIX.length());
|
||||
int packageGroupSeparator = resourceUrl.indexOf('/');
|
||||
int groupResourceSeparator = resourceUrl.indexOf('/', packageGroupSeparator + 1);
|
||||
|
||||
packageName = resourceUrl.substring(0, packageGroupSeparator);
|
||||
groupName = resourceUrl.substring(packageGroupSeparator + 1, groupResourceSeparator);
|
||||
resourceName = resourceUrl.substring(groupResourceSeparator + 1);
|
||||
}//if//
|
||||
else {
|
||||
throw new RuntimeException("Invalid resource URL: " + resourceUrl);
|
||||
}//else//
|
||||
}//JefResourceReference()//
|
||||
/**
|
||||
* Determines whether the given potential resource URL is infact a valid resource URL.
|
||||
* @param resourceUrl The potential resource url.
|
||||
* @return Whether the passed potential resource URL checked out as a valid resource URL.
|
||||
*/
|
||||
public static boolean isResourceUrl(String resourceUrl) {
|
||||
boolean result = false;
|
||||
|
||||
if(resourceUrl != null) {
|
||||
boolean hasPrefix = (resourceUrl.length() >= RESOURCE_URL_PREFIX.length()) && (resourceUrl.substring(0, RESOURCE_URL_PREFIX.length()).equalsIgnoreCase(RESOURCE_URL_PREFIX));
|
||||
boolean hasPrefixAlt = (resourceUrl.length() >= RESOURCE_URL_PREFIX_ALT.length()) && (resourceUrl.substring(0, RESOURCE_URL_PREFIX_ALT.length()).equalsIgnoreCase(RESOURCE_URL_PREFIX_ALT));
|
||||
|
||||
if(hasPrefix || hasPrefixAlt) {
|
||||
int slashCount = 0;
|
||||
int lastSlashIndex = -1;
|
||||
|
||||
result = true;
|
||||
resourceUrl = hasPrefix ? resourceUrl.substring(RESOURCE_URL_PREFIX.length()) : resourceUrl.substring(RESOURCE_URL_PREFIX_ALT.length());
|
||||
|
||||
//Iterate over the characters counting slashes and ensuring they are not sequential.//
|
||||
for(int characterIndex = 0; (result) && (characterIndex < resourceUrl.length()); characterIndex++) {
|
||||
if((resourceUrl.charAt(characterIndex) == '\\') || (resourceUrl.charAt(characterIndex) == '/')) {
|
||||
if(lastSlashIndex == characterIndex - 1) {
|
||||
result = false;
|
||||
}//if//
|
||||
else {
|
||||
slashCount++;
|
||||
lastSlashIndex = characterIndex;
|
||||
}//else//
|
||||
}//if//
|
||||
}//for//
|
||||
|
||||
//Make sure there were only two dividing slashes and the last slash is not at the very end since there should only be three parts to a resource URL (package, group, resource).//
|
||||
if(result) {
|
||||
result = lastSlashIndex != resourceUrl.length() && slashCount == 2;
|
||||
}//if//
|
||||
}//if//
|
||||
}//if//
|
||||
|
||||
return result;
|
||||
}//isResourceUrl()//
|
||||
/**
|
||||
* Gets the name of the resource package being referenced.
|
||||
* @return The referenced resource's package name.
|
||||
*/
|
||||
public String getPackageName() {
|
||||
return packageName;
|
||||
}//getPackageName()//
|
||||
/**
|
||||
* Gets the name of the resource group being referenced.
|
||||
* @return The referenced resource's group name.
|
||||
*/
|
||||
public String getGroupName() {
|
||||
return groupName;
|
||||
}//getGroupName()//
|
||||
/**
|
||||
* Gets the name of the resource being referenced.
|
||||
* @return The referenced resource's name.
|
||||
*/
|
||||
public String getResourceName() {
|
||||
return resourceName;
|
||||
}//getResourceName()//
|
||||
/**
|
||||
* Gets the full URL of the resource being referenced.
|
||||
* @return The referenced resource's full URL.
|
||||
*/
|
||||
public String getResourceUrl() {
|
||||
return resourceUrl;
|
||||
}//getResourceUrl()//
|
||||
/* (non-Javadoc)
|
||||
* @see java.io.Externalizable#readExternal(java.io.ObjectInput)
|
||||
*/
|
||||
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
|
||||
in.readByte();
|
||||
packageName = in.readUTF();
|
||||
groupName = in.readUTF();
|
||||
resourceName = in.readUTF();
|
||||
resourceUrl = in.readUTF();
|
||||
}//readExternal()//
|
||||
/* (non-Javadoc)
|
||||
* @see java.io.Externalizable#writeExternal(java.io.ObjectOutput)
|
||||
*/
|
||||
public void writeExternal(ObjectOutput out) throws IOException {
|
||||
out.writeByte(0);
|
||||
out.writeUTF(packageName);
|
||||
out.writeUTF(groupName);
|
||||
out.writeUTF(resourceName);
|
||||
out.writeUTF(resourceUrl);
|
||||
}//writeExternal()//
|
||||
/* (non-Javadoc)
|
||||
* @see java.lang.Object#clone()
|
||||
*/
|
||||
public Object clone() throws CloneNotSupportedException {
|
||||
ResourceReference clone = (ResourceReference) super.clone();
|
||||
|
||||
clone.packageName = packageName;
|
||||
clone.groupName = groupName;
|
||||
clone.resourceName = resourceName;
|
||||
clone.resourceUrl = resourceUrl;
|
||||
|
||||
return clone;
|
||||
}//clone()//
|
||||
/* (non-Javadoc)
|
||||
* @see java.lang.Object#toString()
|
||||
*/
|
||||
public String toString() {
|
||||
return resourceUrl;
|
||||
}//toString()//
|
||||
/* (non-Javadoc)
|
||||
* @see java.lang.Object#equals(java.lang.Object)
|
||||
*/
|
||||
public boolean equals(Object object) {
|
||||
return (object instanceof ResourceReference) && (resourceUrl.equals(((ResourceReference) object).getResourceUrl()));
|
||||
}//equals()//
|
||||
/* (non-Javadoc)
|
||||
* @see java.lang.Object#hashCode()
|
||||
*/
|
||||
public int hashCode() {
|
||||
return resourceName.hashCode();
|
||||
}//hashCode()//
|
||||
}//JefResourceReference//
|
||||
194
Foundation/src/com/foundation/view/resource/ResourceService.java
Normal file
194
Foundation/src/com/foundation/view/resource/ResourceService.java
Normal file
@@ -0,0 +1,194 @@
|
||||
/*
|
||||
* Copyright (c) 2005,2009 Declarative Engineering LLC.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Declarative Engineering LLC
|
||||
* verson 1 which accompanies this distribution, and is available at
|
||||
* http://declarativeengineering.com/legal/DE_Developer_License_v1.txt
|
||||
*/
|
||||
package com.foundation.view.resource;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.zip.GZIPOutputStream;
|
||||
|
||||
import com.common.util.*;
|
||||
import com.common.debug.*;
|
||||
import com.common.io.ByteArrayOutputStream;
|
||||
import com.common.io.ObjectOutputStream;
|
||||
import com.common.io.StreamSupport;
|
||||
|
||||
/*
|
||||
* Provides a single point of access for all resources available in a system.
|
||||
* <p>Note: A resource URL is made up of four parts. The first is the prefix 'res:\\' or 'res://', followed by the package name. The third and fourth parts are separated by forward or back slashes and are the group name and resource name respectively. For example: <b>res://Pkg.Name/Grp.Name/Res.Name</b></p>
|
||||
* <p><b>This is a thread safe class.</b></p>
|
||||
*/
|
||||
public class ResourceService extends AbstractResourceService {
|
||||
/** The set of all packages. */
|
||||
private IList resourcePackages;
|
||||
/** A mapping of ILists of ResourceGroups indexed by the ResourcePackage that defines them. */
|
||||
private IHashMap packageToGroupsMap = new LiteHashMap(10);
|
||||
/** The set of all categories defined by the application. */
|
||||
private IHashSet categories = new LiteHashSet(100);
|
||||
/** The relative path where all resource files will be found. */
|
||||
private File resourcePath;
|
||||
/**
|
||||
* ResourceService constructor.
|
||||
* @param resourcePath The relative path to the system resource containing the resource packages.
|
||||
* @throws IOException
|
||||
* @throws ClassNotFoundException
|
||||
*/
|
||||
public ResourceService(File resourcePath, IResourceLoader resourceLoader) throws IOException, ClassNotFoundException {
|
||||
super(resourceLoader);
|
||||
this.resourcePath = resourcePath;
|
||||
resourcePackages = loadPackages();
|
||||
|
||||
//Setup the package to group mapping and collect the set of all categories.//
|
||||
for(int packageIndex = 0; packageIndex < resourcePackages.getSize(); packageIndex++) {
|
||||
ResourcePackage resourcePackage = (ResourcePackage) resourcePackages.get(packageIndex);
|
||||
IList resourceGroups = loadGroups(resourcePackage);
|
||||
|
||||
packageToGroupsMap.put(resourcePackage, resourceGroups);
|
||||
|
||||
//Collect all the categories defined by this service.//
|
||||
for(int groupIndex = 0; groupIndex < resourceGroups.getSize(); groupIndex++) {
|
||||
ResourceGroup resourceGroup = (ResourceGroup) resourceGroups.get(groupIndex);
|
||||
|
||||
categories.addAll(resourceGroup.getCategories());
|
||||
}//for//
|
||||
}//for//
|
||||
|
||||
resourcePackages.isChangeable(false);
|
||||
categories.isChangeable(false);
|
||||
//TODO: Make the mapping immutable as well.
|
||||
}//ResourceService()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.view.resource.AbstractResourceService#getResourcePathPrefix()
|
||||
*/
|
||||
protected File getResourcePathPrefix() {
|
||||
return resourcePath;
|
||||
}//getResourcePathPrefix()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.view.resource.AbstractResourceService#getResourceGroups(com.foundation.view.resource.ResourcePackage, java.lang.String)
|
||||
*/
|
||||
protected IList getResourceGroups(ResourcePackage resourcePackage, String category) {
|
||||
/* This code gets the categories and not the groups...
|
||||
IList groups = (IList) packageToGroupsMap.get(resourcePackage);
|
||||
LiteList result = new LiteList(groups.getSize());
|
||||
|
||||
//Search the groups for categories matching the passed category.//
|
||||
for(int groupIndex = 0; groupIndex < groups.getSize(); groupIndex++) {
|
||||
ResourceGroup group = (ResourceGroup) groups.get(groupIndex);
|
||||
|
||||
for(int categoryIndex = 0; categoryIndex < group.getCategories().length; categoryIndex++) {
|
||||
if(group.getCategories()[categoryIndex].getName().equals(category)) {
|
||||
result.add(group.getCategories()[categoryIndex]);
|
||||
categoryIndex = group.getCategories().length;
|
||||
}//if//
|
||||
}//for//
|
||||
}//for//
|
||||
|
||||
return result;
|
||||
*/
|
||||
return (IList) packageToGroupsMap.get(resourcePackage);
|
||||
}//getResourceGroups()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.view.resource.AbstractResourceService#getResourcePackages()
|
||||
*/
|
||||
protected IList getResourcePackages() {
|
||||
return resourcePackages;
|
||||
}//getResourcePackages()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.view.resource.AbstractResourceService#getResources(com.foundation.view.resource.ResourceCategory)
|
||||
*/
|
||||
protected IList getResources(ResourceCategory resourceCategory) {
|
||||
IList result = null;
|
||||
|
||||
try {
|
||||
result = loadResources(resourceCategory);
|
||||
}//try//
|
||||
catch(Throwable e) {
|
||||
Debug.log(e);
|
||||
}//catch//
|
||||
|
||||
return result;
|
||||
}//getResources()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.view.resource.AbstractResourceService#getResourcePackageData()
|
||||
*/
|
||||
public byte[] getResourcePackageData() {
|
||||
ByteArrayOutputStream bout = null;
|
||||
GZIPOutputStream zout = null;
|
||||
ObjectOutputStream out = null;
|
||||
byte[] result = null;
|
||||
|
||||
try {
|
||||
bout = new ByteArrayOutputStream(1000, false);
|
||||
zout = new GZIPOutputStream(bout, 1000);
|
||||
out = new ObjectOutputStream(zout, null);
|
||||
out.writeObject(getResourcePackages());
|
||||
out.flush();
|
||||
zout.finish();
|
||||
result = bout.toByteArray();
|
||||
}//try//
|
||||
catch(Throwable e) {
|
||||
Debug.log(e);
|
||||
}//catch//
|
||||
finally {
|
||||
try {
|
||||
out.close();
|
||||
zout.close();
|
||||
bout.close();
|
||||
}//try//
|
||||
catch(Throwable e) {}
|
||||
}//finally//
|
||||
|
||||
return result;
|
||||
}//getResourcePackageData()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.view.resource.AbstractResourceService#getResourceGroupData(com.foundation.view.resource.ResourcePackage)
|
||||
*/
|
||||
public byte[] getResourceGroupData(ResourcePackage resourcePackage) {
|
||||
byte[] result = null;
|
||||
|
||||
try {
|
||||
InputStream in = getRawResourceStream(new File(resourcePath, resourcePackage.getPath()));
|
||||
|
||||
result = StreamSupport.readBytes(in);
|
||||
}//try//
|
||||
catch(Throwable e) {
|
||||
Debug.log(e);
|
||||
}//catch//
|
||||
|
||||
return result;
|
||||
}//getResourceGroupData()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.view.resource.AbstractResourceService#getResourceData(com.foundation.view.resource.ResourceCategory)
|
||||
*/
|
||||
public byte[] getResourceData(ResourceCategory resourceCategory) {
|
||||
byte[] result = null;
|
||||
|
||||
try {
|
||||
InputStream in = getRawResourceStream(new File(resourcePath, resourceCategory.getPath()));
|
||||
|
||||
result = StreamSupport.readBytes(in);
|
||||
}//try//
|
||||
catch(Throwable e) {
|
||||
Debug.log(e);
|
||||
}//catch//
|
||||
|
||||
return result;
|
||||
}//getResourceData()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.view.resource.AbstractResourceService#getCodeMetadata()
|
||||
*/
|
||||
public String[] getCodeMetadata() {
|
||||
return null;
|
||||
}//getCodeMetadata()//
|
||||
/* (non-Javadoc)
|
||||
* @see com.foundation.view.resource.AbstractResourceService#getCodeData(java.lang.String)
|
||||
*/
|
||||
public byte[] getCodeData(String name) {
|
||||
return null;
|
||||
}//getCodeData()//
|
||||
}//ResourceService//
|
||||
Reference in New Issue
Block a user