text: #!/usr/bin/env python
  # -*- encoding: utf-8 -*-
  
  # Copyright 2011-2014, Nigel Small
  #
  # Licensed under the Apache License, Version 2.0 (the "License");
  # you may not use this file except in compliance with the License.
  # You may obtain a copy of the License at
  #
  #     http://www.apache.org/licenses/LICENSE-2.0
  #
  # Unless required by applicable law or agreed to in writing, software
  # distributed under the License is distributed on an "AS IS" BASIS,
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  # See the License for the specific language governing permissions and
  # limitations under the License.
  
  
  from py2neo.core import Graph, Node, NodePointer, Path, Relationship, Rev
  from py2neo.cypher.lang import cypher_escape
  from py2neo.cypher.util import StartOrMatch
  from py2neo.util import ustr, xstr
  
  
  __all__ = ["CreateStatement"]
  
  
  def _(*args):
      return "".join("_" + ustr(arg) for arg in args)
  
  
  class CreateStatement(object):
      """ Builder for a Cypher CREATE/CREATE UNIQUE statement.
      """
  
      #: The graph against which this statement is to be executed.
      graph = None
  
      #: The parameters to inject into this statement.
      parameters = None
  
      def __init__(self, graph):
          self.graph = graph
          self.supports_node_labels = self.graph.supports_node_labels
          self.entities = []
          self.names = []
          self.start_or_match_clause = StartOrMatch(self.graph)
          self.create_clause = []
          self.create_unique_clause = []
          self.return_clause = []
          self.parameters = {}
  
      def __repr__(self):
          return self.string
  
      def __str__(self):
          return xstr(self.__unicode__())
  
      def __unicode__(self):
          return self.string
  
      def __contains__(self, entity):
          return any(e is entity for e in self.entities)
  
      @property
      def string(self):
          """ The full Cypher statement as a string.
          """
          clauses = [self.start_or_match_clause.string]
          if self.create_clause:
              clauses.append("CREATE " + ",".join(self.create_clause))
          if self.create_unique_clause:
              clauses.append("CREATE UNIQUE " + ",".join(self.create_unique_clause))
          if self.return_clause:
              clauses.append("RETURN " + ",".join(self.return_clause))
          return "\n".join(clauses).strip()
  
      def post(self):
          return self.graph.cypher.post(self.string, self.parameters)
  
      def execute(self):
          """ Execute this statement.
  
          :return: A tuple of created entities.
  
          """
          if not self.entities:
              return ()
          raw = self.post().content
          columns = raw["columns"]
          data = raw["data"]
          dehydrated = dict(zip(columns, data[0]))
          for i, entity in enumerate(self.entities):
              node_names, rel_names = self.names[i]
              if isinstance(entity, Node):
                  metadata = dehydrated[node_names[0]]
                  entity.bind(metadata["self"], metadata)
              elif isinstance(entity, Relationship):
                  metadata = dehydrated[rel_names[0]]
                  entity.bind(metadata["self"], metadata)
              elif isinstance(entity, Path):
                  for j, node in enumerate(entity.nodes):
                      metadata = dehydrated[node_names[j]]
                      node.bind(metadata["self"], metadata)
                  for j, rel in enumerate(entity.rels):
                      metadata = dehydrated[rel_names[j]]
                      rel.bind(metadata["self"], metadata)
          return tuple(self.entities)
  
      def create(self, entity):
          """ Append an entity to the CREATE clause of this statement.
  
          :arg entity: The entity to create.
  
          """
          entity = Graph.cast(entity)
          index = len(self.entities)
          name = _(index)
          if isinstance(entity, Node):
              self.names.append(self._create_node(entity, name))
          elif isinstance(entity, Path):
              self.names.append(self._create_path(entity, name, unique=False))
          else:
              raise TypeError("Cannot create entity of type %s" % type(entity).__name__)
          self.entities.append(entity)
  
      def create_unique(self, entity):
          """ Append an entity to the CREATE UNIQUE clause of this statement.
  
          :arg entity: The entity to add.
  
          """
          entity = Graph.cast(entity)
          index = len(self.entities)
          name = _(index)
          if isinstance(entity, Path):
              if len(entity) == 0:
                  raise ValueError("Cannot create unique path with zero length")
              if not any(node.bound or node in self for node in entity.nodes):
                  raise ValueError("At least one node must be bound to create a unique path")
              self.names.append(self._create_path(entity, name, unique=True))
          else:
              raise TypeError("Cannot create unique entity of type %s" % type(entity).__name__)
          self.entities.append(entity)
  
      def _node_pattern(self, node, name, full):
          template = "{name}"
          kwargs = {"name": name}
          if full:
              if node.labels and self.supports_node_labels:
                  template += "{labels}"
                  kwargs["labels"] = "".join(":" + cypher_escape(label) for label in node.labels)
              if node.properties:
                  template += " {{{name}}}"
                  self.parameters[name] = node.properties
          return "(" + template.format(**kwargs) + ")"
  
      def _create_node(self, node, name):
          if node.bound:
              self.start_or_match_clause.node(name, "{" + name + "}")
              self.parameters[name] = node._id
          else:
              self.create_clause.append(self._node_pattern(node, name, full=True))
          self.return_clause.append(name)
          return [name], []
  
      def _create_path_nodes(self, path, name, unique):
          node_names = []
          for i, node in enumerate(path.nodes):
              if isinstance(node, NodePointer):
                  node_names.append(_(node.address))
                  # Switch out node with object from elsewhere in entity list
                  nodes = list(path.nodes)
                  try:
                      target_node = self.entities[node.address]
                  except IndexError:
                      raise IndexError("Node pointer {%s} out of range" % node.address)
                  if not isinstance(target_node, Node):
                      raise ValueError("Pointer {%s} does not refer to a node" % node.address)
                  nodes[i] = target_node
                  path._Path__nodes = tuple(nodes)
              elif node in self:
                  node_name = _(self.entities.index(node))
                  node_names.append(node_name)
              elif unique and not node.bound:
                  node_name = name + "n" + ustr(i)
                  node_names.append(node_name)
                  self.return_clause.append(node_name)
              else:
                  node_name = name + "n" + ustr(i)
                  node_names.append(node_name)
                  self._create_node(node, node_name)
          return node_names
  
      def _create_path(self, path, name, unique):
          node_names = self._create_path_nodes(path, name, unique)
          rel_names = []
          for i, rel in enumerate(path.rels):
              rel_name = name + "r" + ustr(i)
              rel_names.append(rel_name)
              if rel.bound:
                  self.start_or_match_clause.relationship(rel_name, "{" + rel_name + "}")
                  self.parameters[rel_name] = rel._id
              else:
                  if rel.properties:
                      template = "{start}-[{name}:{type} {{{name}}}]->{end}"
                      self.parameters[rel_name] = rel.properties
                  else:
                      template = "{start}-[{name}:{type}]->{end}"
                  start_index, end_index = i, i + 1
                  if isinstance(rel,  Rev):
                      start_index, end_index = end_index, start_index
                  start_node = path.nodes[start_index]
                  end_node = path.nodes[end_index]
                  start = self._node_pattern(start_node, node_names[start_index],
                                             full=(unique and not start_node.bound and start_node not in self))
                  end = self._node_pattern(end_node, node_names[end_index],
                                           full=(unique and not end_node.bound and end_node not in self))
                  kwargs = {"start": start, "name": rel_name,
                            "type": cypher_escape(rel.type), "end": end}
                  if unique:
                      self.create_unique_clause.append(template.format(**kwargs))
                  else:
                      self.create_clause.append(template.format(**kwargs))
              self.return_clause.append(rel_name)
          return node_names, rel_names
  