ExpresserModelSlaveCopy.java :  » Math » migen » uk » ac » lkl » migen » system » expresser » model » Java Open Source

Java Open Source » Math » migen 
migen » uk » ac » lkl » migen » system » expresser » model » ExpresserModelSlaveCopy.java
package uk.ac.lkl.migen.system.expresser.model;

import java.util.ArrayList;
import java.util.List;

import org.openide.util.WeakListeners;

import uk.ac.lkl.common.util.event.UpdateEvent;
import uk.ac.lkl.common.util.event.UpdateListener;
import uk.ac.lkl.common.util.event.UpdateSupport;
import uk.ac.lkl.common.util.expression.Expression;
import uk.ac.lkl.common.util.expression.ModifiableOperation;
import uk.ac.lkl.common.util.value.IntegerValue;
import uk.ac.lkl.migen.system.expresser.model.event.AttributeChangeListener;
import uk.ac.lkl.migen.system.expresser.model.shape.block.BlockShape;
import uk.ac.lkl.migen.system.expresser.model.tiednumber.TiedNumberExpression;

public class ExpresserModelSlaveCopy extends ExpresserModel {

    /**
     * The slaved universe is currently implemented as a copy of a model. The
     * copy differs in how boxed numbers are copied since they need different
     * values in the master and slave models.
     * 
     */

    // a mapping from master tied numbers to copies in the slave model
    // private HashMap<TiedNumberExpression<?>, TiedNumberExpression<?>>
    // tiedNumbersMasterToSlaveMapping =
    // new HashMap<TiedNumberExpression<?>, TiedNumberExpression<?>>();
    // offsets used to minimise the possibility of two offsets being the same
    private ArrayList<Integer> offsetsInUse = new ArrayList<Integer>();

    private ExpresserModel masterModel;
    
    private boolean frozen = false;
    
    // Need a strong reference for the weak listeners
    private ArrayList<UpdateListener<TiedNumberExpression<IntegerValue>>> attributeChangeTiedNumberListeners =
      new ArrayList<UpdateListener<TiedNumberExpression<IntegerValue>>>();
    private ArrayList<UpdateListener<Expression<IntegerValue>>> attributeChangeExpressionListeners =
      new ArrayList<UpdateListener<Expression<IntegerValue>>>();

    // tied numbers in the slave universe differ from the master
    // by one of randomOffsets chosen randomly
    static private int randomOffsets[] = { 2, -3, -5, 5, 4, -2, 3, -4 };
    // following used only if all the above are inappropriate
    static private int emergencyRandomOffsets[] =
      { 6, 12, 8, -8, 10, 7, 11, -6, 9, -9, -12, -10, -7, -11 };
    
    private UpdateSupport<ExpresserModelSlaveCopy> updateSupport =
  new UpdateSupport<ExpresserModelSlaveCopy>();

    // following is used to display all the unlocked tied numbers regardless of whether they are used in the model
    private ArrayList<TiedNumberExpression<?>> unlockedTiedNumbers;

    public ExpresserModelSlaveCopy() {
  super();
    }

    @SuppressWarnings("unchecked")
    // not clear how to avoid this warning -- named capture doesn't work without
    // lots of other changes
    public TiedNumberExpression<?> getSlaveCopy(TiedNumberExpression<?> masterTiedNumber) {
  List<TiedNumberExpression<IntegerValue>> taskVariables = getMasterModel().getUnlockedNumbers();
  int tiedNumberIndex = taskVariables.indexOf(masterTiedNumber);
  if (tiedNumberIndex < 0) {
      return masterTiedNumber;
  } else {
      List<TiedNumberExpression<IntegerValue>> slaveTaskVariables = getUnlockedNumbers();
      if (tiedNumberIndex < slaveTaskVariables.size()) {
    return slaveTaskVariables.get(tiedNumberIndex);
      } else {
    final TiedNumberExpression<IntegerValue> masterTiedInteger = 
        (TiedNumberExpression<IntegerValue>) masterTiedNumber;
    final TiedNumberExpression<IntegerValue> copy =
        masterTiedInteger.createFreshCopy(true);
    // slave copies are really the same tied number (sort of)
    // so they should share the same id (createFreshCopy(true) does this now)
    //      copy.setIdString(masterTiedInteger.getIdString());
    //      addSlaveToMasterListeners(masterTiedInteger, copy);
    return copy;
      }
  }
    }
    
