@Override
  public void performDragOver(DropTargetEvent event) {
    if (!ArtifactTransfer.getInstance().isSupportedType(event.currentDataType)) {
      event.detail = DND.DROP_NONE;
      return;
    }
    final ArtifactData artData = ArtifactTransfer.getInstance().nativeToJava(event.currentDataType);
    if (artData == null) {
      event.detail = DND.DROP_NONE;
      return;
    }
    for (Artifact art : artData.getArtifacts()) {
      if (art.isOfType(CoreArtifactTypes.UniversalGroup)) {
        event.detail = DND.DROP_NONE;
        return;
      }
    }
    Tree tree = treeViewer.getTree();
    TreeItem dragOverTreeItem = tree.getItem(treeViewer.getTree().toControl(event.x, event.y));

    event.feedback = DND.FEEDBACK_EXPAND;
    event.detail = DND.DROP_NONE;

    // Set as COPY if drag item over group (copy versus move will be determined on drop
    if (dragOverTreeItem != null
        && ((GroupExplorerItem) dragOverTreeItem.getData()).isUniversalGroup()) {
      event.detail = DND.DROP_COPY;
      tree.setInsertMark(null, false);
    }
    // Handle re-ordering within same group
    else if (dragOverTreeItem != null
        && !((GroupExplorerItem) dragOverTreeItem.getData()).isUniversalGroup()) {
      GroupExplorerItem dragOverGroupItem = (GroupExplorerItem) dragOverTreeItem.getData();
      IStructuredSelection selectedItem = (IStructuredSelection) treeViewer.getSelection();
      Object obj = selectedItem.getFirstElement();
      if (obj instanceof GroupExplorerItem) {
        GroupExplorerItem droppingGroupItem = (GroupExplorerItem) obj;

        // the group to move must belong to the same group as the member to insert before/after
        if (dragOverGroupItem.getParentItem().equals(droppingGroupItem.getParentItem())) {
          if (isFeedbackAfter) {
            event.feedback = DND.FEEDBACK_INSERT_AFTER;
          } else {
            event.feedback = DND.FEEDBACK_INSERT_BEFORE;
          }
          event.detail = DND.DROP_MOVE;
        }
      } else {
        if (isFeedbackAfter) {
          event.feedback = DND.FEEDBACK_INSERT_AFTER;
        } else {
          event.feedback = DND.FEEDBACK_INSERT_BEFORE;
        }
        event.detail = DND.DROP_COPY;
      }
    } else {
      tree.setInsertMark(null, false);
    }
  }
  @Override
  protected void javaToNative(Object data, TransferData transferData) {
    if (!(data instanceof ArtifactData)) {
      return;
    }

    ArtifactData artData = (ArtifactData) data;
    /**
     * The resource serialization format is: (int) number of artifacts Then, the following for each
     * resource: (int) artID (int) tagID Then the following (int) urlLength (int) sourceLength
     * (chars) url (chars) source
     */
    try {
      ByteArrayOutputStream out = new ByteArrayOutputStream();
      DataOutputStream dataOut = new DataOutputStream(out);

      // write the number of resources
      dataOut.writeInt(artData.getArtifacts().length);

      for (Artifact artifact : artData.getArtifacts()) {
        writeArtifact(dataOut, artifact);
      }
      dataOut.writeInt(artData.getUrl().length());
      dataOut.writeInt(artData.getSource().length());
      dataOut.writeChars(artData.getUrl());
      dataOut.writeChars(artData.getSource());

      // cleanup
      dataOut.close();
      out.close();
      byte[] bytes = out.toByteArray();
      super.javaToNative(bytes, transferData);
    } catch (Exception e) {
      // it's best to send nothing if there were problems
    }
  }