This is an automated email from the ASF dual-hosted git repository.

rombert pushed a commit to branch issue/SLING-8308
in repository https://gitbox.apache.org/repos/asf/sling-ide-tooling.git

commit 64172898d5ac53c6a4f704d19666f9c0cffc2da4
Author: Robert Munteanu <[email protected]>
AuthorDate: Thu Apr 3 15:25:54 2025 +0200

    SLING-8308 - Add custom contribution for feature model projects
---
 eclipse/eclipse-core/plugin.xml                    | 15 +++++++
 .../ide/eclipse/core/ConfigurationHelper.java      | 12 +++++
 .../apache/sling/ide/eclipse/core/ProjectUtil.java | 41 ++++++++++++-----
 .../ide/eclipse/core/internal/ProjectHelper.java   |  5 +++
 .../internal/SlingFeatureFacetInstallDelegate.java | 24 ++++++++++
 .../SlingFeatureFacetUninstallDelegate.java        | 21 +++++++++
 .../m2e/internal/MavenProjectUtilsTest.java        | 23 ++++++++++
 .../m2e/internal/slingfeature-simple-pom.xml       | 35 +++++++++++++++
 .../eclipse-m2e-ui/lifecycle-mapping-metadata.xml  | 17 ++++++++
 eclipse/eclipse-m2e-ui/plugin.xml                  |  4 ++
 .../eclipse/m2e/internal/MavenProjectUtils.java    | 19 +++++++-
 .../internal/SlingFeatureProjectConfigurator.java  | 51 ++++++++++++++++++++++
 .../sling/ide/test/impl/ProjectUtilTest.java       | 28 ++++++++++++
 eclipse/eclipse-ui/plugin.xml                      | 36 ++++++++++++++-
 .../ui/nav/FeatureModelContentProvider.java        | 43 ++++++++++++++++++
 ...rovider.java => GenericModelLabelProvider.java} | 10 +++--
 .../ui/nav/model/FeatureModelRootFolder.java       | 51 ++++++++++++++++++++++
 17 files changed, 418 insertions(+), 17 deletions(-)

diff --git a/eclipse/eclipse-core/plugin.xml b/eclipse/eclipse-core/plugin.xml
index 94cea9d6..57a26e46 100644
--- a/eclipse/eclipse-core/plugin.xml
+++ b/eclipse/eclipse-core/plugin.xml
@@ -102,6 +102,21 @@
                <action facet="sling.launchpad" version="1.0" type="UNINSTALL">
                        <delegate 
class="org.apache.sling.ide.eclipse.core.internal.SlingLaunchpadFacetUninstallDelegate"/>
                </action>
+
+               <project-facet id="sling.feature">
+                       <label>Feature module</label>
+                       <description>Packaging module which contains a feature 
model.</description>
+               </project-facet>
+               
+               <project-facet-version facet="sling.feature" version="1.0"/>
+               
+               <action facet="sling.feature" version="1.0" type="INSTALL">
+                       <delegate 
class="org.apache.sling.ide.eclipse.core.internal.SlingFeatureFacetInstallDelegate"/>
+               </action>
+
+               <action facet="sling.feature" version="1.0" type="UNINSTALL">
+                       <delegate 
class="org.apache.sling.ide.eclipse.core.internal.SlingFeatureFacetUninstallDelegate"/>
+               </action>
        </extension>  
 
        <!-- Define the Sling launchpad runtime type -->
diff --git 
a/eclipse/eclipse-core/src/org/apache/sling/ide/eclipse/core/ConfigurationHelper.java
 
b/eclipse/eclipse-core/src/org/apache/sling/ide/eclipse/core/ConfigurationHelper.java
index e9fc46b3..b0c7d300 100644
--- 
a/eclipse/eclipse-core/src/org/apache/sling/ide/eclipse/core/ConfigurationHelper.java
+++ 
b/eclipse/eclipse-core/src/org/apache/sling/ide/eclipse/core/ConfigurationHelper.java
@@ -68,4 +68,16 @@ public class ConfigurationHelper {
                
 
        }