    public TiedNumberExpression<?> getOrCreateSlaveCopy(TiedNumberExpression<?> masterTiedNumber) {
  TiedNumberExpression<?> slaveCopy = getSlaveCopy(masterTiedNumber);
  if (slaveCopy == masterTiedNumber) {
      return masterTiedNumber.createFreshCopy(true);
  } else {
      return slaveCopy;
  }
    }

    /**
     * @param masterTiedInteger
     * @param copy
     * 
     * ExpresserModelProxy overrides this
     */
    protected void addSlaveToMasterListeners(
      final TiedNumberExpression<IntegerValue> masterTiedInteger,
      final TiedNumberExpression<IntegerValue> copy) {
  addDisplayModeListener(masterTiedInteger, copy);
  // copy could be updated from within the expression area (global colour resource panel)
  // not any longer -- can only change things in the master panel now
//  addDisplayModeListener(copy, masterTiedInteger);
  addValueListener(masterTiedInteger, copy);
//      addValueListener(copy, masterTiedInteger);  
  addNameListener(masterTiedInteger, copy);
//  addNameListener(copy, masterTiedInteger);
    }

    protected void addNameListener(final TiedNumberExpression<IntegerValue> changed,
                             final TiedNumberExpression<IntegerValue> copy) {
  UpdateListener<TiedNumberExpression<IntegerValue>> nameFieldUpdateListener =
      new UpdateListener<TiedNumberExpression<IntegerValue>>() {

      public void objectUpdated(UpdateEvent<TiedNumberExpression<IntegerValue>> e) {
    copy.setName(changed.getName());
      }

  };
//  changed.addNameFieldUpdateListener(nameFieldUpdateListener);
  // don't know why the following didn't work
  changed.addNameFieldUpdateListener(weakUpdateListenerTiedNumberExpression(nameFieldUpdateListener, changed));  
    }

    protected void addValueListener(final TiedNumberExpression<IntegerValue> changed,
                              final TiedNumberExpression<IntegerValue> copy) {
  UpdateListener<Expression<IntegerValue>> updateListener = 
      new UpdateListener<Expression<IntegerValue>>() {

      public void objectUpdated(UpdateEvent<Expression<IntegerValue>> e) {
    int intValue = changed.getValue().getInt();
    int offset = computeValueOffsetForSlave(copy.hashCode(), intValue);
    copy.setValue(new IntegerValue(intValue + offset));  
      }

  };
  // need a strong reference to the listener
  attributeChangeExpressionListeners.add(updateListener);
  changed.addUpdateListener(
    weakUpdateListenerExpression(updateListener, changed));
    }

    protected void addDisplayModeListener(final TiedNumberExpression<IntegerValue> changed,
                                    final TiedNumberExpression<IntegerValue> copy) {
  UpdateListener<TiedNumberExpression<IntegerValue>> displayModeUpdateListener =
      new UpdateListener<TiedNumberExpression<IntegerValue>>() {

      public void objectUpdated(UpdateEvent<TiedNumberExpression<IntegerValue>> e) {
    copy.setDisplayMode(changed.getDisplayMode());  
      }

  };
  // need a strong reference to the listener
  attributeChangeTiedNumberListeners.add(displayModeUpdateListener);
  // the following didn't work  -- see Issue 564
  // because there were no strong references to the listener
  changed.addDisplayModeUpdateListener(
    weakUpdateListenerTiedNumberExpression(displayModeUpdateListener, changed));
    }

    public TiedNumberExpression<?> getMasterTiedNumber(TiedNumberExpression<IntegerValue> slavedTiedNumber) {
  List<TiedNumberExpression<IntegerValue>> taskVariables = getUnlockedNumbers();
  int tiedNumberIndex = taskVariables.indexOf(slavedTiedNumber);
  if (tiedNumberIndex < 0) {
      return slavedTiedNumber;
  } else if (tiedNumberIndex < getUnlockedNumbers().size()) {
      return getMasterModel().getUnlockedNumbers().get(tiedNumberIndex);
  } else {
      // is not a variable that differs in the two models
      return null;
  }
    }

