/** {@inheritDoc} */
  @Override
  public void actionPerformed(ActionEvent e) {
    final XcosDiagram parentGraph = (XcosDiagram) getGraph(e);

    // action disabled when the cell is edited
    final ScilabComponent comp = ((ScilabComponent) parentGraph.getAsComponent());
    if (comp.isEditing()) {
      return;
    }

    parentGraph.info(XcosMessages.GENERATE_SUPERBLOCK);
    parentGraph.getModel().beginUpdate();
    try {

      final SuperBlock superBlock;
      final Collection<Broken> brokenLinks;
      final Set<Object> inSelectionCells;

      /*
       * Allocate superBlock
       */
      final Object[] selection = parentGraph.getSelectionCells();
      final Object[] blocks = XcosDiagram.filterByClass(selection, BasicBlock.class);
      inSelectionCells = new LinkedHashSet<Object>(Arrays.asList(blocks));

      superBlock = allocateSuperBlock(parentGraph, selection);

      /*
       * First perform all modification on the parent diagram to handle
       * well undo/redo operations.
       */
      brokenLinks = updateParent(parentGraph, superBlock, inSelectionCells);

      /*
       * Then move some cells to the child diagram
       */
      final SuperBlockDiagram childGraph =
          moveToChild(parentGraph, superBlock, brokenLinks, inSelectionCells);

      /*
       * Finish the install on the child and sync it.
       */
      childGraph.installListeners();
      childGraph.installSuperBlockListeners();
      superBlock.invalidateRpar();

      Xcos.getInstance().addDiagram(parentGraph.getSavedFile(), childGraph);
    } finally {
      parentGraph.getModel().endUpdate();
      parentGraph.info(XcosMessages.EMPTY_INFO);
    }
  }
  /**
   * Allocate a superBlock
   *
   * @param parentGraph the base graph
   * @param selection the selected blocks
   * @return the allocated super block (without specific listeners)
   */
  private SuperBlock allocateSuperBlock(final XcosDiagram parentGraph, final Object[] selection) {
    final SuperBlock superBlock = (SuperBlock) BlockFactory.createBlock(INTERFUNCTION_NAME);
    superBlock.setStyle(INTERFUNCTION_NAME);

    /*
     * Allocate the diagram
     */
    final SuperBlockDiagram diag = new SuperBlockDiagram(superBlock);
    superBlock.setChild(diag);
    superBlock.setParentDiagram(parentGraph);

    /*
     * Place the super block
     */
    final mxRectangle dims = parentGraph.getBoundingBoxFromGeometry(selection);
    final double minX = dims.getX();
    final double maxX = minX + dims.getWidth();

    final double minY = dims.getY();
    final double maxY = minY + dims.getHeight();

    superBlock.getGeometry().setX((maxX + minX - superBlock.getGeometry().getWidth()) / 2.0);
    superBlock.getGeometry().setY((maxY + minY - superBlock.getGeometry().getHeight()) / 2.0);

    /*
     * get statistics
     */
    int angleCounter = 0;
    int flipCounter = 0;
    int mirrorCounter = 0;
    for (Object object : selection) {
      if (object instanceof BasicBlock) {
        final BasicBlock b = (BasicBlock) object;

        angleCounter += b.getAngle();
        if (b.getFlip()) {
          flipCounter++;
        }
        if (b.getMirror()) {
          mirrorCounter++;
        }
      }
    }

    /*
     * apply statistics
     */
    final int halfSize = selection.length / 2;
    superBlock.setAngle(BlockPositioning.roundAngle(angleCounter / selection.length));
    superBlock.setFlip(flipCounter > halfSize);
    superBlock.setMirror(mirrorCounter > halfSize);

    return superBlock;
  }
  /**
   * Move the cells to the child graph
   *
   * @param parentGraph the parent graph
   * @param superBlock the superBlock
   * @param brokenLinks the broken links set
   * @param inSelectionCells the cells in selection
   * @return the superblock child diagram
   */
  private SuperBlockDiagram moveToChild(
      final XcosDiagram parentGraph,
      final SuperBlock superBlock,
      final Collection<Broken> brokenLinks,
      final Set<Object> inSelectionCells) {
    final SuperBlockDiagram childGraph = superBlock.getChild();
    final mxGraphModel childModel = (mxGraphModel) childGraph.getModel();

    childModel.beginUpdate();
    try {
      final Collection<Object> cellsToCopy = new ArrayList<Object>();

      /*
       * create a collection with all the cells to move
       */
      cellsToCopy.addAll(inSelectionCells);
      for (Broken b : brokenLinks) {
        cellsToCopy.add(b.getChildBlock());
        cellsToCopy.add(b.getChildLink());
      }

      /*
       * Really copy the cells
       */
      final Object[] cells = cellsToCopy.toArray();
      parentGraph.removeCells(cells, false);
      childGraph.addCells(cells);

      childGraph.setChildrenParentDiagram();
      BlockPositioning.updateBlockView(superBlock);
    } finally {
      childModel.endUpdate();
    }

    return childGraph;
  }
  /**
   * Apply compatibility pattern to the decoded object
   *
   * @param dec Codec that controls the decoding process.
   * @param node XML node to decode the object from.
   * @param obj Object decoded.
   * @return The Object transformed
   * @see org.scilab.modules.xcos.io.codec.XcosObjectCodec#afterDecode(com.mxgraph.io.mxCodec,
   *     org.w3c.dom.Node, java.lang.Object)
   */
  @Override
  public Object afterDecode(mxCodec dec, Node node, Object obj) {
    if (!(obj instanceof BasicBlock)) {
      LOG.severe("Unable to decode " + obj);
      return obj;
    }
    final BasicBlock block = (BasicBlock) obj;

    block.setSimulationFunctionType(SimulationFunctionType.DEFAULT);

    String functionType = (((Element) node).getAttribute(SIMULATION_FUNCTION_TYPE));
    if (functionType != null && functionType.compareTo("") != 0) {
      SimulationFunctionType type = BasicBlock.SimulationFunctionType.valueOf(functionType);
      if (type != null) {
        block.setSimulationFunctionType(type);
      }
    }

    // Re associate the diagram container
    if (block instanceof SuperBlock) {
      final SuperBlock superBlock = (SuperBlock) block;
      if (superBlock.getChild() != null) {
        superBlock.getChild().setContainer(superBlock);
      }
    }

    // update style to replace direction by rotation and add the
    // default style if absent
    StyleMap map = new StyleMap(((Element) node).getAttribute(STYLE));
    formatStyle(map, (BasicBlock) obj);
    block.setStyle(map.toString());
    block.updateFieldsFromStyle();

    /*
     * Compat. to remove old specific implementations
     *
     * These implementation was available from Scilab-5.2.0 to Scilab-5.3.3.
     *
     * Set default values stolen from the old implementation in case of
     * default value.
     */
    if (node.getNodeName().equals("ConstBlock")) {
      if (block.getInterfaceFunctionName().equals(BasicBlock.DEFAULT_INTERFACE_FUNCTION)) {
        block.setInterfaceFunctionName("CONST_m");
      }
      if (block.getSimulationFunctionName().equals(BasicBlock.DEFAULT_SIMULATION_FUNCTION)) {
        block.setSimulationFunctionName("cstblk4");
      }
      if (block.getValue() == null) {
        block.setValue("1");
      }
    }
    if (node.getNodeName().equals("GainBlock")) {
      if (block.getInterfaceFunctionName().equals(BasicBlock.DEFAULT_INTERFACE_FUNCTION)) {
        block.setInterfaceFunctionName("GAINBLK_f");
      }
    }
    // PrintBlock has no default values

    return super.afterDecode(dec, node, obj);
  }