+       
+    public static void convertToFeatureProject(IProject project, IPath 
featureDirectoryPath) throws CoreException {
+
+        IProjectFacet facet = 
ProjectFacetsManager.getProjectFacet("sling.feature");
+        IFacetedProject fp2 = ProjectFacetsManager.create(project, true, null);
+        fp2.installProjectFacet(facet.getLatestVersion(), null, null);
+        project.refreshLocal(IResource.DEPTH_INFINITE, new 
NullProgressMonitor());
+
+        if (featureDirectoryPath != null) {
+            ProjectUtil.setFeatureModelPath(project, featureDirectoryPath);
+        }
+    }
 }
diff --git 
a/eclipse/eclipse-core/src/org/apache/sling/ide/eclipse/core/ProjectUtil.java 
b/eclipse/eclipse-core/src/org/apache/sling/ide/eclipse/core/ProjectUtil.java
index 92ab044c..54bd3047 100644
--- 
a/eclipse/eclipse-core/src/org/apache/sling/ide/eclipse/core/ProjectUtil.java
+++ 
b/eclipse/eclipse-core/src/org/apache/sling/ide/eclipse/core/ProjectUtil.java
@@ -19,6 +19,7 @@ package org.apache.sling.ide.eclipse.core;
 import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
+import java.util.function.Predicate;
 
 import org.apache.sling.ide.eclipse.core.internal.Activator;
 import org.apache.sling.ide.eclipse.core.internal.ProjectHelper;
