1 /* 2 * Copyright (C) 2008 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.xml; 18 19 import com.android.ide.eclipse.adt.AdtConstants; 20 import com.android.ide.eclipse.adt.AdtPlugin; 21 import com.android.ide.eclipse.adt.internal.editors.AndroidXmlEditor; 22 import com.android.ide.eclipse.adt.internal.editors.FirstElementParser; 23 import com.android.ide.eclipse.adt.internal.editors.descriptors.DocumentDescriptor; 24 import com.android.ide.eclipse.adt.internal.editors.descriptors.ElementDescriptor; 25 import com.android.ide.eclipse.adt.internal.editors.uimodel.UiDocumentNode; 26 import com.android.ide.eclipse.adt.internal.sdk.AndroidTargetData; 27 import com.android.ide.eclipse.adt.internal.sdk.Sdk; 28 import com.android.sdklib.IAndroidTarget; 29 import com.android.sdklib.SdkConstants; 30 31 import org.eclipse.core.resources.IFile; 32 import org.eclipse.core.resources.IProject; 33 import org.eclipse.core.runtime.IStatus; 34 import org.eclipse.ui.IEditorInput; 35 import org.eclipse.ui.IEditorPart; 36 import org.eclipse.ui.PartInitException; 37 import org.eclipse.ui.part.FileEditorInput; 38 import org.w3c.dom.Document; 39 40 /** 41 * Multi-page form editor for /res/xml XML files. 42 */ 43 public class XmlEditor extends AndroidXmlEditor { 44 45 public static final String ID = AdtConstants.EDITORS_NAMESPACE + ".xml.XmlEditor"; //$NON-NLS-1$ 46 47 /** Root node of the UI element hierarchy */ 48 private UiDocumentNode mUiRootNode; 49 50 /** 51 * Creates the form editor for resources XML files. 52 */ XmlEditor()53 public XmlEditor() { 54 super(); 55 } 56 57 /** 58 * Returns the root node of the UI element hierarchy, which here 59 * is the document node. 60 */ 61 @Override getUiRootNode()62 public UiDocumentNode getUiRootNode() { 63 return mUiRootNode; 64 } 65 66 // ---- Static ---- 67 68 /** 69 * Indicates if this is a file that this {@link XmlEditor} can handle. 70 * <p/> 71 * The {@link XmlEditor} can handle XML files that have a <searchable> or 72 * <Preferences> root XML element with the adequate xmlns:android attribute. 73 * 74 * @return True if the {@link XmlEditor} can handle that file. 75 */ canHandleFile(IFile file)76 public static boolean canHandleFile(IFile file) { 77 if (AdtPlugin.DEBUG_XML_FILE_INIT) { 78 AdtPlugin.log(IStatus.INFO, "canHandleFile(%1$s)", file.getFullPath().toOSString()); 79 } 80 // we need the target of the file's project to access the descriptors. 81 IProject project = file.getProject(); 82 IAndroidTarget target = Sdk.getCurrent().getTarget(project); 83 if (AdtPlugin.DEBUG_XML_FILE_INIT) { 84 AdtPlugin.log(IStatus.INFO, " target=%1$s", target); 85 } 86 if (target != null) { 87 // Note: the target data can be null when an SDK is not finished loading yet. 88 // We can potentially arrive here when Eclipse is started with a file previously 89 // open and the resource gets refreshed -- at that point we may not have the SDK yet. 90 AndroidTargetData data = Sdk.getCurrent().getTargetData(target); 91 92 FirstElementParser.Result result = FirstElementParser.parse( 93 file.getLocation().toOSString(), 94 SdkConstants.NS_RESOURCES); 95 if (AdtPlugin.DEBUG_XML_FILE_INIT) { 96 AdtPlugin.log(IStatus.INFO, " data=%1$s, result=%2$s", data, result); 97 } 98 99 if (result != null && data != null) { 100 String name = result.getElement(); 101 if (AdtPlugin.DEBUG_XML_FILE_INIT) { 102 AdtPlugin.log(IStatus.INFO, " name=%1$s, xmlnsprefix=%2$s", name, 103 result.getXmlnsPrefix()); 104 } 105 if (name != null && result.getXmlnsPrefix() != null) { 106 DocumentDescriptor desc = data.getXmlDescriptors().getDescriptor(); 107 for (ElementDescriptor elem : desc.getChildren()) { 108 if (elem.getXmlName().equals(name)) { 109 // This is an element that this document can handle 110 return true; 111 } 112 } 113 } 114 } 115 } 116 117 if (AdtPlugin.DEBUG_XML_FILE_INIT) { 118 AdtPlugin.log(IStatus.INFO, " File cannot be handled"); 119 } 120 121 return false; 122 } 123 124 // ---- Base Class Overrides ---- 125 126 /** 127 * Returns whether the "save as" operation is supported by this editor. 128 * <p/> 129 * Save-As is a valid operation for the ManifestEditor since it acts on a 130 * single source file. 131 * 132 * @see IEditorPart 133 */ 134 @Override isSaveAsAllowed()135 public boolean isSaveAsAllowed() { 136 return true; 137 } 138 139 /** 140 * Create the various form pages. 141 */ 142 @Override createFormPages()143 protected void createFormPages() { 144 try { 145 addPage(new XmlTreePage(this)); 146 } catch (PartInitException e) { 147 AdtPlugin.log(e, "Error creating nested page"); //$NON-NLS-1$ 148 } 149 150 } 151 152 /* (non-java doc) 153 * Change the tab/title name to include the project name. 154 */ 155 @Override setInput(IEditorInput input)156 protected void setInput(IEditorInput input) { 157 super.setInput(input); 158 if (input instanceof FileEditorInput) { 159 FileEditorInput fileInput = (FileEditorInput) input; 160 IFile file = fileInput.getFile(); 161 setPartName(String.format("%1$s", file.getName())); 162 } 163 } 164 165 /** 166 * Processes the new XML Model, which XML root node is given. 167 * 168 * @param xml_doc The XML document, if available, or null if none exists. 169 */ 170 @Override xmlModelChanged(Document xml_doc)171 protected void xmlModelChanged(Document xml_doc) { 172 // init the ui root on demand 173 initUiRootNode(false /*force*/); 174 175 mUiRootNode.loadFromXmlNode(xml_doc); 176 177 super.xmlModelChanged(xml_doc); 178 } 179 180 /** 181 * Creates the initial UI Root Node, including the known mandatory elements. 182 * @param force if true, a new UiRootNode is recreated even if it already exists. 183 */ 184 @Override initUiRootNode(boolean force)185 protected void initUiRootNode(boolean force) { 186 // The root UI node is always created, even if there's no corresponding XML node. 187 if (mUiRootNode == null || force) { 188 Document doc = null; 189 if (mUiRootNode != null) { 190 doc = mUiRootNode.getXmlDocument(); 191 } 192 193 // get the target data from the opened file (and its project) 194 AndroidTargetData data = getTargetData(); 195 196 DocumentDescriptor desc; 197 if (data == null) { 198 desc = new DocumentDescriptor("temp", null /*children*/); 199 } else { 200 desc = data.getXmlDescriptors().getDescriptor(); 201 } 202 203 mUiRootNode = (UiDocumentNode) desc.createUiNode(); 204 mUiRootNode.setEditor(this); 205 206 onDescriptorsChanged(doc); 207 } 208 } 209 210 // ---- Local Methods ---- 211 212 /** 213 * Reloads the UI manifest node from the XML, and calls the pages to update. 214 */ onDescriptorsChanged(Document document)215 private void onDescriptorsChanged(Document document) { 216 if (document != null) { 217 mUiRootNode.loadFromXmlNode(document); 218 } else { 219 mUiRootNode.reloadFromXmlNode(mUiRootNode.getXmlNode()); 220 } 221 } 222 223 } 224