//
// ERD2WEditSortedManyToMany.java
// Project ERDirectToWeb
//
// Created by patrice on Thu Aug 29 2002
//
package er.directtoweb.pages;
import java.util.Enumeration;
import org.apache.log4j.Logger;
import com.webobjects.appserver.WOComponent;
import com.webobjects.appserver.WOContext;
import com.webobjects.appserver.WODisplayGroup;
import com.webobjects.appserver.WOResponse;
import com.webobjects.directtoweb.D2W;
import com.webobjects.directtoweb.EditPageInterface;
import com.webobjects.directtoweb.EditRelationshipPageInterface;
import com.webobjects.directtoweb.NextPageDelegate;
import com.webobjects.directtoweb.Services;
import com.webobjects.eoaccess.EOEntity;
import com.webobjects.eoaccess.EOModelGroup;
import com.webobjects.eoaccess.EORelationship;
import com.webobjects.eoaccess.EOUtilities;
import com.webobjects.eocontrol.EODataSource;
import com.webobjects.eocontrol.EODetailDataSource;
import com.webobjects.eocontrol.EOEditingContext;
import com.webobjects.eocontrol.EOEnterpriseObject;
import com.webobjects.eocontrol.EOSortOrdering;
import com.webobjects.foundation.NSArray;
import er.directtoweb.assignments.ERDSortedManyToManyAssignment;
import er.extensions.appserver.ERXSession;
import er.extensions.eof.ERXConstant;
import er.extensions.eof.ERXEC;
import er.extensions.eof.ERXEOControlUtilities;
import er.extensions.eof.ERXGuardedObjectInterface;
import er.extensions.foundation.ERXValueUtilities;
/**
* @d2wKey displayKey
* @d2wKey showIndex
* @d2wKey indexKey
* @d2wKey browserSize
* @d2wKey maxBrowserSize
*/
public class ERD2WEditSortedManyToManyPage extends ERD2WPage implements EditRelationshipPageInterface {
/**
* Do I need to update serialVersionUID?
* See section 5.6 <cite>Type Changes Affecting Serialization</cite> on page 51 of the
* <a href="http://java.sun.com/j2se/1.4/pdf/serial-spec.pdf">Java Object Serialization Spec</a>
*/
private static final long serialVersionUID = 1L;
// this pages allows the editing of a sorted many-to-many relationship
// d2wContext keys:
// indexKey: the key on the join entity that contains sort order info
public static final Logger log = Logger.getLogger(ERD2WEditSortedManyToManyPage.class);
public ERD2WEditSortedManyToManyPage(WOContext c) {
super(c);
}
public final static int QUERY = 0;
public final static int LIST = 1;
public final static int NEW = 2;
protected int _state = QUERY;
public EODataSource selectDataSource = null;
protected EOEnterpriseObject _eoToAddToRelationship;
protected EOEnterpriseObject _newEOInRelationship;
private String _relationshipKey;
public String relationshipKey(){ return _relationshipKey; }
public WODisplayGroup relationshipDisplayGroup = new WODisplayGroup();
public EOEnterpriseObject browserItem;
public NSArray browserSelections;
public String sortedObjects;
public void setMasterObjectAndRelationshipKey(EOEnterpriseObject eo, String relationshipKey) {
EOEditingContext ec = ERXEC.newEditingContext(eo.editingContext(), false); // a non-validating context
EOEnterpriseObject newObject=EOUtilities.localInstanceOfObject(ec,eo);
setObject(newObject);
_relationshipKey = relationshipKey;
if (!object().isToManyKey(relationshipKey))
throw new RuntimeException(relationshipKey+" is not a to-many relationship");
EODetailDataSource relationshipDataSource = new EODetailDataSource(object().classDescription(), relationshipKey());
relationshipDataSource.qualifyWithRelationshipKey(relationshipKey(), newObject);
setDataSource(relationshipDataSource);
relationshipDisplayGroup.setDataSource(relationshipDataSource);
if(isSortedRelationship()){
EOSortOrdering indexOrdering = EOSortOrdering.sortOrderingWithKey(indexKey(),
EOSortOrdering.CompareAscending);
relationshipDisplayGroup.setSortOrderings(new NSArray(indexOrdering));
}
relationshipDisplayGroup.fetch();
setPropertyKey(displayKey());
}
public String displayKey() {
String displayKeyFromD2W = (String)d2wContext().valueForKey("displayKey");
if(displayKeyFromD2W!=null && displayKeyFromD2W.length()!=0){
return displayKeyFromD2W;
}else{
return destinationRelationship().name()+".userPresentableDescription";
}
}
public boolean displayQuery() {
return _state == QUERY;
}
public boolean displayList() {
return _state == LIST;
}
public boolean displayNew() {
return _state == NEW;
}
public boolean isSortedRelationship(){
boolean isSorted = false;
if(entity().userInfo().valueForKey("isSortedJoinEntity") != null &&
((String)entity().userInfo().valueForKey("isSortedJoinEntity")).equals("true")){
isSorted = true;
}
if(log.isDebugEnabled()){
log.debug("Sorted relationship entity: " + entity().name() + " is sorted? " + isSorted);
}
return isSorted;
}
public boolean showIndex(){
return ERXValueUtilities.booleanValueWithDefault(d2wContext().valueForKey("showIndex"), false);
}
public String browserStringForItem() {
String result = browserItem!=null &&
browserItem.valueForKeyPath(displayKey())!=null ?
browserItem.valueForKeyPath(displayKey()).toString() : "missing data";
if(indexKey()!=null && showIndex()){
Integer index = (Integer)browserItem.valueForKey(indexKey());
if(index!=null){
result = (index.intValue()+1) + ". "+ result ;
}
}
return result;
}
public String displayNameForRelationshipKey() {
return Services.capitalize(relationshipKey());
}
public EOEnterpriseObject objectToAddToRelationship() {
return _eoToAddToRelationship;
}
public void setObjectToAddToRelationship(EOEnterpriseObject newValue) {
_eoToAddToRelationship = newValue;
}
// Switch to query view
public WOComponent queryAction() {
_state = LIST;
return context().page();
}
public WOComponent selectAction() {
EOEnterpriseObject _localEoToAddToRelationship = _eoToAddToRelationship != null ?
EOUtilities.localInstanceOfObject(editingContext(),_eoToAddToRelationship) : null;
if (_localEoToAddToRelationship != null) {
// we create a join
EOEnterpriseObject joinEO=ERXEOControlUtilities.createAndInsertObject(editingContext(), entity().name());
NSArray sortedObjects=relationshipDisplayGroup.displayedObjects();
if(isSortedRelationship()){
Number lastIndex = null;
if (sortedObjects!=null && sortedObjects.count()>0) {
EOEnterpriseObject lastObject=(EOEnterpriseObject)relationshipDisplayGroup.displayedObjects().lastObject();
lastIndex=(Number)lastObject.valueForKey(indexKey());
}
int newIndex = lastIndex!=null ? lastIndex.intValue()+1 : 0;
joinEO.takeValueForKey(ERXConstant.integerForInt(newIndex),indexKey());
}
joinEO.addObjectToBothSidesOfRelationshipWithKey(_localEoToAddToRelationship,
destinationRelationship().name());
object().addObjectToBothSidesOfRelationshipWithKey(joinEO, relationshipKey());
dataSource().insertObject(joinEO);
relationshipDisplayGroup.fetch();
} else {
// no object was selected
_state = QUERY;
}
WOComponent result = null;
if(selectActionDelegate!=null){
result = selectActionDelegate.nextPage(context().page());
}
return result;
}
private NextPageDelegate selectActionDelegate;
public NextPageDelegate selectActionDelegate(){ return selectActionDelegate; }
public void setSelectActionDelegate(NextPageDelegate newSelectActionDelegate){ selectActionDelegate = newSelectActionDelegate; }
public WOComponent removeFromToManyRelationshipAction() {
if(((ERXSession)session()).javaScriptEnabled())
updateEOsOrdering();
if (browserSelections != null) {
for (Enumeration e = browserSelections.objectEnumerator(); e.hasMoreElements();) {
EOEnterpriseObject object=(EOEnterpriseObject)e.nextElement();
EOEnterpriseObject _localEoToRremoveFromRelationship =
(EOEnterpriseObject)object.valueForKey(destinationRelationship().name());
object.removeObjectFromBothSidesOfRelationshipWithKey(_localEoToRremoveFromRelationship,
destinationRelationship().name());
object().removeObjectFromBothSidesOfRelationshipWithKey(object,
relationshipKey());
if(((object instanceof ERXGuardedObjectInterface) && ((ERXGuardedObjectInterface)object).canDelete())
|| !(object instanceof ERXGuardedObjectInterface)) {
editingContext().deleteObject(object);
}
}
relationshipDisplayGroup.fetch(); // updateDisplayedObjects is not doing the trick
if (isSortedRelationship()) {
//Now need to reindex the joins if the relationship is sorted
int i = 0;
for(Enumeration e = relationshipDisplayGroup.displayedObjects().objectEnumerator();
e.hasMoreElements();){
EOEnterpriseObject object = (EOEnterpriseObject)e.nextElement();
object.takeValueForKey(ERXConstant.integerForInt(i), indexKey());
i++;
}
}
}
return null;
}
public WOComponent removeAllFromToManyRelationship(){
if(((ERXSession)session()).javaScriptEnabled())
updateEOsOrdering();
for (Enumeration e = relationshipDisplayGroup.displayedObjects().immutableClone().objectEnumerator(); e.hasMoreElements();) {
EOEnterpriseObject joinObject=(EOEnterpriseObject)e.nextElement();
EOEnterpriseObject _localEoToRremoveFromRelationship =
(EOEnterpriseObject)joinObject.valueForKey(destinationRelationship().name());
joinObject.removeObjectFromBothSidesOfRelationshipWithKey(_localEoToRremoveFromRelationship,
destinationRelationship().name());
object().removeObjectFromBothSidesOfRelationshipWithKey(joinObject,
relationshipKey());
if(((joinObject instanceof ERXGuardedObjectInterface) && ((ERXGuardedObjectInterface)joinObject).canDelete())
|| !(joinObject instanceof ERXGuardedObjectInterface)) {
editingContext().deleteObject(joinObject);
}
}
relationshipDisplayGroup.fetch(); // updateDisplayedObjects is not doing the trick
return null;
}
public WOComponent displayQueryAction() {
_state = QUERY;
return null;
}
public Integer itemHashCode(){
return Integer.valueOf(browserItem.hashCode());
}
public void updateEOsOrdering(){
if(isSortedRelationship()){
//If the session uses javascript then we need to update the EOs according
//to what has been changed by the javascript in the WOBrowser
NSArray hiddenFieldValues = NSArray.componentsSeparatedByString(sortedObjects, ",");
if(log.isDebugEnabled()) log.debug("hiddenFieldValues = "+hiddenFieldValues);
if(hiddenFieldValues != null){
int i = 0;
for(Enumeration e = hiddenFieldValues.objectEnumerator(); e.hasMoreElements();){
String objectForHashCode = (String)e.nextElement();
if(log.isDebugEnabled()) log.debug("objectForHashCode = "+objectForHashCode);
EOEnterpriseObject eo = objectForHashCode(objectForHashCode);
if(eo!=null){
eo.takeValueForKey(ERXConstant.integerForInt(i), indexKey());
}else{
log.warn("objectForHashCode: "+objectForHashCode+" doesn't have a corresponding object");
}
i++;
}
}
}
}
public WOComponent returnAction() {
if(((ERXSession)session()).javaScriptEnabled())
updateEOsOrdering();
editingContext().saveChanges();
editingContext().revert();
WOComponent result = nextPageFromDelegate();
if(result == null) {
result = super.nextPage();
}
if (result != null) {
return result;
}
result = (WOComponent) D2W.factory().editPageForEntityNamed(object().entityName(), session());
((EditPageInterface) result).setObject(object());
editingContext().dispose();
return result;
}
public WOComponent newObjectAction() {
WOComponent result;
if (isEntityReadOnly()) {
throw new IllegalStateException("You cannot create new instances of " + entity().name() + "; it is read-only.");
} else {
_state = NEW;
EOEnterpriseObject eo = ERXEOControlUtilities.createAndInsertObject( editingContext(), destinationEntity().name());
EditPageInterface epi = D2W.factory().editPageForEntityNamed(destinationEntity().name(),
session());
epi.setObject(eo);
CreateNewEODelegate delegate = new CreateNewEODelegate();
delegate.editRelationshipPage = this;
delegate.createdEO = eo;
epi.setNextPageDelegate(delegate);
result = (WOComponent)epi;
}
return result;
}
public static class CreateNewEODelegate implements NextPageDelegate {
public WOComponent editRelationshipPage;
public EOEnterpriseObject createdEO;
public WOComponent nextPage(WOComponent sender){
((ERD2WEditSortedManyToManyPage)editRelationshipPage)._eoToAddToRelationship = createdEO;
((ERD2WEditSortedManyToManyPage)editRelationshipPage).selectAction();
((ERD2WEditSortedManyToManyPage)editRelationshipPage)._state = ERD2WEditSortedManyToManyPage.QUERY;
return editRelationshipPage;
}
}
public EOEnterpriseObject newObjectInRelationship() {
return _newEOInRelationship;
}
public WOComponent saveAction() {
relationshipDisplayGroup.fetch();
_state = QUERY;
return null;
}
private EORelationship _destinationRelationship;
public EORelationship destinationRelationship() {
if (_destinationRelationship==null) {
NSArray joinRelationships = ERDSortedManyToManyAssignment.joinRelationshipsForJoinEntity(entity());
String originEntityName=object().entityName();
//General case
for (Enumeration e=joinRelationships.objectEnumerator(); e.hasMoreElements();) {
EORelationship r=(EORelationship)e.nextElement();
if (!originEntityName.equals(r.destinationEntity().name())) {
_destinationRelationship=r;
break;
}
}
// In the case we have a self join relationship we have to be more clever
if(_destinationRelationship==null){
EOEntity originEntity = EOModelGroup.defaultGroup().entityNamed(originEntityName);
EORelationship originRelationship = originEntity.relationshipNamed(relationshipKey());
EORelationship inverseOriginRelationship = originRelationship.inverseRelationship();
for (Enumeration e=joinRelationships.objectEnumerator(); e.hasMoreElements();) {
EORelationship r=(EORelationship)e.nextElement();
if (r!=inverseOriginRelationship) {
_destinationRelationship=r;
break;
}
}
}
}
return _destinationRelationship;
}
public EOEntity destinationEntity() {
return destinationRelationship().destinationEntity();
}
public String indexKey() {
return (String)d2wContext().valueForKey("indexKey");
}
public WOComponent moveObjectUp()
{
if(log.isDebugEnabled()){
log.debug("browserSelection ="+browserSelection());
}
if(browserSelection()!=null){
NSArray sortedObjects=relationshipDisplayGroup.displayedObjects();
int selectedIndex = ((Integer)browserSelection().valueForKey(indexKey())).intValue();
if(log.isDebugEnabled()){
log.debug("sortedObjects ="+sortedObjects);
log.debug("selectedIndex = "+selectedIndex);
log.debug("indexKey = "+indexKey());
}
if(selectedIndex!=0){
objectAtIndex(selectedIndex-1).takeValueForKey(Integer.valueOf(selectedIndex),
indexKey());
browserSelection().takeValueForKey(Integer.valueOf(selectedIndex-1),
indexKey());
}
}
relationshipDisplayGroup.updateDisplayedObjects();
return null;
}
public WOComponent moveObjectDown()
{
if(browserSelection()!=null){
NSArray sortedObjects=relationshipDisplayGroup.displayedObjects();
// make the compiler happy
sortedObjects.count();
int selectedIndex = ((Integer)browserSelection().valueForKey(indexKey())).intValue();
EOEnterpriseObject lastObject =
(EOEnterpriseObject)relationshipDisplayGroup.displayedObjects().lastObject();
int lastIndex =
((Integer)lastObject.valueForKey(indexKey())).intValue();
if(selectedIndex!=lastIndex){
objectAtIndex(selectedIndex+1).takeValueForKey(Integer.valueOf(selectedIndex),
indexKey());
browserSelection().takeValueForKey(Integer.valueOf(selectedIndex+1),
indexKey());
}
}
relationshipDisplayGroup.updateDisplayedObjects();
return null;
}
public EOEnterpriseObject objectAtIndex(int index){
EOEnterpriseObject result = null;
if(log.isDebugEnabled()){
log.debug("looking for index "+index);
}
for(Enumeration e = relationshipDisplayGroup.displayedObjects().objectEnumerator();
e.hasMoreElements();){
EOEnterpriseObject indexObject = (EOEnterpriseObject)e.nextElement();
if(log.isDebugEnabled()){
log.debug("object is at index"+indexObject.valueForKey(indexKey()));
}
if( ((Integer)indexObject.valueForKey(indexKey())).intValue() == index){
result = indexObject;
break;
}
}
return result;
}
public EOEnterpriseObject objectForHashCode(String hashCode){
EOEnterpriseObject result = null;
if(log.isDebugEnabled()){
log.debug("looking for hashCode "+hashCode+".");
}
for(Enumeration e = relationshipDisplayGroup.displayedObjects().objectEnumerator();
e.hasMoreElements();){
EOEnterpriseObject indexObject = (EOEnterpriseObject)e.nextElement();
if(log.isDebugEnabled()){
log.debug("object's hashCode is "+indexObject.hashCode());
}
if( (Integer.toString(indexObject.hashCode())).equals(hashCode)){
result = indexObject;
break;
}
}
return result;
}
public EOEnterpriseObject browserSelection(){
EOEnterpriseObject result = null;
if (browserSelections != null && browserSelections.count()!=0) {
if(browserSelections.count()>1){
throw new RuntimeException("Please choose only one element");
}else
result = (EOEnterpriseObject)browserSelections.objectAtIndex(0);
}
return result;
}
@Override
public void appendToResponse(WOResponse r, WOContext c){
if(((ERXSession)session()).javaScriptEnabled()){
StringBuilder result = new StringBuilder();
for(Enumeration e = relationshipDisplayGroup.displayedObjects().objectEnumerator();
e.hasMoreElements();){
result.append(e.nextElement().hashCode()+",");
}
sortedObjects = result.toString();
}
super.appendToResponse(r,c);
}
public int browserSize() {
int browserSize = 10; // reasonable default value
int maxBrowserSize = 20;
String contextSize = (String)d2wContext().valueForKey("browserSize");
if(contextSize != null) {
try {
browserSize = Integer.parseInt(contextSize);
} catch(NumberFormatException nfe) {
log.error("browserSize not a number: " + browserSize);
}
}
String maxContextSize = (String)d2wContext().valueForKey("maxBrowserSize");
if(maxContextSize != null) {
try {
maxBrowserSize = Integer.parseInt(maxContextSize);
} catch(NumberFormatException nfe) {
log.error("maxBrowserSize not a number: " + maxBrowserSize);
}
}
int count = relationshipDisplayGroup.displayedObjects().count();
browserSize = (count > browserSize && count < maxBrowserSize) ? count : browserSize;
return browserSize;
}
}