@@ -43,6 +44,7 @@ public abstract class ProjectUtil {
 
     private static final String PROPERTY_SYNC_ROOT = Activator.PLUGIN_ID + 
".content_sync_root";
     private static final String PROPERTY_PROVISIONING_MODEL_DIR = 
Activator.PLUGIN_ID + ".provisioning_model_dir";
+    private static final String PROPERTY_FEATURE_MODEL_DIR = 
Activator.PLUGIN_ID + ".feature_model_dir";
     
     private static final String PROPERTY_SYNC_ROOT_DEFAULT_VALUE = "jcr_root";
     
@@ -168,24 +170,30 @@ public abstract class ProjectUtil {
        }
 
        public static IPath getProvisioningModelPath(IProject project) {
-               
-               if ( project == null || !project.isOpen() || 
!ProjectHelper.isLaunchpadProject(project)) {
-                       return null;
-               }
-               
+           
+           return getProjectPath(project, ProjectHelper::isLaunchpadProject, 
PROPERTY_PROVISIONING_MODEL_DIR);
+
+       }
+       
+    private static IPath getProjectPath(IProject project, Predicate<IProject> 
requiredTest, String key) {
+        
+        if ( project == null || !project.isOpen() || 
!requiredTest.test(project)) {
+            return null;
+        }
+        
         IScopeContext projectScope = new ProjectScope(project);
         IEclipsePreferences projectNode = 
projectScope.getNode(Activator.PLUGIN_ID);
         if ( projectNode == null ) {
-               return null;
+            return null;
         }
         
-        String propertyValue = 
projectNode.get(PROPERTY_PROVISIONING_MODEL_DIR, null);
+        String propertyValue = projectNode.get(key, null);
         if ( propertyValue == null ) {
-               return null;
+            return null;
         }
-               
-               return Path.fromPortableString(propertyValue);
-       }
+        
+        return Path.fromPortableString(propertyValue);
+    }
        
        public static void setProvisioningModelPath(IProject project, IPath 
modelPath) {
                
@@ -193,6 +201,17 @@ public abstract class ProjectUtil {
                
        }
        
+    public static IPath getFeatureModelPath(IProject project) {
+        
+        return getProjectPath(project, ProjectHelper::isFeatureProject, 
PROPERTY_FEATURE_MODEL_DIR);
+    }
+    
+    public static void setFeatureModelPath(IProject project, IPath modelPath) {
+
+        setPathPersistentProperty(project, modelPath, 
PROPERTY_FEATURE_MODEL_DIR);
+
+    }
+       
     
     /**
      * Loads a filter for the specified project
diff --git 
a/eclipse/eclipse-core/src/org/apache/sling/ide/eclipse/core/internal/ProjectHelper.java
 
b/eclipse/eclipse-core/src/org/apache/sling/ide/eclipse/core/internal/ProjectHelper.java
index 9adca608..a06d8aa0 100644
--- 
a/eclipse/eclipse-core/src/org/apache/sling/ide/eclipse/core/internal/ProjectHelper.java
+++ 
b/eclipse/eclipse-core/src/org/apache/sling/ide/eclipse/core/internal/ProjectHelper.java
@@ -42,6 +42,7 @@ import org.xml.sax.SAXException;
 
 public class ProjectHelper {
 
+    private static final String SLING_FEATURE_FACET_ID = "sling.feature";
     private static final String[] CONTENT_PACKAGE_STRUCTURE_BASE = new 
String[] { "/", "/content", "/src/main/content" };
 
        public static boolean isPotentialBundleProject(IProject project) {
@@ -144,6 +145,10 @@ public class ProjectHelper {
                return FacetHelper.containsFacet(project, "sling.launchpad");
        }
 
+       public static boolean isFeatureProject(IProject project) {
+           return FacetHelper.containsFacet(project, SLING_FEATURE_FACET_ID);
+       }
+
        public static IJavaProject asJavaProject(IProject project) {
                return JavaCore.create(project);
        }
diff --git 
a/eclipse/eclipse-core/src/org/apache/sling/ide/eclipse/core/internal/SlingFeatureFacetInstallDelegate.java
 
b/eclipse/eclipse-core/src/org/apache/sling/ide/eclipse/core/internal/SlingFeatureFacetInstallDelegate.java
new file mode 100644
index 00000000..875cb5cc
--- /dev/null
+++ 
b/eclipse/eclipse-core/src/org/apache/sling/ide/eclipse/core/internal/SlingFeatureFacetInstallDelegate.java
@@ -0,0 +1,24 @@
+/*
+ * 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.
+ */
+package org.apache.sling.ide.eclipse.core.internal;
+
+public class SlingFeatureFacetInstallDelegate extends BaseFacetInstallDelegate 
{
+
+       public SlingFeatureFacetInstallDelegate() {
+               setEnableValidationBuilderAndCommand(false);
+       }
+}
diff --git 
a/eclipse/eclipse-core/src/org/apache/sling/ide/eclipse/core/internal/SlingFeatureFacetUninstallDelegate.java
 
b/eclipse/eclipse-core/src/org/apache/sling/ide/eclipse/core/internal/SlingFeatureFacetUninstallDelegate.java
new file mode 100644
index 00000000..b3c87e11
--- /dev/null
+++ 
b/eclipse/eclipse-core/src/org/apache/sling/ide/eclipse/core/internal/SlingFeatureFacetUninstallDelegate.java
@@ -0,0 +1,21 @@
+/*
+ * 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.
+ */
+package org.apache.sling.ide.eclipse.core.internal;
+
+public class SlingFeatureFacetUninstallDelegate extends EmptyDelegate {
+
+}
diff --git 
a/eclipse/eclipse-m2e-test/src/org/apache/sling/ide/eclipse/m2e/internal/MavenProjectUtilsTest.java
 
b/eclipse/eclipse-m2e-test/src/org/apache/sling/ide/eclipse/m2e/internal/MavenProjectUtilsTest.java
index bdea1ab1..d5b590db 100644
--- 
a/eclipse/eclipse-m2e-test/src/org/apache/sling/ide/eclipse/m2e/internal/MavenProjectUtilsTest.java
+++ 
b/eclipse/eclipse-m2e-test/src/org/apache/sling/ide/eclipse/m2e/internal/MavenProjectUtilsTest.java
@@ -79,4 +79,27 @@ public class MavenProjectUtilsTest {
         actualJcrRoot = MavenProjectUtils.guessJcrRootFolder(rootPath);
         Assert.assertFalse(actualJcrRoot.isPresent());
        }
+       
+    @Test
+    public void inferDefaultFeatureModelDirectories() throws Exception {
+
+        IPath featuresDir = Path.fromPortableString("src/main/features");
+        
+        // create project
+        final IProject featureProject = projectRule.getProject();
+
+        MavenProjectAdapter project = new MavenProjectAdapter(featureProject);
+        project.createOrUpdateFile(Path.fromPortableString("pom.xml"), 
getClass().getResourceAsStream("slingfeature-simple-pom.xml"));
+        project.ensureDirectoryExists(featuresDir);
+        project.convertToMavenProject();
+
+        // conversion should enable the slingstart configurator and set the 
provisioning model path
+        new Poller(TimeUnit.MINUTES.toMillis(1)).pollUntil(new 
Callable<IPath>() {
+            @Override
+            public IPath call() throws Exception {
+                return ProjectUtil.getFeatureModelPath(featureProject);
+            }
+        }, equalTo(featuresDir));
+        
+    }  
 }
