Standard Actions Specification
Author: Nathan Fiedler
The standard actions consist of delete, cut, copy, paste, and
drag-n-drop. This specification seeks to describe how these features
will perform on the columns view of the schema editor.
Phased Implementation
Because of the depth of features possible with copy/paste and
drag/drop, the design and implementation will be taken in a phased
approach. The most basic features will be implemented first, while
the complex features, such as refactoring, will come at a later
date.
Phase 1
In Phase 1 of the standard actions implementation, the copy and
paste, as well as drag and drop, will operate in much the same way as
the similar features in NetBeans. That is, copying and pasting
components will not involve any adjustments to references or
alterations to the data in any way. It is up to the user to ensure
that the schema is still valid before it is employed.
Supported Features
- Copy and paste will simply make a copy of the component in the
new location (no refactoring, no references)
- Pasting components with a namespace may elide the namespace if
it is not needed
- Drag and drop, like copy and paste, will be implemented in a
similar fashion, with the same limitations
- Modifying a component's visibility, such that a global
component can become a local component, and vice versa
Phase 2
Phase 2 of the implementation will add refactoring capabilities to
take care of some of the more mundane work on behalf of the user.
Supported Features
- Pasting a component where it cannot immediately be added will
result in finding the first matching child component that can
accept the paste.
- Pasting a component as a reference will be possible for those
types of components that can have references
- Pasting a component as a reference from another schema will
create an import and the reference to the source component
- Safely moving a component via refactoring
Delete
The delete feature was implemented earlier in the development
cycle and has been functional for some time. At first it was labelled
as "Remove" and would only allow deletion of components
that were not used elsewhere. In May 2006, the
RemoveAction was renamed to
SafelyDeleteAction and the standard NetBeans
DeleteAction was added to the schema component nodes.
This allows the user to forcefully delete nearly anything, without
regards to references to that component. On the other hand, if the
user wants to remove a component only after ensuring it is not used
elsewhere, they can select the "Safe Delete" action.
Cut, Copy, and Paste
The cut operation will need to be handled in the
same fashion as delete, since the user could cut a
component from the document and not paste it back again. This is
effectively the same as a delete, and since we cannot know what the
user will do, it should be handled defensively. Components that are
not being used elsewhere in the same document, can be cut, just as
they can be deleted.
The copy operation, on the other hand, is
unrestricted in that any component can be copied and pasted,
regardless of whether it is being used elsewhere.
Is the following necessary? If the name
of the original component is identical to that of another component
in the destination container, it must be changed to one that is
unique. This is similar to the behavior of copy/paste within Java
projects, in which a source file is renamed to avoid collision with
existing files.
Drag and Drop
Drag and drop can be treated differently from cut and paste, since
we know for certain the affected components will not be discarded.
That is, dragging and dropping a component is a single operation,
rather than two distinct events as in cut/paste — no components
can be lost.
Where drag/drop is particularly useful is in changing the order of
elements in a sequence. Whereas paste can only be applied on the
parent component, the drop can be performed between components. That
is, if an element is copied to the clipboard, then pasted, it must be
pasted onto a component that can contain it (e.g. a sequence). The
drag and drop interface allows the user to insert a component between
other components, thus allowing the order of elements in a sequence
to be changed, for example.
The condition of maintaining unique component names within a
container, mentioned in the copy and paste section above, holds for
drag and drop. That is, dropping a component into a container that
contains a component with the same name as that which is being
dropped will result in the dropped component being renamed to avoid
collision.
Global to Local
The table below describes what kinds of components can go where
when moving from a global context to a local one.
| Component Kind |
Destination Kind |
Result |
| Attribute |
Attribute Group |
Move, unless referenced. |
| Attribute |
Complex Type |
Move, unless referenced. |
| Attribute Group |
Attribute Group |
A reference is created. |
| Attribute Group |
Complex Type |
A reference is created. |
| Attribute Group |
Redefine |
Move, unless referenced. |
| Complex Type |
Element |
Move, unless referenced. |
| Complex Type |
Redefine |
Move, unless referenced. |
| Simple Type |
Attribute |
Move, unless referenced. |
| Simple Type |
Element |
Move, unless referenced. |
| Simple Type |
List |
Move, unless referenced. |
| Simple Type |
Redefine |
Move, unless referenced. |
| Simple Type |
Restriction |
Move, unless referenced. |
| Simple Type |
Union |
Move, unless referenced. |
Local to Global
The table below describes what kinds of components can be moved
from a local context to the global (child of schema root
component).
| Component Kind |
Parent Kind |
Exceptions |
| Attribute |
Attribute Group |
|
| Attribute |
Complex Type |
|
| Attribute Group |
Attribute Group |
Invalid case |
| Attribute Group |
Complex Type |
Invalid case |
| Attribute Group |
Redefine |
|
| Complex Type |
Element |
|
| Complex Type |
Redefine |
|
| Simple Type |
Attribute |
|
| Simple Type |
Element |
|
| Simple Type |
List |
|
| Simple Type |
Redefine |
|
| Simple Type |
Restriction |
|
| Simple Type |
Union |
|
Unless noted otherwise, the components can be moved from a local
context to the root schema component. If the component does not have
a name, it will be given one (e.g. "attribute0").
Implementation Details
Determining Ability to Paste
To determine if it is possible to paste a particular type of
component into another component, the SchemaComponent
class provides a method called canPaste() that takes a
SchemaComponent parameter and returns true
indicating that the component can contain that child. In the case of
CategoryNode this is trivial since the
childType field gives us the type of components being
shown in that category.
Visibility Changes
When a component is being copied to a destination context that is
different from its origin (global vs. local), it will be necessary to
create a new component of the appropriate type, then copy the
attributes from the original component to its clone. The model
handles this already.
Copying Components
The Component class provides a copy()
method that creates a deep clone of a component for a new parent.
This is used for the copy operation.
Format (i.e. DataFlavor)
By default, the AbstractNode implementation copies
itself to the clipboard and handles the paste via a
PasteType implementation. Since this is the most
straightforward means of supporting copy and paste, as well as drag
and drop, this will be the format used for the clipboard
transferables.
Aside from overriding createPasteTypes() in our
Node subclasses, there is a
ComponentPasteType utility class that does the work of
creating the PasteType implementations for our
nodes.
Operations
The Category node class will need to override
canCopy() to return false, as it makes little sense to
copy an entire category to the clipboard. Likewise, need to override
createPasteTypes() to ensure that only nodes of the same
type as what the category contains are permitted to be pasted.
The SchemaComponentNode class will override
canCopy() and canCut() to return
true, since basically any node can be copied and cut. It
also overrides the method createPasteTypes() to ensure
that only nodes representing components that can be contained in the
node's component are able to be pasted to the node.
To allow pasting a component to a logical parent, which may
not be the structural parent, the PasteType will employ
FindContainerVisitor to find the first reasonable parent
component for the paste.
Drag and Drop
NetBeans drag and drop support is a little strange. Most of the
drag-n-drop specific code is suppressed and it is all handled via
copy/paste and the org.openide.nodes.Index cookie. For
instance, the index parameter is always -1 when
Node.getDropType() is called, so ordering the nodes by
that method is useless (this problem is caused by the
DragDropUtilities class). Likewise, there is no use in
overriding drag() since it is apparently not called
(again, DragDropUtilities is the culprit, in that it
invokes clipboardCut() for drag-move).
To allow reordering the nodes, you must implement the
org.openide.nodes.Index cookie. This is most easily done
by implementing it on a subclass of
org.openide.nodes.Children, which provides an
implementation of a couple of the Index methods
already.