diff --git a/plugins/android-designer/android-designer.iml b/plugins/android-designer/android-designer.iml index a6cb9856732d..dc07d072b1ba 100644 --- a/plugins/android-designer/android-designer.iml +++ b/plugins/android-designer/android-designer.iml @@ -24,6 +24,7 @@ + diff --git a/plugins/android-designer/src/com/intellij/android/designer/designSurface/AndroidDesignerEditorPanel.java b/plugins/android-designer/src/com/intellij/android/designer/designSurface/AndroidDesignerEditorPanel.java index 37a1ba1643e7..216709a18dc3 100644 --- a/plugins/android-designer/src/com/intellij/android/designer/designSurface/AndroidDesignerEditorPanel.java +++ b/plugins/android-designer/src/com/intellij/android/designer/designSurface/AndroidDesignerEditorPanel.java @@ -40,6 +40,7 @@ import com.intellij.openapi.module.Module; import com.intellij.openapi.util.Computable; import com.intellij.openapi.util.Condition; import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.psi.PsiErrorElement; import com.intellij.psi.PsiFile; import com.intellij.psi.PsiManager; import com.intellij.psi.xml.XmlFile; @@ -56,7 +57,6 @@ import org.jetbrains.annotations.NotNull; import javax.swing.*; import java.awt.*; -import java.awt.image.BufferedImage; import java.util.List; /** @@ -137,6 +137,8 @@ public final class AndroidDesignerEditorPanel extends DesignerEditorPanel { parser.updateRootComponent(mySession, rootView); RadViewComponent newRootComponent = parser.getRootComponent(); + newRootComponent.setClientProperty(ModelParser.XML_FILE_KEY, myXmlFile); + PropertyParser propertyParser = new PropertyParser(myModule, myProfileAction.getProfileManager().getSelectedTarget()); newRootComponent.setClientProperty(PropertyParser.KEY, propertyParser); propertyParser.loadRecursive(newRootComponent); @@ -158,7 +160,10 @@ public final class AndroidDesignerEditorPanel extends DesignerEditorPanel { final String layoutXmlText = ApplicationManager.getApplication().runReadAction(new Computable() { @Override public String compute() { - return myXmlFile.getText(); + if (ModelParser.checkTag(myXmlFile.getRootTag())) { + return myXmlFile.getText(); + } + return ModelParser.NO_ROOT_CONTENT; } }); createRenderer(layoutXmlText, new ThrowableRunnable() { @@ -387,27 +392,4 @@ public final class AndroidDesignerEditorPanel extends DesignerEditorPanel { myPSIChangeListener.start(); } } - - private static class RootView extends JComponent { - private int myX; - private int myY; - private BufferedImage myImage; - - public RootView(BufferedImage image, int x, int y) { - myX = x; - myY = y; - setImage(image); - } - - public void setImage(BufferedImage image) { - myImage = image; - setBounds(myX, myY, image.getWidth(), image.getHeight()); - } - - @Override - public void paintComponent(Graphics g) { - super.paintComponent(g); - g.drawImage(myImage, 0, 0, null); - } - } } \ No newline at end of file diff --git a/plugins/android-designer/src/com/intellij/android/designer/designSurface/DropToOperation.java b/plugins/android-designer/src/com/intellij/android/designer/designSurface/DropToOperation.java new file mode 100644 index 000000000000..3ccb4f968921 --- /dev/null +++ b/plugins/android-designer/src/com/intellij/android/designer/designSurface/DropToOperation.java @@ -0,0 +1,88 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.android.designer.designSurface; + +import com.intellij.android.designer.model.ModelParser; +import com.intellij.android.designer.model.RadViewComponent; +import com.intellij.designer.designSurface.EditOperation; +import com.intellij.designer.designSurface.FeedbackLayer; +import com.intellij.designer.designSurface.OperationContext; +import com.intellij.designer.designSurface.feedbacks.AlphaComponent; +import com.intellij.designer.model.RadComponent; + +import javax.swing.*; +import java.awt.*; +import java.util.Collections; +import java.util.List; + +/** + * @author Alexander Lobas + */ +public class DropToOperation implements EditOperation { + private final RadViewComponent myContainer; + private final OperationContext myContext; + private List myComponents; + private JComponent myFeedback; + + public DropToOperation(RadViewComponent container, OperationContext context) { + myContainer = container; + myContext = context; + } + + @Override + public void setComponent(RadComponent component) { + myComponents = Collections.singletonList(component); + } + + @Override + public void setComponents(List components) { + myComponents = components; + } + + @Override + public void showFeedback() { + FeedbackLayer layer = myContext.getArea().getFeedbackLayer(); + + if (myFeedback == null) { + myFeedback = new AlphaComponent(Color.green); + layer.add(myFeedback); + myFeedback.setBounds(myContainer.getBounds(layer)); + layer.repaint(); + } + } + + @Override + public void eraseFeedback() { + if (myFeedback != null) { + FeedbackLayer layer = myContext.getArea().getFeedbackLayer(); + layer.remove(myFeedback); + layer.repaint(); + myFeedback = null; + } + } + + @Override + public boolean canExecute() { + return true; + } + + @Override + public void execute() throws Exception { + for (RadComponent component : myComponents) { + ModelParser.addComponent(myContainer, (RadViewComponent)component, null); + } + } +} \ No newline at end of file diff --git a/plugins/android-designer/src/com/intellij/android/designer/designSurface/RootView.java b/plugins/android-designer/src/com/intellij/android/designer/designSurface/RootView.java new file mode 100644 index 000000000000..acc95d5610da --- /dev/null +++ b/plugins/android-designer/src/com/intellij/android/designer/designSurface/RootView.java @@ -0,0 +1,75 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.android.designer.designSurface; + +import javax.swing.*; +import java.awt.*; +import java.awt.image.BufferedImage; +import java.util.ArrayList; +import java.util.List; + +/** + * @author Alexander Lobas + */ +public class RootView extends JComponent { + private int myX; + private int myY; + private BufferedImage myImage; + private List myEmptyRegions; + + public RootView(BufferedImage image, int x, int y) { + myX = x; + myY = y; + setImage(image); + } + + public void setImage(BufferedImage image) { + myImage = image; + myEmptyRegions = new ArrayList(); + setBounds(myX, myY, image.getWidth(), image.getHeight()); + } + + public void addEmptyRegion(int x, int y, int width, int height) { + if (new Rectangle(0, 0, myImage.getWidth(), myImage.getHeight()).contains(x, y)) { + EmptyRegion r = new EmptyRegion(); + r.myX = x; + r.myY = y; + r.myWidth = width; + r.myHeight = height; + r.myColor = new Color(~myImage.getRGB(x, y)); + myEmptyRegions.add(r); + } + } + + @Override + public void paintComponent(Graphics g) { + super.paintComponent(g); + g.drawImage(myImage, 0, 0, null); + + for (EmptyRegion r : myEmptyRegions) { + g.setColor(r.myColor); + g.fillRect(r.myX, r.myY, r.myWidth, r.myHeight); + } + } + + private static class EmptyRegion { + public Color myColor; + public int myX; + public int myY; + public int myWidth; + public int myHeight; + } +} \ No newline at end of file diff --git a/plugins/android-designer/src/com/intellij/android/designer/designSurface/TreeDropToOperation.java b/plugins/android-designer/src/com/intellij/android/designer/designSurface/TreeDropToOperation.java new file mode 100644 index 000000000000..80fd38068843 --- /dev/null +++ b/plugins/android-designer/src/com/intellij/android/designer/designSurface/TreeDropToOperation.java @@ -0,0 +1,36 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.android.designer.designSurface; + +import com.intellij.android.designer.model.ModelParser; +import com.intellij.android.designer.model.RadViewComponent; +import com.intellij.designer.componentTree.TreeEditOperation; +import com.intellij.designer.designSurface.OperationContext; +import com.intellij.designer.model.RadComponent; + +/** + * @author Alexander Lobas + */ +public class TreeDropToOperation extends TreeEditOperation { + public TreeDropToOperation(RadComponent host, OperationContext context) { + super(host, context); + } + + @Override + protected void execute(RadComponent insertBefore) throws Exception { + ModelParser.addComponent((RadViewComponent)myHost, (RadViewComponent)myComponents.get(0), (RadViewComponent)insertBefore); + } +} \ No newline at end of file diff --git a/plugins/android-designer/src/com/intellij/android/designer/model/ModelParser.java b/plugins/android-designer/src/com/intellij/android/designer/model/ModelParser.java index fa4a6a9692a5..020a3f689bdd 100644 --- a/plugins/android-designer/src/com/intellij/android/designer/model/ModelParser.java +++ b/plugins/android-designer/src/com/intellij/android/designer/model/ModelParser.java @@ -17,6 +17,7 @@ package com.intellij.android.designer.model; import com.android.ide.common.rendering.api.RenderSession; import com.android.ide.common.rendering.api.ViewInfo; +import com.intellij.android.designer.designSurface.RootView; import com.intellij.designer.model.MetaManager; import com.intellij.designer.model.MetaModel; import com.intellij.designer.model.RadComponent; @@ -26,10 +27,14 @@ import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.fileTypes.StdFileTypes; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Computable; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.psi.PsiErrorElement; import com.intellij.psi.XmlElementFactory; import com.intellij.psi.XmlRecursiveElementVisitor; import com.intellij.psi.xml.XmlFile; import com.intellij.psi.xml.XmlTag; +import com.intellij.xml.util.XmlUtil; +import org.jetbrains.annotations.Nullable; import javax.swing.*; import java.util.List; @@ -38,6 +43,13 @@ import java.util.List; * @author Alexander Lobas */ public class ModelParser extends XmlRecursiveElementVisitor { + public static final String NO_ROOT_CONTENT = + ""; + + public static final String XML_FILE_KEY = "XML_FILE"; + + private static final int EMPTY_COMPONENT_SIZE = 5; + private final MetaManager myMetaManager; private final XmlFile myXmlFile; private RadViewComponent myRootComponent; @@ -61,11 +73,11 @@ public class ModelParser extends XmlRecursiveElementVisitor { @Override public String compute() { XmlTag root = myXmlFile.getRootTag(); - if (root != null) { + if (checkTag(root)) { root.accept(ModelParser.this); + return myXmlFile.getText(); } - - return myXmlFile.getText(); + return NO_ROOT_CONTENT; } }); } @@ -111,19 +123,58 @@ public class ModelParser extends XmlRecursiveElementVisitor { return component; } + public static void addComponent(RadViewComponent container, RadViewComponent newComponent, @Nullable RadViewComponent insertBefore) + throws Exception { + newComponent.setParent(container); + + List children = container.getChildren(); + if (insertBefore == null) { + children.add(newComponent); + } + else { + children.add(children.indexOf(insertBefore), newComponent); + } + + setComponentTag(container.getTag(), newComponent, insertBefore == null ? null : insertBefore.getTag()); + + PropertyParser propertyParser = container.getRoot().getClientProperty(PropertyParser.KEY); + propertyParser.load(newComponent); + } + public static void setComponentTag(final XmlTag parentTag, final RadViewComponent component, final XmlTag nextTag) { ApplicationManager.getApplication().runWriteAction(new Runnable() { @Override public void run() { - Language language = StdFileTypes.XML.getLanguage(); - XmlTag xmlTag = - XmlElementFactory.getInstance(parentTag.getProject()).createTagFromText(component.getMetaModel().getCreation(), language); + Project project; + RadViewComponent root = null; + XmlFile xmlFile = null; - if (nextTag == null) { - xmlTag = parentTag.addSubTag(xmlTag, false); + if (!checkTag(parentTag) && component.getParent() == component.getRoot()) { + root = (RadViewComponent)component.getParent(); + xmlFile = root.getClientProperty(XML_FILE_KEY); + project = xmlFile.getProject(); } else { - xmlTag = (XmlTag)parentTag.addBefore(xmlTag, nextTag); + project = parentTag.getProject(); + } + + Language language = StdFileTypes.XML.getLanguage(); + XmlTag xmlTag = + XmlElementFactory.getInstance(project).createTagFromText("\n" + component.getMetaModel().getCreation(), language); + + if (checkTag(parentTag)) { + if (nextTag == null) { + xmlTag = parentTag.addSubTag(xmlTag, false); + } + else { + xmlTag = (XmlTag)parentTag.addBefore(xmlTag, nextTag); + } + } + else { + xmlTag.setAttribute("xmlns:android", "http://schemas.android.com/apk/res/android"); + xmlTag = (XmlTag)xmlFile.getDocument().add(xmlTag); + root.setTag(xmlFile.getDocument().getRootTag()); + XmlUtil.expandTag(xmlTag); } component.setTag(xmlTag); @@ -131,6 +182,15 @@ public class ModelParser extends XmlRecursiveElementVisitor { }); } + public static boolean checkTag(XmlTag tag) { + try { + return tag != null && tag.getFirstChild() != null && !(tag.getFirstChild() instanceof PsiErrorElement) && tag.getProject() != null; + } + catch (Throwable e) { + return false; + } + } + ////////////////////////////////////////////////////////////////////////////////////////// // // @@ -145,27 +205,26 @@ public class ModelParser extends XmlRecursiveElementVisitor { return myLayoutXmlText; } - public void updateRootComponent(RenderSession session, JComponent nativeComponent) throws Exception { - RadViewComponent rootComponent = myRootComponent; - - if (session.getRootViews().size() == 1) { - RadViewComponent newRootComponent = createComponent(myXmlFile.getRootTag(), myMetaManager.getModelByTag("")); - - rootComponent.setParent(newRootComponent); - newRootComponent.getChildren().add(rootComponent); - - myRootComponent = rootComponent = newRootComponent; + public void updateRootComponent(RenderSession session, RootView nativeComponent) throws Exception { + if (myRootComponent == null) { + myRootComponent = createComponent(myXmlFile.getRootTag(), myMetaManager.getModelByTag("")); + } + else if (session.getRootViews().size() == 1) { + RadViewComponent rootComponent = myRootComponent; + myRootComponent = createComponent(myXmlFile.getRootTag(), myMetaManager.getModelByTag("")); + myRootComponent.getChildren().add(rootComponent); + rootComponent.setParent(myRootComponent); } - updateRootComponent(rootComponent, session, nativeComponent); + updateRootComponent(myRootComponent, session, nativeComponent); } public static void updateRootComponent(RadViewComponent rootComponent, RenderSession session, - JComponent nativeComponent) { + RootView nativeComponent) { List views = session.getRootViews(); - int size = views.size(); List children = rootComponent.getChildren(); + int size = children.size(); for (int i = 0; i < size; i++) { updateComponent((RadViewComponent)children.get(i), views.get(i), nativeComponent, 0, 0); } @@ -176,7 +235,7 @@ public class ModelParser extends XmlRecursiveElementVisitor { private static void updateComponent(RadViewComponent component, ViewInfo view, - JComponent nativeComponent, + RootView nativeComponent, int parentX, int parentY) { component.setViewInfo(view); @@ -184,11 +243,18 @@ public class ModelParser extends XmlRecursiveElementVisitor { int left = parentX + view.getLeft(); int top = parentY + view.getTop(); - component.setBounds(left, top, view.getRight() - view.getLeft(), view.getBottom() - view.getTop()); + int width = view.getRight() - view.getLeft(); + int height = view.getBottom() - view.getTop(); + + if (width < EMPTY_COMPONENT_SIZE && height < EMPTY_COMPONENT_SIZE) { + nativeComponent.addEmptyRegion(left, top, EMPTY_COMPONENT_SIZE, EMPTY_COMPONENT_SIZE); + } + + component.setBounds(left, top, Math.max(width, EMPTY_COMPONENT_SIZE), Math.max(height, EMPTY_COMPONENT_SIZE)); List views = view.getChildren(); List children = component.getChildren(); - int size = views.size(); + int size = children.size(); for (int i = 0; i < size; i++) { updateComponent((RadViewComponent)children.get(i), views.get(i), nativeComponent, left, top); diff --git a/plugins/android-designer/src/com/intellij/android/designer/model/RadLinearLayout.java b/plugins/android-designer/src/com/intellij/android/designer/model/RadLinearLayout.java index 7edb1f0b4213..e6cbf4533c44 100644 --- a/plugins/android-designer/src/com/intellij/android/designer/model/RadLinearLayout.java +++ b/plugins/android-designer/src/com/intellij/android/designer/model/RadLinearLayout.java @@ -40,22 +40,7 @@ public class RadLinearLayout extends RadViewLayout { } private void addNewComponent(RadViewComponent newComponent, @Nullable RadViewComponent insertBefore) throws Exception { - RadViewComponent container = (RadViewComponent)myContainer; - newComponent.setParent(container); - - List children = container.getChildren(); - if (insertBefore == null) { - children.add(newComponent); - } - else { - children.add(children.indexOf(insertBefore), newComponent); - } - - ModelParser - .setComponentTag(container.getTag(), newComponent, insertBefore == null ? null : insertBefore.getTag()); - - PropertyParser propertyParser = container.getRoot().getClientProperty(PropertyParser.KEY); - propertyParser.load(newComponent); + ModelParser.addComponent((RadViewComponent)myContainer, newComponent, insertBefore); } private class TreeCreateOperation extends TreeEditOperation { diff --git a/plugins/android-designer/src/com/intellij/android/designer/model/RadRootViewLayout.java b/plugins/android-designer/src/com/intellij/android/designer/model/RadRootViewLayout.java new file mode 100644 index 000000000000..58cb437d055a --- /dev/null +++ b/plugins/android-designer/src/com/intellij/android/designer/model/RadRootViewLayout.java @@ -0,0 +1,38 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.android.designer.model; + +import com.intellij.android.designer.designSurface.DropToOperation; +import com.intellij.android.designer.designSurface.TreeDropToOperation; +import com.intellij.designer.designSurface.EditOperation; +import com.intellij.designer.designSurface.OperationContext; + +/** + * @author Alexander Lobas + */ +public class RadRootViewLayout extends RadViewLayout { + @Override + public EditOperation processChildOperation(OperationContext context) { + if (myContainer.getChildren().isEmpty() && (context.isCreate() || context.isPaste()) && context.getComponents().size() == 1) { + if (context.isTree()) { + return new TreeDropToOperation(myContainer, context); + } + return new DropToOperation((RadViewComponent)myContainer, context); + } + + return null; + } +} \ No newline at end of file diff --git a/plugins/android-designer/src/com/intellij/android/designer/model/views-meta-model.xml b/plugins/android-designer/src/com/intellij/android/designer/model/views-meta-model.xml index 7918b73cf229..8cdf52ed49a1 100644 --- a/plugins/android-designer/src/com/intellij/android/designer/model/views-meta-model.xml +++ b/plugins/android-designer/src/com/intellij/android/designer/model/views-meta-model.xml @@ -5,15 +5,17 @@ + layout="com.intellij.android.designer.model.RadRootViewLayout" + tag="<root>" + delete="false"> + tag="merge" + delete="false"> diff --git a/plugins/android-designer/src/com/intellij/android/designer/propertyTable/editors/ResourceDialog.java b/plugins/android-designer/src/com/intellij/android/designer/propertyTable/editors/ResourceDialog.java index af914b52de7a..350ca69a65e1 100644 --- a/plugins/android-designer/src/com/intellij/android/designer/propertyTable/editors/ResourceDialog.java +++ b/plugins/android-designer/src/com/intellij/android/designer/propertyTable/editors/ResourceDialog.java @@ -83,6 +83,9 @@ public class ResourceDialog extends DialogWrapper implements TreeSelectionListen myContentPanel.addTab("Project", myProjectPanel.myComponent); myContentPanel.addTab("System", mySystemPanel.myComponent); + myProjectPanel.myTreeBuilder.expandAll(null); + mySystemPanel.myTreeBuilder.expandAll(null); + myContentPanel.addChangeListener(new ChangeListener() { @Override public void stateChanged(ChangeEvent e) { diff --git a/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/model/MetaManager.java b/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/model/MetaManager.java index 95e6a6ec50fe..345eb76b2663 100644 --- a/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/model/MetaManager.java +++ b/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/model/MetaManager.java @@ -92,6 +92,11 @@ public abstract class MetaManager { meta.setLayout((Class)classLoader.loadClass(layout)); } + String delete = element.getAttributeValue("delete"); + if (delete != null) { + meta.setDelete(Boolean.parseBoolean(delete)); + } + Element presentation = element.getChild("presentation"); if (presentation != null) { meta.setPresentation(presentation.getAttributeValue("title"), presentation.getAttributeValue("icon")); diff --git a/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/model/MetaModel.java b/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/model/MetaModel.java index 399439f46fc9..8fea8ebad914 100644 --- a/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/model/MetaModel.java +++ b/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/model/MetaModel.java @@ -36,6 +36,7 @@ public class MetaModel { private String myIconPath; private Icon myIcon; private String myCreation; + private boolean myDelete = true; private List myNormalProperties = Collections.emptyList(); private List myImportantProperties = Collections.emptyList(); private List myExpertProperties = Collections.emptyList(); @@ -75,6 +76,14 @@ public class MetaModel { myCreation = creation; } + public boolean canDelete() { + return myDelete; + } + + public void setDelete(boolean delete) { + myDelete = delete; + } + public String getTitle() { return myTitle; } diff --git a/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/model/RadComponent.java b/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/model/RadComponent.java index c84a5b97aa3c..05ed077f0b91 100644 --- a/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/model/RadComponent.java +++ b/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/model/RadComponent.java @@ -175,7 +175,7 @@ public abstract class RadComponent { ////////////////////////////////////////////////////////////////////////////////////////// public boolean canDelete() { - return true; + return myMetaModel == null || myMetaModel.canDelete(); } public void delete() throws Exception {