diff --git 
a/eclipse/eclipse-m2e-test/src/org/apache/sling/ide/eclipse/m2e/internal/slingfeature-simple-pom.xml
 
b/eclipse/eclipse-m2e-test/src/org/apache/sling/ide/eclipse/m2e/internal/slingfeature-simple-pom.xml
new file mode 100644
index 00000000..081f1e06
--- /dev/null
+++ 
b/eclipse/eclipse-m2e-test/src/org/apache/sling/ide/eclipse/m2e/internal/slingfeature-simple-pom.xml
@@ -0,0 +1,35 @@
+<!--
+    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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0"; 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"; 
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
http://maven.apache.org/xsd/maven-4.0.0.xsd";>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>org.apache.sling.ide.testing</groupId>
+    <artifactId>simple-feature</artifactId>
+    <version>1.0.0-SNAPSHOT</version>
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.sling</groupId>
+                <artifactId>slingfeature-maven-plugin</artifactId>
+                <version>1.9.0</version>
+                <extensions>true</extensions>
+                <executions>
+                    <execution>
+                        <goals>
+                            <goal>attach-features</goal>
+                        </goals>
+                    </execution>
+                </executions>                
+            </plugin>
+        </plugins>
+    </build>
+</project>
\ No newline at end of file
diff --git a/eclipse/eclipse-m2e-ui/lifecycle-mapping-metadata.xml 
b/eclipse/eclipse-m2e-ui/lifecycle-mapping-metadata.xml
index a24e1df5..b625ad45 100644
--- a/eclipse/eclipse-m2e-ui/lifecycle-mapping-metadata.xml
+++ b/eclipse/eclipse-m2e-ui/lifecycle-mapping-metadata.xml
@@ -118,5 +118,22 @@
                 </configurator>
             </action>
         </pluginExecution>
+        <pluginExecution>
+            <pluginExecutionFilter>
+                <groupId>org.apache.sling</groupId>
+                <artifactId>slingfeature-maven-plugin</artifactId>
+                <versionRange>[1.0.0,)</versionRange>
+                <goals>
+                    <goal>analyse-features</goal>
+                    <goal>aggregate-features</goal>
+                    <goal>attach-features</goal>
+                </goals>
+            </pluginExecutionFilter>
+            <action>
+                <configurator>
+                    
<id>org.apache.sling.ide.eclipse.m2e.slingfeatureConfigurator</id>
+                </configurator>
+            </action>
+        </pluginExecution>
     </pluginExecutions>
 </lifecycleMappingMetadata>
\ No newline at end of file
diff --git a/eclipse/eclipse-m2e-ui/plugin.xml 
b/eclipse/eclipse-m2e-ui/plugin.xml
index 13405b13..51e88a51 100644
--- a/eclipse/eclipse-m2e-ui/plugin.xml
+++ b/eclipse/eclipse-m2e-ui/plugin.xml
@@ -69,6 +69,10 @@
                 id="org.apache.sling.ide.eclipse.m2e.slingstartConfigurator"
                 