    /**
     * @param expression
     * @return a copy of the expression with each tied number replaced by the
     *         slave universe copy
     */
    @SuppressWarnings({ "unchecked", "rawtypes" })
    public Expression<IntegerValue> createSlaveEquivalent(Expression<IntegerValue> expression) {
  if (expression.isTiedNumber()) {
      return (Expression<IntegerValue>)
               getOrCreateSlaveCopy((TiedNumberExpression<IntegerValue>) expression);
  } else if (expression instanceof ModifiableOperation) {
      ModifiableOperation<IntegerValue, IntegerValue> operation =
        (ModifiableOperation<IntegerValue, IntegerValue>) expression;
      int operandCount = operation.getNumOperands();
      if (operandCount == 0) {
    return expression;
      }
      ArrayList<Expression<IntegerValue>> newOperands =
        new ArrayList<Expression<IntegerValue>>(operandCount);
      for (int i = 0; i < operandCount; i++) {
    Expression operand = operation.getOperand(i);
    newOperands.add(i, createSlaveEquivalent(operand));
      }
      return new ModifiableOperation(operation.getOperator(), newOperands);
  } else {
      return expression;
  }
    }
    
    /**
     * @param expression
     * @return a copy of the expression with each tied number replaced by the
     *         master universe copy
     */
    @SuppressWarnings({ "unchecked", "rawtypes" })
    public Expression<IntegerValue> createMasterEquivalent(Expression<IntegerValue> expression) {
  if (expression.isTiedNumber()) {
      return (Expression<IntegerValue>) getMasterTiedNumber((TiedNumberExpression<IntegerValue>) expression);
  } else if (expression instanceof ModifiableOperation) {
      ModifiableOperation<IntegerValue, IntegerValue> operation =
        (ModifiableOperation<IntegerValue, IntegerValue>) expression;
      int operandCount = operation.getNumOperands();
      if (operandCount == 0) {
    return expression;
      }
      ArrayList<Expression<IntegerValue>> newOperands =
        new ArrayList<Expression<IntegerValue>>(operandCount);
      for (int i = 0; i < operandCount; i++) {
    Expression operand = operation.getOperand(i);
    newOperands.add(i, createMasterEquivalent(operand));
      }
      return new ModifiableOperation(operation.getOperator(), newOperands);
  } else {
      return expression;
  }
    }

    public int computeValueOffsetForSlave(int hashCode, int intValue) {
  // by adding the hash code of the tied number and the value multiplied
  // by a small prime
  // we should get different offsets for different values
  // see Issue 384
  int hashCodeRemaining = hashCode;
  int key = hashCode + intValue * 7;
  int offset = randomOffsets[key % randomOffsets.length];
  if (intValue + offset <= 1) {
      // don't want a value less than 2
      offset = Math.abs(offset);
  }
  int offsetThatMayBeInUse = offset;
  // try to avoid having the same offsets for different tied numbers
  while (isOffsetInUse(offset) && hashCodeRemaining > 0) {
      hashCodeRemaining = hashCodeRemaining / randomOffsets.length;
      offset = randomOffsets[hashCodeRemaining % randomOffsets.length];
      if (intValue + offset <= 1) {
    offset = Math.abs(offset);
      }
  }
  if (hashCodeRemaining == 0) {
      // try again with emergency numbers
      hashCodeRemaining = hashCode;
      key = hashCode + intValue * 7;
      offset = emergencyRandomOffsets[key % emergencyRandomOffsets.length];
      if (intValue + offset <= 1) {
    // don't want a value less than 2
    offset = Math.abs(offset);
      }
      // try to avoid having the same offsets for different tied numbers
      while (isOffsetInUse(offset) && hashCodeRemaining > 0) {
    hashCodeRemaining =
      hashCodeRemaining / emergencyRandomOffsets.length;
    offset = emergencyRandomOffsets[hashCodeRemaining % emergencyRandomOffsets.length];
    if (intValue + offset <= 1) {
        offset = Math.abs(offset);
    }
      }
  }
  if (hashCodeRemaining == 0) {
      offset = firstUnusedOffset(2 - intValue);
      if (offset == 0) {
    return offsetThatMayBeInUse;
      }
  }
  usingOffset(offset);
  return offset;
    }

