package open.dolphin.client;
import java.awt.Toolkit;
import java.awt.datatransfer.*;
import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.StringTokenizer;
import javax.swing.ActionMap;
import javax.swing.JComponent;
import javax.swing.JTextPane;
import javax.swing.TransferHandler;
import javax.swing.text.BadLocationException;
import javax.swing.text.Document;
import javax.swing.text.JTextComponent;
import javax.swing.text.Position;
import open.dolphin.infomodel.AttachmentModel;
import open.dolphin.infomodel.IInfoModel;
import open.dolphin.infomodel.ModuleInfoBean;
import open.dolphin.infomodel.SchemaModel;
import open.dolphin.stampbox.StampTreeNode;
import org.apache.commons.codec.binary.Base64;
/**
* KartePaneTransferHandler
*
* @author Minagawa,Kazushi. Digital Globe, Inc.
*/
public class SOATransferHandler extends TransferHandler implements IKarteTransferHandler {
private static DataFlavor nixFileDataFlavor;
static {
try {
nixFileDataFlavor = new DataFlavor("text/uri-list;class=java.lang.String");
} catch (Exception e) {
}
}
private final KartePane soaPane;
private JTextPane source;
private boolean shouldRemove;
// Start and end position in the source text.
// We need this information when performing a MOVE
// in order to remove the dragged text from the source.
Position p0 = null, p1 = null;
public SOATransferHandler(KartePane soaPane) {
this.soaPane = soaPane;
}
@Override
public boolean importData(TransferHandler.TransferSupport support) {
JTextPane tc = (JTextPane)support.getComponent();
if (!canImport(support)) {
return false;
}
Transferable tr = support.getTransferable();
if (tc.equals(source) &&
(tc.getCaretPosition() >= p0.getOffset()) &&
(tc.getCaretPosition() <= p1.getOffset())) {
shouldRemove = false;
return true;
}
try {
if (tr.isDataFlavorSupported(LocalStampTreeNodeTransferable.localStampTreeNodeFlavor)) {
// StampTreeNodeを受け入れる
shouldRemove = false;
StampTreeNode droppedNode = (StampTreeNode) tr.getTransferData(LocalStampTreeNodeTransferable.localStampTreeNodeFlavor);
return doStampInfoDrop(droppedNode);
} else if (tr.isDataFlavorSupported(ImageEntryTransferable.imageEntryFlavor)) {
// シェーマボックスからのDnDを受け入れる
ImageEntry entry = (ImageEntry) tr.getTransferData(ImageEntryTransferable.imageEntryFlavor);
return doImageEntryDrop(entry);
} else if (tr.isDataFlavorSupported(SchemaListTransferable.schemaListFlavor)) {
// Paneからのシェーマを受け入れる
SchemaList list = (SchemaList) tr.getTransferData(SchemaListTransferable.schemaListFlavor);
return doSchemaDrop(list);
} else if (tr.isDataFlavorSupported(AttachmentTransferable.attachmentFlavor)) {
// PaneからのAttachmentを受け入れる
AttachmentModel attachment = (AttachmentModel)tr.getTransferData(AttachmentTransferable.attachmentFlavor);
return doAttachmentDrop(attachment);
} else if (tr.isDataFlavorSupported(DataFlavor.stringFlavor)) {
String str = (String) tr.getTransferData(DataFlavor.stringFlavor);
tc.replaceSelection(str);
shouldRemove = tc == source;
return true;
} else if (tr.isDataFlavorSupported(DataFlavor.javaFileListFlavor)) {
// Image File ならimportする
List<File> files = (List<File>) tr.getTransferData(DataFlavor.javaFileListFlavor);
return doFileDrop(files);
} else if (tr.isDataFlavorSupported(nixFileDataFlavor)) {
// Image File ならimportする
String data = (String)tr.getTransferData(nixFileDataFlavor);
return doFileDropNix(data);
}
} catch (UnsupportedFlavorException | IOException ufe) {
}
return false;
}
// Create a Transferable implementation that contains the
// selected text.
@Override
protected Transferable createTransferable(JComponent c) {
source = (JTextPane) c;
int start = source.getSelectionStart();
int end = source.getSelectionEnd();
Document doc = source.getDocument();
if (start == end) {
return null;
}
try {
p0 = doc.createPosition(start);
p1 = doc.createPosition(end);
} catch (BadLocationException e) {
}
String data = source.getSelectedText();
return new StringSelection(data);
}
@Override
public int getSourceActions(JComponent c) {
return COPY_OR_MOVE;
}
// Remove the old text if the action is a MOVE.
// However, we do not allow dropping on top of the selected text,
// so in that case do nothing.
@Override
protected void exportDone(JComponent c, Transferable data, int action) {
JTextComponent tc = (JTextComponent) c;
if (tc.isEditable() && (shouldRemove == true) && (action == MOVE)) {
if ((p0 != null) && (p1 != null)
&& (p0.getOffset() != p1.getOffset())) {
try {
tc.getDocument().remove(p0.getOffset(),
p1.getOffset() - p0.getOffset());
} catch (BadLocationException e) {
}
}
}
shouldRemove = false;
source = null;
}
@Override
public boolean canImport(TransferHandler.TransferSupport support) {
JTextPane tc = (JTextPane)support.getComponent();
boolean ok = tc.isEditable();
ok = ok && hasFlavor(support.getDataFlavors());
return ok;
}
/**
* Flavorリストのなかに受け入れられものがあるかどうかを返す。
* @param flavors
* @return
*/
protected boolean hasFlavor(DataFlavor[] flavors) {
boolean ret = false;
for (DataFlavor flavor : flavors) {
// String ok
if (DataFlavor.stringFlavor.equals(flavor)) {
ret = true;
break;
}
// StampTreeNode OK
else if (LocalStampTreeNodeTransferable.localStampTreeNodeFlavor.equals(flavor)) {
ret = true;
break;
}
// Schema OK
else if (SchemaListTransferable.schemaListFlavor.equals(flavor)) {
ret = true;
break;
}
// Attachment OK
else if (AttachmentTransferable.attachmentFlavor.equals(flavor)) {
ret = true;
break;
}
// Image OK
else if (ImageEntryTransferable.imageEntryFlavor.equals(flavor)) {
ret = true;
break;
}
// File OK
else if (DataFlavor.javaFileListFlavor.equals(flavor)) {
ret = true;
break;
}
// Unix|Linux File List maybe jdk1.6
else if (nixFileDataFlavor.equals(flavor)) {
ret = true;
break;
}
}
return ret;
}
/**
* DropされたModuleInfo(StampInfo)をインポートする。
* @param tr Transferable
* @return 成功した時 true
*/
private boolean doStampInfoDrop(StampTreeNode droppedNode) {
try {
// 葉の場合
if (droppedNode.isLeaf()) {
ModuleInfoBean stampInfo = (ModuleInfoBean) droppedNode.getStampInfo();
String role = stampInfo.getStampRole();
if (role.equals(IInfoModel.ROLE_TEXT)) {
soaPane.stampInfoDropped(stampInfo);
} else if (role.equals(IInfoModel.ROLE_SOA)) {
soaPane.stampInfoDropped(stampInfo);
}
return true;
}
// Dropされたノードの葉を列挙する
Enumeration e = droppedNode.preorderEnumeration();
ArrayList<ModuleInfoBean> textList = new ArrayList<>(5);
ArrayList<ModuleInfoBean> soaList = new ArrayList<>(5);
while (e.hasMoreElements()) {
StampTreeNode node = (StampTreeNode) e.nextElement();
if (node.isLeaf()) {
ModuleInfoBean stampInfo = (ModuleInfoBean) node.getStampInfo();
if (!stampInfo.isSerialized()) {
continue;
}
if (stampInfo.getEntity().equals(IInfoModel.ROLE_TEXT)) {
textList.add(stampInfo);
} else if (stampInfo.getEntity().equals(IInfoModel.ROLE_SOA)) {
soaList.add(stampInfo);
}
}
}
if (!textList.isEmpty()) {
soaPane.textStampInfoDropped(textList);
}
if (!soaList.isEmpty()) {
soaPane.stampInfoDropped(soaList);
}
return true;
} catch (Exception e) {
e.printStackTrace(System.err);
}
return false;
}
/**
* Dropされたシェーマをインポーオする。
* @param tr
* @return
*/
private boolean doSchemaDrop(SchemaList list) {
Chart chart = null;
ChartDocument doc = soaPane.getParent();
if(doc instanceof KarteEditor) {
chart = ((KarteEditor)doc).getContext();
}
try {
// Schemaリストを取得する
SchemaModel[] schemas = list.schemaList;
for (SchemaModel schema : schemas) {
soaPane.stampSchema(schema);
}
if (soaPane.getDraggedCount() > 0 && soaPane.getDrragedStamp() != null) {
soaPane.setDroppedCount(schemas.length);
}
return true;
} catch (Exception e) {
e.printStackTrace(System.err);
}
return false;
}
/**
* DropされたAttachmentをインポーオする。
* @param attachment AttachmentModel
* @return
*/
private boolean doAttachmentDrop(AttachmentModel attachment) {
try {
soaPane.stampAttachment(attachment);
if (soaPane.getDraggedCount() > 0 && soaPane.getDrragedStamp() != null) {
soaPane.setDroppedCount(1);
}
return true;
} catch (Exception e) {
e.printStackTrace(System.err);
}
return false;
}
/**
* Dropされたイメージをインポートする。
*/
private boolean doImageEntryDrop(ImageEntry entry) {
Chart chart = null;
ChartDocument doc = soaPane.getParent();
if(doc instanceof KarteEditor) {
chart = ((KarteEditor)doc).getContext();
}
try {
soaPane.imageEntryDropped(entry);
return true;
} catch (Exception e) {
e.printStackTrace(System.err);
}
return false;
}
/**
* DropされたイメージFileをインポートする。
*/
private boolean doFileDrop(List<File> files) {
try {
File file = files.get(0);
soaPane.fileDropped(file);
return true;
} catch (Exception ex) {
ex.printStackTrace(System.err);
}
return false;
}
/**
* StackOverFlow: Linux maybe Unix File Drop
* @param data uri-list
* @return File の時 true
*/
private boolean doFileDropNix(String data) {
File file = null;
for(StringTokenizer st = new StringTokenizer(data, "\r\n"); st.hasMoreTokens();) {
String token = st.nextToken().trim();
if(token.startsWith("#") || token.isEmpty()) {
// comment line, by RFC 2483
continue;
}
try {
file = new File(new URI(token));
break;
} catch(Exception e) {
e.printStackTrace(System.err);
}
}
if (file!=null) {
soaPane.fileDropped(file);
return true;
} else {
return false;
}
}
/**
* クリップボードへデータを転送する。
*/
@Override
public void exportToClipboard(JComponent comp, Clipboard clip, int action) {
super.exportToClipboard(comp, clip, action);
// cut の時 ...?
if (action == MOVE) {
JTextPane pane = (JTextPane) comp;
if (pane.isEditable()) {
pane.replaceSelection("");
}
}
}
@Override
public JComponent getComponent() {
return soaPane.getTextPane();
}
private boolean canPaste() {
if (!soaPane.getTextPane().isEditable()) {
return false;
}
Transferable t = Toolkit.getDefaultToolkit().getSystemClipboard().getContents(null);
if (t == null) {
return false;
}
return t.isDataFlavorSupported(DataFlavor.stringFlavor) ||
t.isDataFlavorSupported(LocalStampTreeNodeTransferable.localStampTreeNodeFlavor) ||
t.isDataFlavorSupported(SchemaListTransferable.schemaListFlavor) ||
t.isDataFlavorSupported(ImageEntryTransferable.imageEntryFlavor) ||
t.isDataFlavorSupported(AttachmentTransferable.attachmentFlavor) ||
t.isDataFlavorSupported(DataFlavor.javaFileListFlavor) ||
t.isDataFlavorSupported(nixFileDataFlavor);
}
@Override
public void enter(ActionMap map) {
// SOAPane がクリックされた状態
if (soaPane.getTextPane().isEditable()) {
map.get(GUIConst.ACTION_PASTE).setEnabled(canPaste());
map.get(GUIConst.ACTION_INSERT_TEXT).setEnabled(true);
map.get(GUIConst.ACTION_INSERT_SCHEMA).setEnabled(true);
map.get(GUIConst.ACTION_ATTACHMENT).setEnabled(true);
}
}
@Override
public void exit(ActionMap map) {
}
}