class="org.apache.sling.ide.eclipse.m2e.internal.SlingstartProjectConfigurator"
                 name="Sling Launchpad Configurator" />
+        <configurator 
+                id="org.apache.sling.ide.eclipse.m2e.slingfeatureConfigurator"
+                
class="org.apache.sling.ide.eclipse.m2e.internal.SlingFeatureProjectConfigurator"
+                name="Sling Feature Configurator" />
     </extension>
     
     <!-- generic error marker (not bound to any quick fixes) for errors in any 
project configurators -->
diff --git 
a/eclipse/eclipse-m2e-ui/src/org/apache/sling/ide/eclipse/m2e/internal/MavenProjectUtils.java
 
b/eclipse/eclipse-m2e-ui/src/org/apache/sling/ide/eclipse/m2e/internal/MavenProjectUtils.java
index 32aada93..fc31fa96 100644
--- 
a/eclipse/eclipse-m2e-ui/src/org/apache/sling/ide/eclipse/m2e/internal/MavenProjectUtils.java
+++ 
b/eclipse/eclipse-m2e-ui/src/org/apache/sling/ide/eclipse/m2e/internal/MavenProjectUtils.java
@@ -97,7 +97,24 @@ public class MavenProjectUtils {
        return new LinkedHashSet<>(candidates);
        
     }
-    
+
+    public static Set<String> 
getFeatureDirectoryCandidateLocations(MavenProject mavenProject) {
+        List<String> candidates = new ArrayList<>();
+        candidates.add("src/main/features");
+        candidates.add("src/test/features");
+        
+        Plugin slingFeaturePlugin = 
mavenProject.getPlugin("org.apache.sling:slingfeature-maven-plugin");
+        if ( slingFeaturePlugin.getConfiguration() instanceof Xpp3Dom ) {
+            Xpp3Dom config = (Xpp3Dom) slingFeaturePlugin.getConfiguration();
+            Xpp3Dom featuresDir = config.getChild("features");
+            if ( featuresDir != null ) {
+                 candidates.add(0, featuresDir.getValue());
+            }           
+        }
+        
+        return new LinkedHashSet<>(candidates);        
+    }
+
     private MavenProjectUtils() {
         
     }
diff --git 
a/eclipse/eclipse-m2e-ui/src/org/apache/sling/ide/eclipse/m2e/internal/SlingFeatureProjectConfigurator.java
 
b/eclipse/eclipse-m2e-ui/src/org/apache/sling/ide/eclipse/m2e/internal/SlingFeatureProjectConfigurator.java
new file mode 100644
index 00000000..9dfd6b1b
--- /dev/null
+++ 
b/eclipse/eclipse-m2e-ui/src/org/apache/sling/ide/eclipse/m2e/internal/SlingFeatureProjectConfigurator.java
@@ -0,0 +1,51 @@
+/*
+ * 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.
+ */
+package org.apache.sling.ide.eclipse.m2e.internal;
+
+import java.util.Set;
+
+import org.apache.sling.ide.eclipse.core.ConfigurationHelper;
+import org.eclipse.core.resources.IFolder;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.m2e.core.project.configurator.ProjectConfigurationRequest;
+
+public class SlingFeatureProjectConfigurator extends 
AbstractProjectConfigurator {
+
+       @Override
+       public void configure(ProjectConfigurationRequest request, 
IProgressMonitor monitor) throws CoreException {
+               
+               IProject project = request.mavenProjectFacade().getProject();
+               Set<String> candidates = 
MavenProjectUtils.getFeatureDirectoryCandidateLocations(request.mavenProject());
+               IPath featuresPath = null; 
+               
+               for ( String candidate : candidates ) {
+                       IPath candidatePath = 
Path.fromPortableString(candidate);
+                       IFolder featuresFolder = 
project.getFolder(candidatePath);
+                       if ( featuresFolder.exists() ) {
+                               featuresPath = candidatePath;
+                               break;
+                       }
+               }
+               
+               trace("Configuring project {0} with features path {1}", 
project.getName(), featuresPath);
+               ConfigurationHelper.convertToFeatureProject(project, 
featuresPath);
+       }
+}
diff --git 
a/eclipse/eclipse-test/src/org/apache/sling/ide/test/impl/ProjectUtilTest.java 
b/eclipse/eclipse-test/src/org/apache/sling/ide/test/impl/ProjectUtilTest.java
index a5a19f5c..0b2debba 100644
--- 
a/eclipse/eclipse-test/src/org/apache/sling/ide/test/impl/ProjectUtilTest.java
+++ 
b/eclipse/eclipse-test/src/org/apache/sling/ide/test/impl/ProjectUtilTest.java
@@ -142,4 +142,32 @@ public class ProjectUtilTest {
        
        assertThat("provisioning model path", 
ProjectUtil.getProvisioningModelPath(contentProject), equalTo(modelPath));
     }
