1 /*
2  * Copyright (C) 2009 The Android Open Source Project
3  *
4  * Licensed under the Eclipse Public License, Version 1.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.eclipse.org/org/documents/epl-v10.php
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.ide.eclipse.adt.internal.editors.layout.gle2;
18 
19 import com.android.ide.eclipse.adt.internal.editors.descriptors.ElementDescriptor;
20 import com.android.ide.eclipse.adt.internal.editors.layout.descriptors.ViewElementDescriptor;
21 
22 import org.eclipse.swt.dnd.ByteArrayTransfer;
23 import org.eclipse.swt.dnd.TransferData;
24 
25 import java.io.UnsupportedEncodingException;
26 
27 /**
28  * A d'n'd {@link Transfer} class that can transfer a <em>simplified</em> XML fragment
29  * to transfer elements and their attributes between {@link LayoutCanvas}.
30  * <p/>
31  * The implementation is based on the {@link ByteArrayTransfer} and what we transfer
32  * is text with the following fixed format:
33  * <p/>
34  * <pre>
35  * {element-name element-property ...
36  *  attrib_name="attrib_value"
37  *  attrib2="..."
38  *  {...inner elements...
39  *  }
40  * }
41  * {...next element...
42  * }
43  *
44  * </pre>
45  * The format has nothing to do with XML per se, except for the fact that the
46  * transfered content represents XML elements and XML attributes.
47  *
48  * <p/>
49  * The detailed syntax is:
50  * <pre>
51  * - ELEMENT     := {NAME PROPERTY*\nATTRIB_LINE*ELEMENT*}\n
52  * - PROPERTY    := $[A-Z]=[^ ]*
53  * - NAME        := [^\n=]+
54  * - ATTRIB_LINE := @URI:NAME=[^\n]*\n
55  * </pre>
56  *
57  * Elements are represented by {@link SimpleElement}s and their attributes by
58  * {@link SimpleAttribute}s, all of which have very specific properties that are
59  * specifically limited to our needs for drag'n'drop.
60  */
61 final class SimpleXmlTransfer extends ByteArrayTransfer {
62 
63     // Reference: http://www.eclipse.org/articles/Article-SWT-DND/DND-in-SWT.html
64 
65     private static final String TYPE_NAME = "android.ADT.simple.xml.transfer.1";    //$NON-NLS-1$
66     private static final int TYPE_ID = registerType(TYPE_NAME);
67     private static final SimpleXmlTransfer sInstance = new SimpleXmlTransfer();
68 
69     /** Private constructor. Use {@link #getInstance()} to retrieve the singleton instance. */
SimpleXmlTransfer()70     private SimpleXmlTransfer() {
71         // pass
72     }
73 
74     /** Returns the singleton instance. */
getInstance()75     public static SimpleXmlTransfer getInstance() {
76         return sInstance;
77     }
78 
79     /**
80      * Helper method that returns the FQCN transfered for the given {@link ElementDescriptor}.
81      * <p/>
82      * If the descriptor is a {@link ViewElementDescriptor}, the transfered data is the FQCN
83      * of the Android View class represented (e.g. "android.widget.Button").
84      * For any other non-null descriptor, the XML name is used.
85      * Otherwise it is null.
86      *
87      * @param desc The {@link ElementDescriptor} to transfer.
88      * @return The FQCN, XML name or null.
89      */
getFqcn(ElementDescriptor desc)90     public static String getFqcn(ElementDescriptor desc) {
91         if (desc instanceof ViewElementDescriptor) {
92             return ((ViewElementDescriptor) desc).getFullClassName();
93         } else if (desc != null) {
94             return desc.getXmlName();
95         }
96 
97         return null;
98     }
99 
100     @Override
getTypeIds()101     protected int[] getTypeIds() {
102         return new int[] { TYPE_ID };
103     }
104 
105     @Override
getTypeNames()106     protected String[] getTypeNames() {
107         return new String[] { TYPE_NAME };
108     }
109 
110     /** Transforms a array of {@link SimpleElement} into a native data transfer. */
111     @Override
javaToNative(Object object, TransferData transferData)112     protected void javaToNative(Object object, TransferData transferData) {
113         if (object == null || !(object instanceof SimpleElement[])) {
114             return;
115         }
116 
117         if (isSupportedType(transferData)) {
118             StringBuilder sb = new StringBuilder();
119             for (SimpleElement e : (SimpleElement[]) object) {
120                 sb.append(e.toString());
121             }
122             String data = sb.toString();
123 
124             try {
125                 byte[] buf = data.getBytes("UTF-8");  //$NON-NLS-1$
126                 super.javaToNative(buf, transferData);
127             } catch (UnsupportedEncodingException e) {
128                 // unlikely; ignore
129             }
130         }
131     }
132 
133     /**
134      * Recreates an array of {@link SimpleElement} from a native data transfer.
135      *
136      * @return An array of {@link SimpleElement} or null. The array may be empty.
137      */
138     @Override
nativeToJava(TransferData transferData)139     protected Object nativeToJava(TransferData transferData) {
140         if (isSupportedType(transferData)) {
141             byte[] buf = (byte[]) super.nativeToJava(transferData);
142             if (buf != null && buf.length > 0) {
143                 try {
144                     String s = new String(buf, "UTF-8"); //$NON-NLS-1$
145                     return SimpleElement.parseString(s);
146                 } catch (UnsupportedEncodingException e) {
147                     // unlikely to happen, but still possible
148                 }
149             }
150         }
151 
152         return null;
153     }
154 }
155