    //    
    // public void putSlaveCopy(TiedNumberExpression<?> masterTiedNumber,
    // TiedNumberExpression<?> slaveTiedNumber) {
    // tiedNumbersMasterToSlaveMapping.put(masterTiedNumber, slaveTiedNumber);
    // }

    @Override
    public void removeAllObjects() {
  super.removeAllObjects();
  // tiedNumbersMasterToSlaveMapping.clear();
  offsetsInUse.clear();
  attributeChangeTiedNumberListeners.clear();
  attributeChangeExpressionListeners.clear();
    }

    public boolean isOffsetInUse(Integer offset) {
  return offsetsInUse.contains(offset);
    }

    public void usingOffset(Integer offset) {
  offsetsInUse.add(offset);
    }

    public int firstUnusedOffset(int minimum) {
  for (int offset : randomOffsets) {
      if (offset >= minimum && !isOffsetInUse(offset)) {
    return offset;
      }
  }
  for (int offset : emergencyRandomOffsets) {
      if (offset >= minimum && !isOffsetInUse(offset)) {
    return offset;
      }
  }
  return 0;
    }

    public boolean atLeastOneTaskVariable() {
  return !getUnlockedNumbers().isEmpty();
    }

    public void beginCopy() {
  // see comment associated with ModelCopier.getSlaveModelBeingConstructed
  ModelCopier.setSlaveModelBeingConstructed(this);
    }

    public void endCopy() {
  ModelCopier.setSlaveModelBeingConstructed(null);
    }

    public ExpresserModel getMasterModel() {
  return masterModel;
    }

    public void setMasterModel(ExpresserModel masterModel) {
  this.masterModel = masterModel;
    }

    @Override
    public boolean isSlaved() {
  return true;
    }
    
    @SuppressWarnings("unchecked")
    public UpdateListener<TiedNumberExpression<IntegerValue>> weakUpdateListenerTiedNumberExpression(
      UpdateListener<TiedNumberExpression<IntegerValue>> listener, Object source) {
  return (UpdateListener<TiedNumberExpression<IntegerValue>>) WeakListeners.create(UpdateListener.class,
                                                                             listener, 
                                                                             source);
    }
    
    @SuppressWarnings("unchecked")
    public UpdateListener<Expression<IntegerValue>> weakUpdateListenerExpression(
      UpdateListener<Expression<IntegerValue>> listener, Object source) {
  return (UpdateListener<Expression<IntegerValue>>) WeakListeners.create(UpdateListener.class,
                                                                   listener, 
                                                                   source);
    }

    public boolean isFrozen() {
        return frozen;
    }

    public void setFrozen(boolean frozen) {
        this.frozen = frozen;
    }
    
    public void addUpdateListener(UpdateListener<ExpresserModelSlaveCopy> listener) {
  updateSupport.addListener(listener);
    }

    public void removeUpdateListener(UpdateListener<ExpresserModelSlaveCopy> listener) {
  updateSupport.removeListener(listener);
    }
    
    public void fireUpdateListeners() {
  updateSupport.fireObjectUpdated(null);
    }

    @Override
    public ArrayList<TiedNumberExpression<?>> getUnlockedTiedNumbers() {
        return unlockedTiedNumbers;
    }

    public void setUnlockedTiedNumbers(ArrayList<TiedNumberExpression<?>> unlockedTiedNumbers) {
        this.unlockedTiedNumbers = unlockedTiedNumbers;
    }
    
    @Override
    public ArrayList<TiedNumberExpression<?>> getAnimationTiedNumbers() {
  return getUnlockedTiedNumbers();
    }
    
    @Override
    @SuppressWarnings("unchecked")
    public AttributeChangeListener<BlockShape> weakListener(AttributeChangeListener<BlockShape> listener, Object source) {
  // non-slaved models don't need to bother making these listeners weak
  // moved from ExpresserModel to facilitate porting to Google Web Toolkit
  return (AttributeChangeListener<BlockShape>) WeakListeners.create(AttributeChangeListener.class,
                                                              listener, 
                                                              source);
    }

}
java2s.com  | Contact Us | Privacy Policy
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.