+    
+    @Test
+    public void noFeaturesDirectory() throws Exception {
+        
+        assertThat("feature model path", 
ProjectUtil.getFeatureModelPath(contentProject), nullValue());
+    }
+
+    @Test
+    public void existingFeaturesDirectoryWithoutLaunchpadNature() throws 
Exception {
+
+        IPath modelPath = Path.fromPortableString("src/main/features");
+        project.ensureDirectoryExists(modelPath);
+        ProjectUtil.setFeatureModelPath(contentProject, modelPath);
+        
+        assertThat("feature model path", 
ProjectUtil.getFeatureModelPath(contentProject), nullValue());
+    }
+
+    @Test
+    public void existingFeaturessDirectoryWithLaunchpadNature() throws 
Exception {
+        
+        project.installFacet("sling.feature", "1.0");
+        
+        IPath modelPath = Path.fromPortableString("src/main/features");
+        project.ensureDirectoryExists(modelPath);
+        ProjectUtil.setFeatureModelPath(contentProject, modelPath);
+        
+        assertThat("feature model path", 
ProjectUtil.getFeatureModelPath(contentProject), equalTo(modelPath));
+    }    
 }
diff --git a/eclipse/eclipse-ui/plugin.xml b/eclipse/eclipse-ui/plugin.xml
index a37b1a47..e3334449 100644
--- a/eclipse/eclipse-ui/plugin.xml
+++ b/eclipse/eclipse-ui/plugin.xml
@@ -231,6 +231,7 @@
                                <contentExtension 
pattern="org.apache.sling.ide.eclipse-ui.navigatorJcrContent" />
                                <contentExtension 
pattern="org.apache.sling.ide.eclipse-ui.navigatorVaultMetaInf" />
                                <contentExtension 
pattern="org.apache.sling.ide.eclipse-ui.navigatorProvisioningModel" />
+                               <contentExtension 
pattern="org.apache.sling.ide.eclipse-ui.navigatorFeatureModel" />
                                <contentExtension 
pattern="org.apache.sling.ide.eclipse-ui.linkHelper"/>        
                        </includes>
                </viewerContentBinding>
@@ -356,7 +357,7 @@
            name="Provisioning Model"
            appearsBefore="org.eclipse.jst.jee.ui.ejb"
            
contentProvider="org.apache.sling.ide.eclipse.ui.nav.ProvisioningModelContentProvider"
-           
labelProvider="org.apache.sling.ide.eclipse.ui.nav.ProvisioningModelLabelProvider"
+           
labelProvider="org.apache.sling.ide.eclipse.ui.nav.GenericModelLabelProvider"
            icon="icons/obj16/sling_misc.gif">
           <triggerPoints>
               <and>
@@ -382,6 +383,36 @@
              </and>
           </possibleChildren>
       </navigatorContent>
