blob: 7ff523d24f541a7ca986a40eca23688755dacaad [file] [log] [blame]
//
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you 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.
//
= FolderOrdering103187
:page-layout: wiki
:page-tags: wiki, devfaq, needsreview
:markup-in-source: verbatim,quotes,macros
:jbake-status: published
:page-syntax: true
:description: Folder Ordering using Numeric Sort
:icons: font
:source-highlighter: pygments
:toc: left
:toc-title:
:experimental:
Folder ordering using numeric sort.
[[Infrastructure]]
== Infrastructure
*merged to trunk* (Jun 16 2007)
link:http://deadlock.netbeans.org/fisheye/changelog/netbeans/?cs=MAIN:jglick:20070616100742[changeset]
link:https://bz.apache.org/netbeans/show_bug.cgi?id=103187[Issue #103187]
link:http://deadlock.netbeans.org/hudson/job/trunk/lastSuccessfulBuild/artifact/nbbuild/build/generated/layers.txt[Current SFS with positions]
[[Overview]]
== Overview
[[Previous_state]]
=== Previous state
The Filesystems API specifies no order in folder children (`FileObject.getChildren`).
But the Datasystems API does assign a meaning to the order of `DataFolder.getChildren`.
Currently there are four ways a folder may get an order:
1. If the folder attribute `OpenIDE-Folder-Order` is specified, it is used.
(Any children not mentioned are put at the end.)
The format is `a/b/c` where `a` etc. are file or subfolder names.
`DataFolder.setOrder` sets this attribute.
. If the folder attribute `OpenIDE-Folder-SortMode` is specified, it is used.
The format is a single character, e.g. `M` for sort by modification time.
This method is hardly used any more, although the sort mode attribute is still shown
in the property sheet for folder nodes.
. If there are some relative ordering attributes, the folder is topologically sorted.
The attribute `a/b`, if set to the value `Boolean.TRUE`, means that
`a` should come somewhere before `b` in the folder
(not necessarily immediately before).
. Otherwise, the fallback order is alphabetical (same as `OpenIDE-Folder-SortMode=F`).
NOTE: although these methods can apply to any folder, in practice only folders on
the system filesystem, i.e. defined in XML layers, are explicitly ordered.
[[Problems_with_current_state]]
=== Problems with current state
The third way is the only one suitable for a modular system.
It has been in use since NetBeans 3.1.
Unfortunately it suffers from several drawbacks:
1. New developers are confused by ordering attributes.
For example, it is common to assume that `a/b` means `a` will immediate precede `b`,
which is not the case.
It is also common to assume that `a/b=false` is equivalent to `b/a=true`,
which is not the case (it has no effect).
. Typing relative ordering attributes in an XML layer is slow and results in bloat
since most filenames have to be written three times:
[source,xml]
----
<file name="a"/>
<attr name="a/b" boolvalue="true"/>
<file name="b"/>
<attr name="b/c" boolvalue="true"/>
<file name="c"/>
<attr name="c/d" boolvalue="true"/>
<file name="d"/>
----
. When many unrelated modules contribute to a folder, it is often not clear which modules
are more fundamental. (The convention is to have more optional modules declare order
relative to more fundamental modules, so the fundamental modules need not know about
the optional modules.)
To ensure that the folder order will hold up even when some modules are disabled,
it is common to overspecify ordering, which can lead to a near-quadratic number of attrs;
when this is not done, it is common for folders to fall into haphazard order
as the set of modules changes in unexpected ways.
. Attributes often need to be edited to accommodate renames or other changes
of unrelated files.
. Contradictory orderings lead to `TopologicalSortException`s, which are unfriendly
and difficult to debug.
. The module development support has a difficult time writing out ordering attributes
when the developer uses drag-and-drop to reorder files in an XML layer.
[[Solution]]
=== Solution
To address these problems, the solution is to add a fifth means of ordering a folder.
Every file in the folder could have a `Number`-valued attribute `position`.
Files would then be sorted (in increasing order) by position.
This would also be consistent with `Lookups.metaInfServices`.
For example:
[source,xml,subs="{markup-in-source}"]
----
<file name="a"><attr name="position" intvalue="100"/></file>
<file name="b"><attr name="position" intvalue="200"/></file>
<file name="c"><attr name="position" intvalue="300"/></file>
<file name="d"><attr name="position" intvalue="400"/></file>
----
[[Details]]
== Details
[[Ordering_semantics]]
=== Ordering semantics
Normally positive integers would be used for positions,
but floats or negative integers could be used for emergencies
in case an item needed to be inserted between two adjacent integers.
Files with no marked position would be placed at the end (and a brief warning logged).
Files with the same position would be ordered alphabetically (and a brief warning logged).
As an exception, you may mark files with the position 0 to indicate that
their position is irrelevant;
no warning is logged if several such files exist in the same folder.
Numeric and relative ordering can coexist, for backwards compatibility.
Uses of relative ordering attributes should be logged as warnings
to assist in migration.
[[Setting_order]]
=== Setting order
`DataFolder.setOrder` should remove any of the old ordering methods in effect
and set positions on each file in the folder.
The tricky part is to avoid changing the positions of files which already have positions
unless necessary to accommodate the new order.
For example, given an initial folder content:
[source]
----
a (#100)
b (#200)
c (#300)
d
----
and asked to set the order to `d a c b`,
it would be best to change only two attributes, e.g.:
[source]
----
d (#0)
a (#100)
c (#300)
b (#400)
----
or something similar.
I.e. first need to compute a minimal set of transpositions.
TIP: decompose permutation graph into disjoint cycles.
Then use some heuristics to decide
which of a pair in a transposition to "move",
and what its new position should be.
Heuristics could include:
1. Prefer to add a position to a file which lacks one than to change an existing position.
. Prefer round numbers like 100 to numbers like 123, and prefer integers to floats.
. Prefer to move a locally modified file to an untouched one.
(Can be implemented by looking at position of a `MultiFileObject`'s leader filesystem.)
[[Java_APIs]]
=== Java APIs
Introduce methods to order a folder in `FileUtil` in the Filesystems API,
and to set a new order.
This avoids duplicating somewhat subtle code.
Since callers might be ignoring some files in a folder (e.g. `*.form`)
the list of files to consider needs to be passed as well.
[source,java,subs="{markup-in-source}"]
----
class FileUtil {
public static List<FileObject> getOrder(Collection<FileObject> children, boolean logWarnings);
/** @postcondition children = getOrder(children, false) */
public static void setOrder(List<FileObject> children) throws IOException;
public static boolean affectsOrder(FileAttributeEvent event);
// ...
}
----
(used in: `core/startup`, `core/windows`, `editor/mimelookup/impl`, `openide/loaders`)
[[Separators]]
=== Separators
For menu folders and other places where there is a distinguished null value
or other separator,
it is generally permitted to have extra separators.
(Leading, trailing, or adjacent duplicate separators are ignored.)
With numeric ordering, a simple convention could help group items into separated blocks.
For example:
[source,xml,subs="{markup-in-source}"]
----
<file name="sep500"><attr name="position" intvalue="500"/></file>
<file name="cut"><attr name="position" intvalue="600"/></file>
<file name="copy"><attr name="position" intvalue="700"/></file>
<file name="paste"><attr name="position" intvalue="800"/></file>
<file name="delete"><attr name="position" intvalue="900"/></file>
<file name="sep1000"><attr name="position" intvalue="1000"/></file>
<file name="undo"><attr name="position" intvalue="1200"/></file>
<file name="redo"><attr name="position" intvalue="1300"/></file>
<file name="sep1500"><attr name="position" intvalue="1500"/></file>
----
will display as:
[source,java,subs="{markup-in-source}"]
----
cut
copy
paste
delete
------
undo
redo
----
but it is easy to add new items at the top, bottom, or middle of any block;
add new blocks at any position; divide existing blocks; etc.
[[SideBar_and_position_attributes]]
=== SideBar and position attributes
The editor folders under `SideBar` were using a `position`
attribute for a different purpose.
These have been converted (compatibly) to use `location` instead.
[[Changed_code]]
== Changed code
Scope is "Big IDE" with all clusters (incl. CND and Profiler).
[[Readers_of_relative_ordering_attrs]]
=== Readers of relative ordering attrs
* `FolderOrder` (`openide/loaders`; also `DataFolder` and `FolderList`)
This is the canonical reader of ordering attributes.
* `CompoundFolderChildren` (`editor/mimelookup/impl`; also `FolderChildren`)
Does its own reading to order the result of merging together several folders.
Víťa agrees it could probably be changed to use `MultiFileSystem` instead,
or could use any new sorting API (if it supported parallel folders),
or could directly implement sorting by position.
* `OptionUtilities` (`editor`)
Víťa says it is semi-obsolete, but similar to `CompoundFolderChildren`.
* `ModeParser` (`core/windows`)
Prefers to operate at Filesystems API level, for efficiency and predictability.
(Using `DataFolder` is slower and introduces asynchronous behavior.)
* `RecognizeInstanceFiles` (`core/startup`)
Cannot refer to Datasystems API.
[[Writers_of_relative_ordering_attrs]]
=== Writers of relative ordering attrs
* `LanguagesManager` (`languages/engine`) _(needs tuning)_
* `MenuFolderNode` and `ToolbarFolderNode` (`core/windows`)
* `CreatedModifiedFiles` (`apisupport/project`; also `ui.wizard.action.DataModel`)
* `WritableXMLFileSystem` (`apisupport/project`)
* `LanguageRegistry` (`scripting/gsf`) _(needs tuning)_
* `MidpPaletteProvider` (`mobility/designer2/midp`) _(done though untested)_
[[Uses_of_relative_ordering_attrs]]
=== Uses of relative ordering attrs
Fixed in bulk mode using `apisupport/relative2position`.
[[Commit_validation]]
=== Commit validation
`ValidateLayerConsistencyTest` (in `core`) should verify that:
1. No relative ordering attributes are in use on any folder.
. Neither `OpenIDE-Folder-Order` nor `OpenIDE-Folder-SortMode` are used.
. Any `position` attribute has a numeric value.
. If any file (or subfolder) in a folder has a `position` attribute,
then they all do; and all the values are distinct.
_Implemented._
Run not only in the `trunk` Hudson project (i.e. full IDE),
but also in `nbms-and-javadoc` (to check experimental modules).
[[API_Docs_Updated]]
=== API Docs Updated
* Modules API.
* `editor/mimelookup/impl` Javadoc
* xref:./DevFaqOrderAttributes.adoc[DevFaqOrderAttributes]
* xref:./NewAndNoteWorthyMilestone10.adoc[NewAndNoteWorthyMilestone10]
[[Misc._impl_still_left_to_do]]
=== Misc. impl still left to do
* Fix up `LanguagesManager` and `LanguageRegistry`.
_(some fixes done already; remainder probably best left to domain developers)_
* Change `FileUtil.setOrder` to be more conservative:
avoid changing existing `position` attributes if possible.
''(in progress; cf.
link:https://bz.apache.org/netbeans/show_bug.cgi?id=110981[issue #110981])''
* Fix up various ordering attrs which are not quite right.
Especially files which claim to be ordered in folders which do not care.
Also `Editors/text/*+xml/**` (e.g. Ant context menu) are generally not right.
_(generally will be left to whoever handles UI spec conformance bugs)_
* `nbbuild/build.xml#index-layer-paths` ought to order files. _(done)_
* Need some way of marking a file as not intended to be ordered.
E.g. position="0" or position="none".
Useful for e.g. hidden subfolders.
`getOrder` can put these wherever it likes but should never warn about them.
link:https://bz.apache.org/netbeans/show_bug.cgi?id=107550[Issue #107550] _(done)_
* Clean up experimental modules.
link:http://deadlock.netbeans.org/hudson/job/nbms-and-javadoc/lastSuccessfulBuild/testReport/org.netbeans.core.projects/ValidateLayerConsistencyTest/testFolderOrdering/[Current errors]
_(done)_