Package ca.uwaterloo.fydp.xcde

Source Code of ca.uwaterloo.fydp.xcde.XCDERootDirectoryUserStimulusChange

package ca.uwaterloo.fydp.xcde;


import ca.uwaterloo.fydp.ossp.OSSPRundownObject;
import ca.uwaterloo.fydp.ossp.OSSPState;
import ca.uwaterloo.fydp.ossp.OSSPStimulus;
import ca.uwaterloo.fydp.ossp.std.OSSPCompoundStimulus;
import ca.uwaterloo.fydp.ossp.std.OSSPDirectoryStimulus;
import ca.uwaterloo.fydp.ossp.std.OSSPDirectoryStimulusChange;
import ca.uwaterloo.fydp.ossp.std.OSSPDirectoryStimulusCreate;
import ca.uwaterloo.fydp.ossp.std.OSSPDirectoryStimulusDelete;

public class XCDERootDirectoryUserStimulusChange implements OSSPStimulus {

  public static final long serialVersionUID = 0;

  public String currUserName;
 
  public XCDERootDirectoryUserState newState;
 
  /**
   * Constructs a user change stimulus.
   *
   * @param _currUserName the user's current name
   * @param _newState their new state
   */
  public XCDERootDirectoryUserStimulusChange(String _currUserName, XCDERootDirectoryUserState _newState) {
    currUserName = _currUserName;
    newState = _newState;
  }
 
  public String toString() {
    return "CHANGE USER " + currUserName + " TO " + newState;
  }
 
  public OSSPState apply(OSSPState curr) {
    ((XCDERootDirectory)curr).changeUser(currUserName, newState);
    return curr.postApply(this);
  }

  public OSSPStimulus synchronize(OSSPStimulus other, boolean tieBreaker) {
    if (other instanceof OSSPDirectoryStimulus) {
      // User changes do not affect directory changes (though the reverse is
      // different).
      return other;
    } else if (other instanceof XCDERootDirectoryUserStimulusAdd) {
      // Check if the user names are the same.
      XCDERootDirectoryUserStimulusAdd tmp =
        (XCDERootDirectoryUserStimulusAdd)other;
      if (tmp.state.userName.equals(newState.userName)) {
        // An add and change message for the same user name.
        // This only happens when there is a race condition for
        // one client to join and another to change its name. Our
        // solution is that the first one "wins", and the second
        // receives a change message for its own user name, which
        // tells it that there's been a conflict and it should login
        // again with a different name.
       
        // Since changing a user name and adding that user name
        // cannot be simultaneously valid (see the remove code),
        // tmp.state.userName should never equal newState.userName.
        if (tmp.state.userName.equals(newState.userName)) {
          throw new RuntimeException("Something is broken.");
        }
        if (tieBreaker) {
          // We are on the server and were applied here first, so
          // we "win". Thus, the add does nothing.
          return null;
        } else {
          // We are on the client, so we "lose", and other gets
          // applied here, which tells us to change our name. However,
          // we've already applied the change here. Thus, we need to
          // change the user name again according to the received add
          // stimulus.
          return new XCDERootDirectoryUserStimulusChange(newState.userName, tmp.state);
        }
      }
      // Else they are different user names and thus don't affect
      // each other.
      return other;
    } else if (other instanceof XCDERootDirectoryUserStimulusChange) {
      // Check if the starting names are the same.
      XCDERootDirectoryUserStimulusChange tmp =
        (XCDERootDirectoryUserStimulusChange)other;
      if (tmp.currUserName.equals(currUserName)) {
        // Two change messages for the same user name.
        // This happens when there is confusion over who
        // controls a certain user name. (This should never
        // happen with properly behaved clients, since any confusion
        // would be resolved during connection.) We let the first
        // change occur and the second be overruled. The initiator
        // of the second knows to choose a new user name by virtue
        // of receiving a change for itself.
        if (tieBreaker) {
          // We are on the server and were applied here first, so
          // we "win", and other doesn't get applied.
          return null;
        } else {
          // We are on the client, so we "lose", and other gets
          // applied here. However, our own change may have
          // changed the name.
          return new XCDERootDirectoryUserStimulusChange(newState.userName, tmp.newState);
        }
      }
      // Else the source names are different, but what about the target names?
     
      // Check if the user names are the same.
      if (tmp.newState.userName.equals(newState.userName)) {
        // Two change messages for the same target user name.
        // This only happens when there is a race condition for
        // two existing clients to change to the same name. Our
        // solution is that the first one "wins", and the second
        // receives a change message for its own user name, which
        // tells it that there's been a conflict and it should login
        // again with a different name.
       
        if (tieBreaker) {
          // We are on the server and were applied here first, so
          // we "win". However, we need to remove the old user name
          // that the client was trying to change.
          return new XCDERootDirectoryUserStimulusRemove(tmp.currUserName);
        } else {
          // We are on the client, so we "lose", and other gets
          // applied here, which tells us to change our name. However,
          // we've already done our change here. Thus we need to delete
          // the old user name and change the new one.
          return new OSSPCompoundStimulus(
              new XCDERootDirectoryUserStimulusRemove(tmp.currUserName),
              new XCDERootDirectoryUserStimulusChange(newState.userName, tmp.newState));
        }
      }
      // Else they are different target user names and thus don't affect
      // each other.
      return other;
    } else if (other instanceof XCDERootDirectoryUserStimulusRemove) {
      XCDERootDirectoryUserStimulusRemove tmp =
        (XCDERootDirectoryUserStimulusRemove)other;
      // First, it if the user is being renamed, it shouldn't be possible for the
      // remove to be for that name. Check this.
      if ((!currUserName.equals(newState.userName)) && tmp.userName.equals(newState.userName)) {
        throw new RuntimeException("Something is broken.");
      }
      // Now check if same source user name.
      if (tmp.userName.equals(currUserName)) {
        // This happens when there is confusion over which client
        // controls which user name. Here, two clients think they
        // control the same name. Our solution is that the first
        // client to initiate a change in such a conflict situation
        // gets to make it. The other guy's change is squelched and
        // they're sent the change that was used. When they see
        // the change, they should know that there is a conflict
        // of user names and that they should change their name.
       
        if (tieBreaker) {
          return null;
        } else {
          // We may have changed the user name here, so we need a new remove
          // for the new name.
          return new XCDERootDirectoryUserStimulusRemove(newState.userName);
        }
      }
      // Else they are different user names and thus don't affect
      // each other.
      return other;
    } else {
      return other.secondChanceSynchronize(this, tieBreaker);
    }
  }