+      <navigatorContent 
id="org.apache.sling.ide.eclipse-ui.navigatorFeatureModel" 
+           name="Feature Model"
+           appearsBefore="org.eclipse.jst.jee.ui.ejb"
+           
contentProvider="org.apache.sling.ide.eclipse.ui.nav.FeatureModelContentProvider"
+           
labelProvider="org.apache.sling.ide.eclipse.ui.nav.GenericModelLabelProvider"
+           icon="icons/obj16/sling_misc.gif">
+          <triggerPoints>
+              <and>
+                  <!-- only trigger for Sling Feature Projects -->
+                  <adapt type="org.eclipse.core.resources.IResource">
+                      <test 
property="org.eclipse.wst.common.project.facet.core.projectFacet" 
value="sling.feature"/>
+                  </adapt>
+                  <or>
+                      <!-- and either IProject or ProvisioningModelRootFolder 
-->
+                      <instanceof value="org.eclipse.core.resources.IProject" 
/>
+                      <instanceof 
value="org.apache.sling.ide.eclipse.ui.nav.model.FeatureModelRootFolder" />
+                  </or>
+              </and>
+          </triggerPoints>
+          <possibleChildren>
+              <and>
+                  <!-- only trigger for Sling Feature Projects -->
+                  <adapt type="org.eclipse.core.resources.IResource">
+                      <test 
property="org.eclipse.wst.common.project.facet.core.projectFacet" 
value="sling.feature"/>
+                  </adapt>         
+                  <instanceof 
+                         value="org.eclipse.core.resources.IResource" /> 
+             </and>
+          </possibleChildren>
+      </navigatorContent>      
    </extension>   
    <extension
          point="org.eclipse.core.runtime.adapters">
@@ -459,6 +490,9 @@
                <test
                                
property="org.eclipse.wst.common.project.facet.core.projectFacet" 
                                value="sling.launchpad"/>
+               <test
+                               
property="org.eclipse.wst.common.project.facet.core.projectFacet" 
+                               value="sling.feature"/>
                  </or>
                        </not>               
          </definition>
diff --git 
a/eclipse/eclipse-ui/src/org/apache/sling/ide/eclipse/ui/nav/FeatureModelContentProvider.java
 
b/eclipse/eclipse-ui/src/org/apache/sling/ide/eclipse/ui/nav/FeatureModelContentProvider.java
new file mode 100644
index 00000000..27e0487c
--- /dev/null
+++ 
b/eclipse/eclipse-ui/src/org/apache/sling/ide/eclipse/ui/nav/FeatureModelContentProvider.java
@@ -0,0 +1,43 @@
+/*
+ * 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.
+ */
+package org.apache.sling.ide.eclipse.ui.nav;
+
+import org.apache.sling.ide.eclipse.core.ProjectUtil;
+import org.apache.sling.ide.eclipse.ui.nav.model.FeatureModelRootFolder;
+import org.eclipse.core.resources.IFolder;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.runtime.IPath;
+
+public class FeatureModelContentProvider extends 
BaseRootFolderContentProvider<FeatureModelRootFolder> {
+
+    public FeatureModelContentProvider() {
+       super(FeatureModelRootFolder.class);
+       }
+    
+    @Override
+    protected FeatureModelRootFolder findRootFolder(IProject project) {
+    
+       IPath modelDirPath = ProjectUtil.getFeatureModelPath(project);
+       
+       IFolder folder = project.getFolder(modelDirPath);
+       if ( !folder.exists() ) {
+               return null;
+       }
+       
+       return new FeatureModelRootFolder(folder);
+    }
+}
diff --git 
a/eclipse/eclipse-ui/src/org/apache/sling/ide/eclipse/ui/nav/ProvisioningModelLabelProvider.java
 
