/// <summary>
        /// Creates a new <see cref="AdGroupCriterion"/> configured for an
        /// <code>ADD</code> operation.
        /// </summary>
        /// <param name="node">The node whose criterion should be added.</param>
        /// <param name="adGroupId">The ad group ID of the criterion.</param>
        /// <param name="idGenerator">The temporary ID generator for new nodes.</param>
        /// <returns>An <see cref="AdGroupCriterion"/> object for <code>ADD</code>
        /// operation.</returns>
        internal static AdGroupCriterion CreateCriterionForAdd(ProductPartitionNode node,
        long adGroupId, TemporaryIdGenerator idGenerator)
        {
            PreconditionUtilities.CheckNotNull(node, ShoppingMessages.NodeCannotBeNull);

              AdGroupCriterion adGroupCriterion;

              if (node.IsExcludedUnit) {
            adGroupCriterion = new NegativeAdGroupCriterion();
              } else {
            adGroupCriterion = new BiddableAdGroupCriterion() {
              biddingStrategyConfiguration = node.GetBiddingConfig()
            };
              }
              adGroupCriterion.adGroupId = adGroupId;
              adGroupCriterion.criterion = node.GetCriterion();

              adGroupCriterion.criterion.id = node.ProductPartitionId;
              if (node.Parent != null) {
            (adGroupCriterion.criterion as ProductPartition).parentCriterionId =
               node.Parent.ProductPartitionId;
              }

              return adGroupCriterion;
        }
 public void TestDoesNotGeneratePositiveId() {
   int startId = -1;
   TemporaryIdGenerator generator = new TemporaryIdGenerator(startId);
   Assert.Throws<ApplicationException>(delegate() {
     long next = generator.Next;
   });
 }
    public void TestNextWithStartId() {
      int startId = -20;
      TemporaryIdGenerator generator = new TemporaryIdGenerator(startId);

      Assert.That(generator.Next == startId);
      Assert.That(generator.Next == startId + 1);
    }
    public void TestThrowsExceptionWithPositiveStartId() {
      int startId = 20;
      Assert.Throws<ArgumentException>(delegate() {
        TemporaryIdGenerator generator = new TemporaryIdGenerator(startId);
      });

      startId = 0;
      Assert.Throws<ArgumentException>(delegate() {
        TemporaryIdGenerator generator = new TemporaryIdGenerator(startId);
      });
    }
        /// <summary>
        /// Creates ADD operations for the node and ALL of its children.
        /// </summary>
        /// <param name="node">The node.</param>
        /// <param name="idGenerator">The temporary ID generator for new nodes.</param>
        /// <returns>A list of operation pair for the specified operation and nodes.
        /// </returns>
        private List<OperationPair> CreateAddOperations(ProductPartitionNode node,
        TemporaryIdGenerator idGenerator)
        {
            AdGroupCriterionOperation addOp = new AdGroupCriterionOperation();
              addOp.@operator = Operator.ADD;

              // Overwrite the ID set by the user when doing ADD operations. This
              // minimizes the chances of a malformed tree.
              node.ProductPartitionId = idGenerator.Next;

              addOp.operand = ProductPartitionNodeAdapter.CreateCriterionForAdd(node, adGroupId,
              idGenerator);

              List<OperationPair> operationsList = new List<OperationPair>();
              operationsList.Add(new OperationPair(node, addOp));

              // Recursively add all of this node's children to the operations list.
              foreach (ProductPartitionNode child in node.Children) {
            operationsList.AddRange(CreateAddOperations(child, idGenerator));
              }
              return operationsList;
        }
        /// <summary>
        /// Adds to the operations list all operations required to mutate
        /// <paramref name="originalNode"/> to the state* of
        /// <paramref name="newNode"/>.
        /// </summary>
        /// <param name="originalNode">The original node.</param>
        /// <param name="newNode">The new node.</param>
        /// <param name="ops">The operations list to add to.</param>
        /// <param name="idGenerator">The temporary ID generator for ADD operations.</param>
        /// <returns>The set of child product dimensions that require further
        /// processing.</returns>
        private void AddMutateOperations(ProductPartitionNode originalNode,
        ProductPartitionNode newNode, List<OperationPair> ops, TemporaryIdGenerator idGenerator)
        {
            NodeDifference nodeDifference = Diff(originalNode, newNode, dimensionComparator);
              bool isProcessChildren;

              switch (nodeDifference) {
            case NodeDifference.NEW_NODE:
              ops.AddRange(CreateAddOperations(newNode, idGenerator));
              // No need to further process children. The ADD operations above will include operations
              // for all children of newNode.
              isProcessChildren = false;
              break;

            case NodeDifference.REMOVED_NODE:
              ops.Add(CreateRemoveOperation(originalNode));
              // No need to further process children. The REMOVE operation above will perform a
              // cascading delete of all children of newNode.
              isProcessChildren = false;
              break;

            case NodeDifference.PARTITION_TYPE_CHANGE:
            case NodeDifference.EXCLUDED_UNIT_CHANGE:
              ops.Add(CreateRemoveOperation(originalNode));
              ops.AddRange(CreateAddOperations(newNode, idGenerator));
              // No need to further process children. The ADD operations above will include operations
              // for all children of newNode.
              isProcessChildren = false;
              break;

            case NodeDifference.BID_CHANGE:
              // Ensure that the new node has the proper ID (this may have been lost if the node
              // was removed and then re-added).
              newNode.ProductPartitionId = originalNode.ProductPartitionId;
              ops.Add(CreateSetBidOperation(newNode));
              // Process the children of newNode. The SET operation above will only handle changes
              // made to newNode, not its children.
              isProcessChildren = true;
              break;

            case NodeDifference.NONE:
              // Ensure that the new node has the proper ID (this may have been lost if the node
              // was removed and then re-added).
              newNode.ProductPartitionId = originalNode.ProductPartitionId;
              // This node does not have changes, but its children may.
              isProcessChildren = true;
              break;

            default:
              throw new InvalidOperationException("Unrecognized difference: " + nodeDifference);
              }

              if (isProcessChildren) {
            // Try to match the children in new and original trees to identify the
            // matching dimensions.

            foreach (ProductPartitionNode newChild in newNode.Children) {
              if (originalNode.HasChild(newChild.Dimension)) {
            // this is probably an edit.
            AddMutateOperations(originalNode.GetChild(newChild.Dimension), newChild, ops,
                idGenerator);
              } else {
            // this is a new node.
            AddMutateOperations(null, newChild, ops, idGenerator);
              }
            }

            foreach (ProductPartitionNode originalChild in originalNode.Children) {
              if (newNode.HasChild(originalChild.Dimension)) {
            // this is probably an edit. We dealt with it before
            continue;
              } else {
            // this is a removed node.
            AddMutateOperations(originalChild, null, ops, idGenerator);
              }
            }
              }
        }
 /// <summary>
 /// Creates and returns the pairs of node/operation that will apply the
 /// changes made to this tree.
 /// </summary>
 /// <returns>The list of operation pairs.</returns>
 internal List<OperationPair> CreateMutateOperationPairs()
 {
     TemporaryIdGenerator idGenerator = new TemporaryIdGenerator();
       List<OperationPair> ops = new List<OperationPair>();
       AddMutateOperations(this.originalRoot, this.root, ops, idGenerator);
       return ops;
 }
 public void TestNextWithDefaultValues() {
   TemporaryIdGenerator generator = new TemporaryIdGenerator();
   Assert.That(generator.Next == Int32.MinValue);
   Assert.That(generator.Next == Int32.MinValue + 1);
 }