  private OSSPStimulus _secondChanceSynchronize(OSSPStimulus other, boolean tieBreaker, int i) {
    if (newState.filePath == null) {
      // If our added user is not viewing a file, then non-user stimuli
      // do not affect us.
      return this;
    }
    if (other instanceof OSSPCompoundStimulus) {
      XCDERootDirectoryUserStimulusChange tmp = (XCDERootDirectoryUserStimulusChange)_secondChanceSynchronize( ((OSSPCompoundStimulus)other).getFirst(), tieBreaker, i );
      if (tmp == null) {
        return null;
      }
      return tmp._secondChanceSynchronize( ((OSSPCompoundStimulus)other).getSecond(), tieBreaker, i );
    }
    if ( i < newState.filePath.length ) {
      // Then other must be a directory stimulus.
      if (!newState.filePath[i].equals(((OSSPDirectoryStimulus)other).getAffectedElementName())) {
        // Different next segment, so "other" does not affect us.
        return this;
      }
      // Else same next segment. Check other's type.
      if (other instanceof OSSPDirectoryStimulusCreate) {
        // Adverse state. We couldn't have been viewing something that
        // didn't exist.
        throw new RuntimeException("Something is broken.");
      } else if (other instanceof OSSPDirectoryStimulusDelete) {
        // Someone has deleted the thing we're looking at. Change
        // to not look at anything.
        return new XCDERootDirectoryUserStimulusChange(currUserName, new XCDERootDirectoryUserState(newState.userName, null, null, newState.readyForBuild, newState.readyForTest));
      } else {
        // Else it's a change, so we continue.
        other = ((OSSPDirectoryStimulusChange)other).elementChange;
        return _secondChanceSynchronize(other, tieBreaker, i+1);
      }
    } else {
      // We survived all the path segments. Thus "other" must be left as a
      // stimulus for a file.
      if (other instanceof XCDEDocumentAnnotationStimulus) {
        // Annotation stimuli don't affect us.
        return this;
      } else if (other instanceof XCDEDocChange) {
        // Adjust our selection.
        return new XCDERootDirectoryUserStimulusChange(currUserName, new XCDERootDirectoryUserState(newState.userName, newState.filePath, ((XCDEDocChange)other).updateSelection(newState.cursor), newState.readyForBuild, newState.readyForTest));
      } else {
        // There are no other file stimulus types.
        throw new RuntimeException("Unknown document stimulus type.");
      }
    }
  }
 
  public OSSPStimulus secondChanceSynchronize(OSSPStimulus other,
      boolean tieBreaker) {
    return _secondChanceSynchronize(other, tieBreaker, 0);
  }

  public OSSPRundownObject updateRundownObject(OSSPRundownObject ob, boolean isSelf) {
    // If a user has been renamed, the rundown should now be for the new name. We
    // don't check self, b/c we don't prevent different clients from editing
    // users not logged on by them. A change to the class hierarchy should be made
    // to fix this.
    if (ob == null) {
      return ob;
    }
    int i = ((XCDERootDirectoryRundownObject)ob).userNames.indexOf(currUserName);
    if (i == -1) {
      return ob;
    }
    ((XCDERootDirectoryRundownObject)ob).userNames.set(i, newState.userName);
    return ob;
  }
}
TOP

Related Classes of ca.uwaterloo.fydp.xcde.XCDERootDirectoryUserStimulusChange

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