b/eclipse/eclipse-ui/src/org/apache/sling/ide/eclipse/ui/nav/GenericModelLabelProvider.java
similarity index 81%
rename from 
eclipse/eclipse-ui/src/org/apache/sling/ide/eclipse/ui/nav/ProvisioningModelLabelProvider.java
rename to 
eclipse/eclipse-ui/src/org/apache/sling/ide/eclipse/ui/nav/GenericModelLabelProvider.java
index 8d60c855..c8e4ccea 100644
--- 
a/eclipse/eclipse-ui/src/org/apache/sling/ide/eclipse/ui/nav/ProvisioningModelLabelProvider.java
+++ 
b/eclipse/eclipse-ui/src/org/apache/sling/ide/eclipse/ui/nav/GenericModelLabelProvider.java
@@ -16,7 +16,9 @@
  */
 package org.apache.sling.ide.eclipse.ui.nav;
 
+import org.apache.sling.ide.eclipse.ui.nav.model.FeatureModelRootFolder;
 import org.apache.sling.ide.eclipse.ui.nav.model.ProvisioningModelRootFolder;
+import org.apache.sling.ide.eclipse.ui.nav.model.RootFolder;
 import org.eclipse.jface.viewers.ILabelProvider;
 import org.eclipse.jface.viewers.ILabelProviderListener;
 import org.eclipse.swt.graphics.Image;
@@ -26,7 +28,7 @@ import org.eclipse.ui.PlatformUI;
 /**
  * Mostly copied from 
org.eclipse.ui.internal.ide.dialogs.FileFolderSelectionDialog.FileLabelProvider.
  */
-public class ProvisioningModelLabelProvider implements ILabelProvider {
+public class GenericModelLabelProvider implements ILabelProvider {
 
     private static final Image IMG_FOLDER = 
PlatformUI.getWorkbench().getSharedImages()
             .getImage(ISharedImages.IMG_OBJ_FOLDER);
@@ -53,7 +55,7 @@ public class ProvisioningModelLabelProvider implements 
ILabelProvider {
 
     @Override
     public Image getImage(Object element) {
-       if ( element instanceof ProvisioningModelRootFolder ) {
+       if ( element instanceof ProvisioningModelRootFolder || element 
instanceof FeatureModelRootFolder ) {
                return IMG_FOLDER;
        }
        
@@ -62,8 +64,8 @@ public class ProvisioningModelLabelProvider implements 
ILabelProvider {
 
     @Override
     public String getText(Object element) {
-        if (element instanceof ProvisioningModelRootFolder) {
-               ProvisioningModelRootFolder rootFolder = 
(ProvisioningModelRootFolder) element;
+        if (element instanceof ProvisioningModelRootFolder || element 
instanceof FeatureModelRootFolder) {
+               RootFolder rootFolder = (RootFolder) element;
             return rootFolder.getProjectRelativePath().toPortableString();
         }
         return null;
diff --git 
a/eclipse/eclipse-ui/src/org/apache/sling/ide/eclipse/ui/nav/model/FeatureModelRootFolder.java
 
b/eclipse/eclipse-ui/src/org/apache/sling/ide/eclipse/ui/nav/model/FeatureModelRootFolder.java
new file mode 100644
index 00000000..5037de9f
--- /dev/null
+++ 
b/eclipse/eclipse-ui/src/org/apache/sling/ide/eclipse/ui/nav/model/FeatureModelRootFolder.java
@@ -0,0 +1,51 @@
+/*
+ * 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.
+ */
+package org.apache.sling.ide.eclipse.ui.nav.model;
+
+import org.eclipse.core.resources.IFolder;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IPath;
+
+public class FeatureModelRootFolder implements RootFolder {
+
+    private final IFolder folder;
+
+    public FeatureModelRootFolder(IFolder folder) {
+        this.folder = folder;
+    }
+
+    @Override
+    public String toString() {
+        return folder.getProjectRelativePath().toString();
+    }
+    
+    @Override
+    public IResource findMember(IPath path) {
+       return folder.findMember(path);
+    }    
+
+       @Override
+       public IPath getProjectRelativePath() {
+               return folder.getProjectRelativePath();
+       }
+
+       @Override
+       public IResource[] members() throws CoreException {
+               return folder.members();
+       }
+}

Reply via email to