[447327] Support folder-based "locators"

https://bugs.eclipse.org/bugs/show_bug.cgi?id=447327
diff --git a/plugins/org.eclipse.oomph.setup.targlets/src/org/eclipse/oomph/setup/targlets/impl/TargletTaskImpl.java b/plugins/org.eclipse.oomph.setup.targlets/src/org/eclipse/oomph/setup/targlets/impl/TargletTaskImpl.java
index 82a3e2b..88f674e 100644
--- a/plugins/org.eclipse.oomph.setup.targlets/src/org/eclipse/oomph/setup/targlets/impl/TargletTaskImpl.java
+++ b/plugins/org.eclipse.oomph.setup.targlets/src/org/eclipse/oomph/setup/targlets/impl/TargletTaskImpl.java
@@ -754,7 +754,7 @@
       {
         it.remove();
       }
-      else if (targlet.getRequirements().isEmpty() && targlet.getSourceLocators().isEmpty())
+      else if (targlet.getRequirements().isEmpty() && targlet.getSourceLocators().isEmpty() && targlet.getDropinLocations().isEmpty())
       {
         // Eliminate targlets that are effectively empty, i.e., no requirements, no source locators, and the active repository list is empty.
         String activeRepositoryList = targlet.getActiveRepositoryListName();
diff --git a/plugins/org.eclipse.oomph.targlets.core/src/org/eclipse/oomph/targlets/internal/core/TargletContainer.java b/plugins/org.eclipse.oomph.targlets.core/src/org/eclipse/oomph/targlets/internal/core/TargletContainer.java
index dc51dab..7872046 100644
--- a/plugins/org.eclipse.oomph.targlets.core/src/org/eclipse/oomph/targlets/internal/core/TargletContainer.java
+++ b/plugins/org.eclipse.oomph.targlets.core/src/org/eclipse/oomph/targlets/internal/core/TargletContainer.java
@@ -22,6 +22,7 @@
 import org.eclipse.oomph.p2.core.ProfileTransaction.CommitContext;
 import org.eclipse.oomph.p2.internal.core.CacheUsageConfirmer;
 import org.eclipse.oomph.resources.SourceLocator;
+import org.eclipse.oomph.targlets.DropinLocation;
 import org.eclipse.oomph.targlets.FeatureGenerator;
 import org.eclipse.oomph.targlets.IUGenerator;
 import org.eclipse.oomph.targlets.Targlet;
@@ -139,6 +140,7 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.jar.JarFile;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
@@ -595,6 +597,14 @@
       generateUnits(descriptor, result.toUnmodifiableSet(), bundles, features, progress.newChild(98));
     }
 
+    for (Targlet targlet : targlets)
+    {
+      for (DropinLocation dropinLocation : targlet.getDropinLocations())
+      {
+        analyzeDropinLocation(dropinLocation, bundles, features);
+      }
+    }
+
     fBundles = bundles.toArray(new TargetBundle[bundles.size()]);
     fFeatures = features.toArray(new TargetFeature[features.size()]);
     progress.done();
@@ -631,8 +641,7 @@
       File file = repo.getArtifactFile(it.next());
       if (file != null)
       {
-        TargetBundle bundle = new TargetBundle(file);
-        bundles.add(bundle);
+        addTargetBundle(file, bundles);
       }
     }
   }
@@ -645,12 +654,90 @@
       File file = repo.getArtifactFile(it.next());
       if (file != null)
       {
-        TargetFeature feature = new TargetFeature(file);
-        features.add(feature);
+        addTargetFeature(file, features);
       }
     }
   }
 
+  private void analyzeDropinLocation(DropinLocation dropinLocation, List<TargetBundle> bundles, List<TargetFeature> features)
+  {
+    String rootFolder = dropinLocation.getRootFolder();
+    if (!StringUtil.isEmpty(rootFolder))
+    {
+      File folder = new File(rootFolder);
+      if (folder.isDirectory())
+      {
+        analyzeDropinLocation(folder, bundles, features);
+      }
+    }
+  }
+
+  private void analyzeDropinLocation(File folder, List<TargetBundle> bundles, List<TargetFeature> features)
+  {
+    File[] files = folder.listFiles();
+    if (files != null)
+    {
+      for (File file : files)
+      {
+        JarFile jarFile = null;
+
+        try
+        {
+          if (file.isFile())
+          {
+            if (file.getName().endsWith(".jar"))
+            {
+              jarFile = new JarFile(file);
+              if (jarFile.getJarEntry(FeatureGenerator.FEATURE_XML) != null)
+              {
+                addTargetFeature(file, features);
+              }
+              else if (jarFile.getJarEntry(JarFile.MANIFEST_NAME) != null)
+              {
+                addTargetBundle(file, bundles);
+              }
+            }
+          }
+          else if (file.isDirectory())
+          {
+            if (new File(file, FeatureGenerator.FEATURE_XML).isFile())
+            {
+              addTargetFeature(file, features);
+            }
+            else if (new File(file, JarFile.MANIFEST_NAME).isFile())
+            {
+              addTargetBundle(file, bundles);
+            }
+            else
+            {
+              analyzeDropinLocation(file, bundles, features);
+            }
+          }
+        }
+        catch (Exception ex)
+        {
+          TargletsCorePlugin.INSTANCE.log(ex, IStatus.WARNING);
+        }
+        finally
+        {
+          IOUtil.close(jarFile);
+        }
+      }
+    }
+  }
+
+  private void addTargetBundle(File file, List<TargetBundle> bundles) throws CoreException
+  {
+    TargetBundle bundle = new TargetBundle(file);
+    bundles.add(bundle);
+  }
+
+  private void addTargetFeature(File file, List<TargetFeature> features) throws CoreException
+  {
+    TargetFeature feature = new TargetFeature(file);
+    features.add(feature);
+  }
+
   public void forceUpdate(boolean activateTargetDefinition, boolean mirrors, IProgressMonitor monitor) throws CoreException
   {
     try
diff --git a/plugins/org.eclipse.oomph.targlets.edit/icons/full/obj16/DropinLocation.gif b/plugins/org.eclipse.oomph.targlets.edit/icons/full/obj16/DropinLocation.gif
new file mode 100644
index 0000000..e665a14
--- /dev/null
+++ b/plugins/org.eclipse.oomph.targlets.edit/icons/full/obj16/DropinLocation.gif
Binary files differ
diff --git a/plugins/org.eclipse.oomph.targlets.edit/plugin.properties b/plugins/org.eclipse.oomph.targlets.edit/plugin.properties
index 1d8c082..970d495 100644
--- a/plugins/org.eclipse.oomph.targlets.edit/plugin.properties
+++ b/plugins/org.eclipse.oomph.targlets.edit/plugin.properties
@@ -29,6 +29,8 @@
 _UI_ComponentExtGenerator_type = Component Extension Generator
 _UI_ComponentExtension_requirements_feature = Requirements
 _UI_ComponentExtension_type = Component Extension
+_UI_DropinLocation_rootFolder_feature = Root Folder
+_UI_DropinLocation_type = Dropin Location
 _UI_FeatureGenerator_type = Feature Generator
 _UI_IUGenerator_type = IU Generator
 _UI_InstallableUnitGenerator_type = Installable Unit Generator
@@ -42,10 +44,11 @@
 _UI_Targlet_activeRepositories_feature = Active Repositories
 _UI_Targlet_activeRepositoryListName_feature = Active Repository List Name
 _UI_Targlet_activeRepositoryList_feature = Active Repository List
+_UI_Targlet_dropinLocations_feature = Dropin Locations
 _UI_Targlet_includeAllPlatforms_feature = Include All Platforms
-_UI_Targlet_includeSources_feature = Include Sources
-_UI_Targlet_includeAllRequirements_feature = Include All Requirements
 _UI_Targlet_includeAllRequirements_description = Whether all transitive requirements must be satisfied for target platform resolution to succeed
+_UI_Targlet_includeAllRequirements_feature = Include All Requirements
+_UI_Targlet_includeSources_feature = Include Sources
 _UI_Targlet_installableUnitGenerators_feature = Installable Unit Generators
 _UI_Targlet_name_feature = Name
 _UI_Targlet_repositoryLists_feature = Repository Lists
diff --git a/plugins/org.eclipse.oomph.targlets.edit/src/org/eclipse/oomph/targlets/provider/DropinLocationItemProvider.java b/plugins/org.eclipse.oomph.targlets.edit/src/org/eclipse/oomph/targlets/provider/DropinLocationItemProvider.java
new file mode 100644
index 0000000..01a106a
--- /dev/null
+++ b/plugins/org.eclipse.oomph.targlets.edit/src/org/eclipse/oomph/targlets/provider/DropinLocationItemProvider.java
@@ -0,0 +1,171 @@
+/**
+ */
+package org.eclipse.oomph.targlets.provider;
+
+import org.eclipse.oomph.targlets.DropinLocation;
+import org.eclipse.oomph.targlets.TargletPackage;
+
+import org.eclipse.emf.common.notify.AdapterFactory;
+import org.eclipse.emf.common.notify.Notification;
+import org.eclipse.emf.common.util.ResourceLocator;
+import org.eclipse.emf.edit.provider.ComposeableAdapterFactory;
+import org.eclipse.emf.edit.provider.IChildCreationExtender;
+import org.eclipse.emf.edit.provider.IEditingDomainItemProvider;
+import org.eclipse.emf.edit.provider.IItemLabelProvider;
+import org.eclipse.emf.edit.provider.IItemPropertyDescriptor;
+import org.eclipse.emf.edit.provider.IItemPropertySource;
+import org.eclipse.emf.edit.provider.IStructuredItemContentProvider;
+import org.eclipse.emf.edit.provider.ITreeItemContentProvider;
+import org.eclipse.emf.edit.provider.ItemPropertyDescriptor;
+import org.eclipse.emf.edit.provider.ItemProviderAdapter;
+import org.eclipse.emf.edit.provider.ViewerNotification;
+
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * This is the item provider adapter for a {@link org.eclipse.oomph.targlets.DropinLocation} object.
+ * <!-- begin-user-doc -->
+ * <!-- end-user-doc -->
+ * @generated
+ */
+public class DropinLocationItemProvider extends ItemProviderAdapter
+    implements IEditingDomainItemProvider, IStructuredItemContentProvider, ITreeItemContentProvider, IItemLabelProvider, IItemPropertySource
+{
+  /**
+   * This constructs an instance from a factory and a notifier.
+   * <!-- begin-user-doc -->
+   * <!-- end-user-doc -->
+   * @generated
+   */
+  public DropinLocationItemProvider(AdapterFactory adapterFactory)
+  {
+    super(adapterFactory);
+  }
+
+  /**
+   * This returns the property descriptors for the adapted class.
+   * <!-- begin-user-doc -->
+   * <!-- end-user-doc -->
+   * @generated
+   */
+  @Override
+  public List<IItemPropertyDescriptor> getPropertyDescriptors(Object object)
+  {
+    if (itemPropertyDescriptors == null)
+    {
+      super.getPropertyDescriptors(object);
+
+      addRootFolderPropertyDescriptor(object);
+    }
+    return itemPropertyDescriptors;
+  }
+
+  /**
+   * This adds a property descriptor for the Root Folder feature.
+   * <!-- begin-user-doc -->
+   * <!-- end-user-doc -->
+   * @generated
+   */
+  protected void addRootFolderPropertyDescriptor(Object object)
+  {
+    itemPropertyDescriptors.add(createItemPropertyDescriptor(((ComposeableAdapterFactory)adapterFactory).getRootAdapterFactory(), getResourceLocator(),
+        getString("_UI_DropinLocation_rootFolder_feature"),
+        getString("_UI_PropertyDescriptor_description", "_UI_DropinLocation_rootFolder_feature", "_UI_DropinLocation_type"),
+        TargletPackage.Literals.DROPIN_LOCATION__ROOT_FOLDER, true, false, false, ItemPropertyDescriptor.GENERIC_VALUE_IMAGE, null, null));
+  }
+
+  /**
+   * <!-- begin-user-doc -->
+   * <!-- end-user-doc -->
+   * @generated
+   */
+  @Override
+  public boolean hasChildren(Object object)
+  {
+    return hasChildren(object, true);
+  }
+
+  /**
+   * This returns DropinLocation.gif.
+   * <!-- begin-user-doc -->
+   * <!-- end-user-doc -->
+   * @generated
+   */
+  @Override
+  public Object getImage(Object object)
+  {
+    return overlayImage(object, getResourceLocator().getImage("full/obj16/DropinLocation"));
+  }
+
+  /**
+   * <!-- begin-user-doc -->
+   * <!-- end-user-doc -->
+   * @generated
+   */
+  @Override
+  protected boolean shouldComposeCreationImage()
+  {
+    return true;
+  }
+
+  /**
+   * This returns the label text for the adapted class.
+   * <!-- begin-user-doc -->
+   * <!-- end-user-doc -->
+   * @generated
+   */
+  @Override
+  public String getText(Object object)
+  {
+    String label = ((DropinLocation)object).getRootFolder();
+    return label == null || label.length() == 0 ? getString("_UI_DropinLocation_type") : getString("_UI_DropinLocation_type") + " " + label;
+  }
+
+  /**
+   * This handles model notifications by calling {@link #updateChildren} to update any cached
+   * children and by creating a viewer notification, which it passes to {@link #fireNotifyChanged}.
+   * <!-- begin-user-doc -->
+   * <!-- end-user-doc -->
+   * @generated
+   */
+  @Override
+  public void notifyChanged(Notification notification)
+  {
+    updateChildren(notification);
+
+    switch (notification.getFeatureID(DropinLocation.class))
+    {
+      case TargletPackage.DROPIN_LOCATION__ROOT_FOLDER:
+        fireNotifyChanged(new ViewerNotification(notification, notification.getNotifier(), false, true));
+        return;
+    }
+    super.notifyChanged(notification);
+  }
+
+  /**
+   * This adds {@link org.eclipse.emf.edit.command.CommandParameter}s describing the children
+   * that can be created under this object.
+   * <!-- begin-user-doc -->
+   * <!-- end-user-doc -->
+   * @generated
+   */
+  @Override
+  protected void collectNewChildDescriptors(Collection<Object> newChildDescriptors, Object object)
+  {
+    super.collectNewChildDescriptors(newChildDescriptors, object);
+  }
+
+  /**
+   * Return the resource locator for this item provider's resources.
+   * <!-- begin-user-doc -->
+   * <!-- end-user-doc -->
+   * @generated
+   */
+  @Override
+  public ResourceLocator getResourceLocator()
+  {
+    return ((IChildCreationExtender)adapterFactory).getResourceLocator();
+  }
+
+}
diff --git a/plugins/org.eclipse.oomph.targlets.edit/src/org/eclipse/oomph/targlets/provider/TargletItemProvider.java b/plugins/org.eclipse.oomph.targlets.edit/src/org/eclipse/oomph/targlets/provider/TargletItemProvider.java
index 228f73c..8642d34 100644
--- a/plugins/org.eclipse.oomph.targlets.edit/src/org/eclipse/oomph/targlets/provider/TargletItemProvider.java
+++ b/plugins/org.eclipse.oomph.targlets.edit/src/org/eclipse/oomph/targlets/provider/TargletItemProvider.java
@@ -17,6 +17,7 @@
 import org.eclipse.oomph.p2.provider.RequirementItemProvider;
 import org.eclipse.oomph.resources.ResourcesFactory;
 import org.eclipse.oomph.targlets.Targlet;
+import org.eclipse.oomph.targlets.TargletFactory;
 import org.eclipse.oomph.targlets.TargletPackage;
 
 import org.eclipse.emf.common.notify.AdapterFactory;
@@ -167,6 +168,7 @@
       childrenFeatures.add(TargletPackage.Literals.TARGLET__SOURCE_LOCATORS);
       childrenFeatures.add(TargletPackage.Literals.TARGLET__INSTALLABLE_UNIT_GENERATORS);
       childrenFeatures.add(TargletPackage.Literals.TARGLET__REPOSITORY_LISTS);
+      childrenFeatures.add(TargletPackage.Literals.TARGLET__DROPIN_LOCATIONS);
     }
     return childrenFeatures;
   }
@@ -291,6 +293,7 @@
       case TargletPackage.TARGLET__SOURCE_LOCATORS:
       case TargletPackage.TARGLET__INSTALLABLE_UNIT_GENERATORS:
       case TargletPackage.TARGLET__REPOSITORY_LISTS:
+      case TargletPackage.TARGLET__DROPIN_LOCATIONS:
         fireNotifyChanged(new ViewerNotification(notification, notification.getNotifier(), true, false));
         return;
     }
@@ -314,6 +317,8 @@
     newChildDescriptors.add(createChildParameter(TargletPackage.Literals.TARGLET__SOURCE_LOCATORS, ResourcesFactory.eINSTANCE.createSourceLocator()));
 
     newChildDescriptors.add(createChildParameter(TargletPackage.Literals.TARGLET__REPOSITORY_LISTS, P2Factory.eINSTANCE.createRepositoryList()));
+
+    newChildDescriptors.add(createChildParameter(TargletPackage.Literals.TARGLET__DROPIN_LOCATIONS, TargletFactory.eINSTANCE.createDropinLocation()));
   }
 
 }
diff --git a/plugins/org.eclipse.oomph.targlets.edit/src/org/eclipse/oomph/targlets/provider/TargletItemProviderAdapterFactory.java b/plugins/org.eclipse.oomph.targlets.edit/src/org/eclipse/oomph/targlets/provider/TargletItemProviderAdapterFactory.java
index 1e828d6..8384c42 100644
--- a/plugins/org.eclipse.oomph.targlets.edit/src/org/eclipse/oomph/targlets/provider/TargletItemProviderAdapterFactory.java
+++ b/plugins/org.eclipse.oomph.targlets.edit/src/org/eclipse/oomph/targlets/provider/TargletItemProviderAdapterFactory.java
@@ -496,6 +496,31 @@
   }
 
   /**
+   * This keeps track of the one adapter used for all {@link org.eclipse.oomph.targlets.DropinLocation} instances.
+   * <!-- begin-user-doc -->
+   * <!-- end-user-doc -->
+   * @generated
+   */
+  protected DropinLocationItemProvider dropinLocationItemProvider;
+
+  /**
+   * This creates an adapter for a {@link org.eclipse.oomph.targlets.DropinLocation}.
+   * <!-- begin-user-doc -->
+   * <!-- end-user-doc -->
+   * @generated
+   */
+  @Override
+  public Adapter createDropinLocationAdapter()
+  {
+    if (dropinLocationItemProvider == null)
+    {
+      dropinLocationItemProvider = new DropinLocationItemProvider(this);
+    }
+
+    return dropinLocationItemProvider;
+  }
+
+  /**
    * This returns the root adapter factory that contains this factory.
    * <!-- begin-user-doc -->
    * <!-- end-user-doc -->
@@ -692,6 +717,10 @@
     {
       projectNameGeneratorItemProvider.dispose();
     }
+    if (dropinLocationItemProvider != null)
+    {
+      dropinLocationItemProvider.dispose();
+    }
   }
 
   /**
@@ -774,6 +803,8 @@
 
         newChildDescriptors.add(createChildParameter(BasePackage.Literals.ANNOTATION__CONTENTS, TargletFactory.eINSTANCE.createProjectNameGenerator()));
 
+        newChildDescriptors.add(createChildParameter(BasePackage.Literals.ANNOTATION__CONTENTS, TargletFactory.eINSTANCE.createDropinLocation()));
+
         return null;
       }
 
diff --git a/plugins/org.eclipse.oomph.targlets.editor/src/org/eclipse/oomph/targlets/presentation/ComponentModelWizard.java b/plugins/org.eclipse.oomph.targlets.editor/src/org/eclipse/oomph/targlets/presentation/ComponentModelWizard.java
index a07d47a..0e0740f 100644
--- a/plugins/org.eclipse.oomph.targlets.editor/src/org/eclipse/oomph/targlets/presentation/ComponentModelWizard.java
+++ b/plugins/org.eclipse.oomph.targlets.editor/src/org/eclipse/oomph/targlets/presentation/ComponentModelWizard.java
@@ -12,6 +12,7 @@
 
 import org.eclipse.oomph.base.util.BaseResourceFactoryImpl;
 import org.eclipse.oomph.targlets.ComponentDefinition;
+import org.eclipse.oomph.targlets.FeatureGenerator;
 import org.eclipse.oomph.targlets.TargletFactory;
 
 import org.eclipse.emf.common.util.URI;
@@ -363,7 +364,7 @@
         return getErrorMessage(project, "The selected project appears to be a plugin component.");
       }
 
-      if (project.getFile("feature.xml").exists())
+      if (project.getFile(FeatureGenerator.FEATURE_XML).exists())
       {
         return getErrorMessage(project, "The selected project appears to be a feature component.");
       }
diff --git a/plugins/org.eclipse.oomph.targlets/model/Targlets.ecore b/plugins/org.eclipse.oomph.targlets/model/Targlets.ecore
index a1e257f..afca3e7 100644
--- a/plugins/org.eclipse.oomph.targlets/model/Targlets.ecore
+++ b/plugins/org.eclipse.oomph.targlets/model/Targlets.ecore
@@ -78,6 +78,12 @@
     <eStructuralFeatures xsi:type="ecore:EAttribute" name="includeAllPlatforms" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EBoolean"/>
     <eStructuralFeatures xsi:type="ecore:EAttribute" name="includeAllRequirements"
         eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EBoolean" defaultValueLiteral="true"/>
+    <eStructuralFeatures xsi:type="ecore:EReference" name="dropinLocations" upperBound="-1"
+        eType="#//DropinLocation" containment="true">
+      <eAnnotations source="http:///org/eclipse/emf/ecore/util/ExtendedMetaData">
+        <details key="name" value="dropinLocation"/>
+      </eAnnotations>
+    </eStructuralFeatures>
   </eClassifiers>
   <eClassifiers xsi:type="ecore:EClass" name="ComponentExtension" eSuperTypes="../../org.eclipse.oomph.base/model/Base.ecore#//ModelElement">
     <eStructuralFeatures xsi:type="ecore:EReference" name="requirements" upperBound="-1"
@@ -117,6 +123,10 @@
   <eClassifiers xsi:type="ecore:EClass" name="CategoryGenerator" eSuperTypes="#//SiteGenerator"/>
   <eClassifiers xsi:type="ecore:EClass" name="ProductGenerator" eSuperTypes="#//IUGenerator"/>
   <eClassifiers xsi:type="ecore:EClass" name="ProjectNameGenerator" eSuperTypes="#//IUGenerator"/>
+  <eClassifiers xsi:type="ecore:EClass" name="DropinLocation">
+    <eStructuralFeatures xsi:type="ecore:EAttribute" name="rootFolder" lowerBound="1"
+        eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
+  </eClassifiers>
   <eClassifiers xsi:type="ecore:EDataType" name="InstallableUnit" instanceClassName="org.eclipse.equinox.p2.metadata.IInstallableUnit"
       serializable="false"/>
   <eClassifiers xsi:type="ecore:EDataType" name="StringToVersionMap" instanceTypeName="java.util.Map&lt;java.lang.String, org.eclipse.equinox.p2.metadata.Version>"
diff --git a/plugins/org.eclipse.oomph.targlets/src/org/eclipse/oomph/targlets/DropinLocation.java b/plugins/org.eclipse.oomph.targlets/src/org/eclipse/oomph/targlets/DropinLocation.java
new file mode 100644
index 0000000..5b61d9d
--- /dev/null
+++ b/plugins/org.eclipse.oomph.targlets/src/org/eclipse/oomph/targlets/DropinLocation.java
@@ -0,0 +1,51 @@
+/**
+ */
+package org.eclipse.oomph.targlets;
+
+import org.eclipse.emf.ecore.EObject;
+
+/**
+ * <!-- begin-user-doc -->
+ * A representation of the model object '<em><b>Dropin Location</b></em>'.
+ * <!-- end-user-doc -->
+ *
+ * <p>
+ * The following features are supported:
+ * </p>
+ * <ul>
+ *   <li>{@link org.eclipse.oomph.targlets.DropinLocation#getRootFolder <em>Root Folder</em>}</li>
+ * </ul>
+ *
+ * @see org.eclipse.oomph.targlets.TargletPackage#getDropinLocation()
+ * @model
+ * @generated
+ */
+public interface DropinLocation extends EObject
+{
+  /**
+   * Returns the value of the '<em><b>Root Folder</b></em>' attribute.
+   * <!-- begin-user-doc -->
+   * <p>
+   * If the meaning of the '<em>Root Folder</em>' attribute isn't clear,
+   * there really should be more of a description here...
+   * </p>
+   * <!-- end-user-doc -->
+   * @return the value of the '<em>Root Folder</em>' attribute.
+   * @see #setRootFolder(String)
+   * @see org.eclipse.oomph.targlets.TargletPackage#getDropinLocation_RootFolder()
+   * @model required="true"
+   * @generated
+   */
+  String getRootFolder();
+
+  /**
+   * Sets the value of the '{@link org.eclipse.oomph.targlets.DropinLocation#getRootFolder <em>Root Folder</em>}' attribute.
+   * <!-- begin-user-doc -->
+   * <!-- end-user-doc -->
+   * @param value the new value of the '<em>Root Folder</em>' attribute.
+   * @see #getRootFolder()
+   * @generated
+   */
+  void setRootFolder(String value);
+
+} // DropinLocation
diff --git a/plugins/org.eclipse.oomph.targlets/src/org/eclipse/oomph/targlets/FeatureGenerator.java b/plugins/org.eclipse.oomph.targlets/src/org/eclipse/oomph/targlets/FeatureGenerator.java
index 5931ae1..8545a34 100644
--- a/plugins/org.eclipse.oomph.targlets/src/org/eclipse/oomph/targlets/FeatureGenerator.java
+++ b/plugins/org.eclipse.oomph.targlets/src/org/eclipse/oomph/targlets/FeatureGenerator.java
@@ -22,6 +22,8 @@
  */
 public interface FeatureGenerator extends IUGenerator
 {
+  public static final String FEATURE_XML = "feature.xml";
+
   public static final String PROP_REQUIRED_LICENCSE_FEATURE_ID = "org.eclipse.oomph.targlets.core.requiredLicenseFeatureID";
 
   public static final String PROP_REQUIRED_LICENCSE_FEATURE_VERSION_RANGE = "org.eclipse.oomph.targlets.core.requiredLicenseFeatureVersionRange";
diff --git a/plugins/org.eclipse.oomph.targlets/src/org/eclipse/oomph/targlets/Targlet.java b/plugins/org.eclipse.oomph.targlets/src/org/eclipse/oomph/targlets/Targlet.java
index 7f70736..edc4daa 100644
--- a/plugins/org.eclipse.oomph.targlets/src/org/eclipse/oomph/targlets/Targlet.java
+++ b/plugins/org.eclipse.oomph.targlets/src/org/eclipse/oomph/targlets/Targlet.java
@@ -38,6 +38,7 @@
  *   <li>{@link org.eclipse.oomph.targlets.Targlet#isIncludeSources <em>Include Sources</em>}</li>
  *   <li>{@link org.eclipse.oomph.targlets.Targlet#isIncludeAllPlatforms <em>Include All Platforms</em>}</li>
  *   <li>{@link org.eclipse.oomph.targlets.Targlet#isIncludeAllRequirements <em>Include All Requirements</em>}</li>
+ *   <li>{@link org.eclipse.oomph.targlets.Targlet#getDropinLocations <em>Dropin Locations</em>}</li>
  * </ul>
  *
  * @see org.eclipse.oomph.targlets.TargletPackage#getTarglet()
@@ -124,6 +125,23 @@
   EList<IUGenerator> getInstallableUnitGenerators();
 
   /**
+   * Returns the value of the '<em><b>Dropin Locations</b></em>' containment reference list.
+   * The list contents are of type {@link org.eclipse.oomph.targlets.DropinLocation}.
+   * <!-- begin-user-doc -->
+   * <p>
+   * If the meaning of the '<em>Dropin Locations</em>' containment reference list isn't clear,
+   * there really should be more of a description here...
+   * </p>
+   * <!-- end-user-doc -->
+   * @return the value of the '<em>Dropin Locations</em>' containment reference list.
+   * @see org.eclipse.oomph.targlets.TargletPackage#getTarglet_DropinLocations()
+   * @model containment="true"
+   *        extendedMetaData="name='dropinLocation'"
+   * @generated
+   */
+  EList<DropinLocation> getDropinLocations();
+
+  /**
    * Returns the value of the '<em><b>Repository Lists</b></em>' containment reference list.
    * The list contents are of type {@link org.eclipse.oomph.p2.RepositoryList}.
    * <!-- begin-user-doc -->
diff --git a/plugins/org.eclipse.oomph.targlets/src/org/eclipse/oomph/targlets/TargletFactory.java b/plugins/org.eclipse.oomph.targlets/src/org/eclipse/oomph/targlets/TargletFactory.java
index adbdaf2..1b21413 100644
--- a/plugins/org.eclipse.oomph.targlets/src/org/eclipse/oomph/targlets/TargletFactory.java
+++ b/plugins/org.eclipse.oomph.targlets/src/org/eclipse/oomph/targlets/TargletFactory.java
@@ -166,6 +166,15 @@
   ProjectNameGenerator createProjectNameGenerator();
 
   /**
+   * Returns a new object of class '<em>Dropin Location</em>'.
+   * <!-- begin-user-doc -->
+   * <!-- end-user-doc -->
+   * @return a new object of class '<em>Dropin Location</em>'.
+   * @generated
+   */
+  DropinLocation createDropinLocation();
+
+  /**
    * Returns the package supported by this factory.
    * <!-- begin-user-doc -->
    * <!-- end-user-doc -->
diff --git a/plugins/org.eclipse.oomph.targlets/src/org/eclipse/oomph/targlets/TargletPackage.java b/plugins/org.eclipse.oomph.targlets/src/org/eclipse/oomph/targlets/TargletPackage.java
index 24e616b..991c0e7 100644
--- a/plugins/org.eclipse.oomph.targlets/src/org/eclipse/oomph/targlets/TargletPackage.java
+++ b/plugins/org.eclipse.oomph.targlets/src/org/eclipse/oomph/targlets/TargletPackage.java
@@ -254,13 +254,22 @@
   int TARGLET__INCLUDE_ALL_REQUIREMENTS = BasePackage.MODEL_ELEMENT_FEATURE_COUNT + 10;
 
   /**
+   * The feature id for the '<em><b>Dropin Locations</b></em>' containment reference list.
+   * <!-- begin-user-doc -->
+   * <!-- end-user-doc -->
+   * @generated
+   * @ordered
+   */
+  int TARGLET__DROPIN_LOCATIONS = BasePackage.MODEL_ELEMENT_FEATURE_COUNT + 11;
+
+  /**
    * The number of structural features of the '<em>Targlet</em>' class.
    * <!-- begin-user-doc -->
    * <!-- end-user-doc -->
    * @generated
    * @ordered
    */
-  int TARGLET_FEATURE_COUNT = BasePackage.MODEL_ELEMENT_FEATURE_COUNT + 11;
+  int TARGLET_FEATURE_COUNT = BasePackage.MODEL_ELEMENT_FEATURE_COUNT + 12;
 
   /**
    * The operation id for the '<em>Get Annotation</em>' operation.
@@ -1014,6 +1023,43 @@
   int PROJECT_NAME_GENERATOR_OPERATION_COUNT = IU_GENERATOR_OPERATION_COUNT + 0;
 
   /**
+   * The meta object id for the '{@link org.eclipse.oomph.targlets.impl.DropinLocationImpl <em>Dropin Location</em>}' class.
+   * <!-- begin-user-doc -->
+   * <!-- end-user-doc -->
+   * @see org.eclipse.oomph.targlets.impl.DropinLocationImpl
+   * @see org.eclipse.oomph.targlets.impl.TargletPackageImpl#getDropinLocation()
+   * @generated
+   */
+  int DROPIN_LOCATION = 15;
+
+  /**
+   * The feature id for the '<em><b>Root Folder</b></em>' attribute.
+   * <!-- begin-user-doc -->
+   * <!-- end-user-doc -->
+   * @generated
+   * @ordered
+   */
+  int DROPIN_LOCATION__ROOT_FOLDER = 0;
+
+  /**
+   * The number of structural features of the '<em>Dropin Location</em>' class.
+   * <!-- begin-user-doc -->
+   * <!-- end-user-doc -->
+   * @generated
+   * @ordered
+   */
+  int DROPIN_LOCATION_FEATURE_COUNT = 1;
+
+  /**
+   * The number of operations of the '<em>Dropin Location</em>' class.
+   * <!-- begin-user-doc -->
+   * <!-- end-user-doc -->
+   * @generated
+   * @ordered
+   */
+  int DROPIN_LOCATION_OPERATION_COUNT = 0;
+
+  /**
    * The meta object id for the '<em>Installable Unit</em>' data type.
    * <!-- begin-user-doc -->
    * <!-- end-user-doc -->
@@ -1021,7 +1067,7 @@
    * @see org.eclipse.oomph.targlets.impl.TargletPackageImpl#getInstallableUnit()
    * @generated
    */
-  int INSTALLABLE_UNIT = 15;
+  int INSTALLABLE_UNIT = 16;
 
   /**
    * The meta object id for the '<em>String To Version Map</em>' data type.
@@ -1031,7 +1077,7 @@
    * @see org.eclipse.oomph.targlets.impl.TargletPackageImpl#getStringToVersionMap()
    * @generated
    */
-  int STRING_TO_VERSION_MAP = 16;
+  int STRING_TO_VERSION_MAP = 17;
 
   /**
    * Returns the meta object for class '{@link org.eclipse.oomph.targlets.TargletContainer <em>Container</em>}'.
@@ -1120,6 +1166,17 @@
   EReference getTarglet_InstallableUnitGenerators();
 
   /**
+   * Returns the meta object for the containment reference list '{@link org.eclipse.oomph.targlets.Targlet#getDropinLocations <em>Dropin Locations</em>}'.
+   * <!-- begin-user-doc -->
+   * <!-- end-user-doc -->
+   * @return the meta object for the containment reference list '<em>Dropin Locations</em>'.
+   * @see org.eclipse.oomph.targlets.Targlet#getDropinLocations()
+   * @see #getTarglet()
+   * @generated
+   */
+  EReference getTarglet_DropinLocations();
+
+  /**
    * Returns the meta object for the containment reference list '{@link org.eclipse.oomph.targlets.Targlet#getRepositoryLists <em>Repository Lists</em>}'.
    * <!-- begin-user-doc -->
    * <!-- end-user-doc -->
@@ -1370,6 +1427,27 @@
   EClass getProjectNameGenerator();
 
   /**
+   * Returns the meta object for class '{@link org.eclipse.oomph.targlets.DropinLocation <em>Dropin Location</em>}'.
+   * <!-- begin-user-doc -->
+   * <!-- end-user-doc -->
+   * @return the meta object for class '<em>Dropin Location</em>'.
+   * @see org.eclipse.oomph.targlets.DropinLocation
+   * @generated
+   */
+  EClass getDropinLocation();
+
+  /**
+   * Returns the meta object for the attribute '{@link org.eclipse.oomph.targlets.DropinLocation#getRootFolder <em>Root Folder</em>}'.
+   * <!-- begin-user-doc -->
+   * <!-- end-user-doc -->
+   * @return the meta object for the attribute '<em>Root Folder</em>'.
+   * @see org.eclipse.oomph.targlets.DropinLocation#getRootFolder()
+   * @see #getDropinLocation()
+   * @generated
+   */
+  EAttribute getDropinLocation_RootFolder();
+
+  /**
    * Returns the meta object for data type '{@link org.eclipse.equinox.p2.metadata.IInstallableUnit <em>Installable Unit</em>}'.
    * <!-- begin-user-doc -->
    * <!-- end-user-doc -->
@@ -1484,6 +1562,14 @@
     EReference TARGLET__INSTALLABLE_UNIT_GENERATORS = eINSTANCE.getTarglet_InstallableUnitGenerators();
 
     /**
+     * The meta object literal for the '<em><b>Dropin Locations</b></em>' containment reference list feature.
+     * <!-- begin-user-doc -->
+     * <!-- end-user-doc -->
+     * @generated
+     */
+    EReference TARGLET__DROPIN_LOCATIONS = eINSTANCE.getTarglet_DropinLocations();
+
+    /**
      * The meta object literal for the '<em><b>Repository Lists</b></em>' containment reference list feature.
      * <!-- begin-user-doc -->
      * <!-- end-user-doc -->
@@ -1702,6 +1788,24 @@
     EClass PROJECT_NAME_GENERATOR = eINSTANCE.getProjectNameGenerator();
 
     /**
+     * The meta object literal for the '{@link org.eclipse.oomph.targlets.impl.DropinLocationImpl <em>Dropin Location</em>}' class.
+     * <!-- begin-user-doc -->
+     * <!-- end-user-doc -->
+     * @see org.eclipse.oomph.targlets.impl.DropinLocationImpl
+     * @see org.eclipse.oomph.targlets.impl.TargletPackageImpl#getDropinLocation()
+     * @generated
+     */
+    EClass DROPIN_LOCATION = eINSTANCE.getDropinLocation();
+
+    /**
+     * The meta object literal for the '<em><b>Root Folder</b></em>' attribute feature.
+     * <!-- begin-user-doc -->
+     * <!-- end-user-doc -->
+     * @generated
+     */
+    EAttribute DROPIN_LOCATION__ROOT_FOLDER = eINSTANCE.getDropinLocation_RootFolder();
+
+    /**
      * The meta object literal for the '<em>Installable Unit</em>' data type.
      * <!-- begin-user-doc -->
      * <!-- end-user-doc -->
diff --git a/plugins/org.eclipse.oomph.targlets/src/org/eclipse/oomph/targlets/impl/DropinLocationImpl.java b/plugins/org.eclipse.oomph.targlets/src/org/eclipse/oomph/targlets/impl/DropinLocationImpl.java
new file mode 100644
index 0000000..e356736
--- /dev/null
+++ b/plugins/org.eclipse.oomph.targlets/src/org/eclipse/oomph/targlets/impl/DropinLocationImpl.java
@@ -0,0 +1,180 @@
+/**
+ */
+package org.eclipse.oomph.targlets.impl;
+
+import org.eclipse.oomph.targlets.DropinLocation;
+import org.eclipse.oomph.targlets.TargletPackage;
+
+import org.eclipse.emf.common.notify.Notification;
+import org.eclipse.emf.ecore.EClass;
+import org.eclipse.emf.ecore.impl.ENotificationImpl;
+import org.eclipse.emf.ecore.impl.MinimalEObjectImpl;
+
+/**
+ * <!-- begin-user-doc -->
+ * An implementation of the model object '<em><b>Dropin Location</b></em>'.
+ * <!-- end-user-doc -->
+ * <p>
+ * The following features are implemented:
+ * </p>
+ * <ul>
+ *   <li>{@link org.eclipse.oomph.targlets.impl.DropinLocationImpl#getRootFolder <em>Root Folder</em>}</li>
+ * </ul>
+ *
+ * @generated
+ */
+public class DropinLocationImpl extends MinimalEObjectImpl.Container implements DropinLocation
+{
+  /**
+   * The default value of the '{@link #getRootFolder() <em>Root Folder</em>}' attribute.
+   * <!-- begin-user-doc -->
+   * <!-- end-user-doc -->
+   * @see #getRootFolder()
+   * @generated
+   * @ordered
+   */
+  protected static final String ROOT_FOLDER_EDEFAULT = null;
+
+  /**
+   * The cached value of the '{@link #getRootFolder() <em>Root Folder</em>}' attribute.
+   * <!-- begin-user-doc -->
+   * <!-- end-user-doc -->
+   * @see #getRootFolder()
+   * @generated
+   * @ordered
+   */
+  protected String rootFolder = ROOT_FOLDER_EDEFAULT;
+
+  /**
+   * <!-- begin-user-doc -->
+   * <!-- end-user-doc -->
+   * @generated
+   */
+  protected DropinLocationImpl()
+  {
+    super();
+  }
+
+  /**
+   * <!-- begin-user-doc -->
+   * <!-- end-user-doc -->
+   * @generated
+   */
+  @Override
+  protected EClass eStaticClass()
+  {
+    return TargletPackage.Literals.DROPIN_LOCATION;
+  }
+
+  /**
+   * <!-- begin-user-doc -->
+   * <!-- end-user-doc -->
+   * @generated
+   */
+  public String getRootFolder()
+  {
+    return rootFolder;
+  }
+
+  /**
+   * <!-- begin-user-doc -->
+   * <!-- end-user-doc -->
+   * @generated
+   */
+  public void setRootFolder(String newRootFolder)
+  {
+    String oldRootFolder = rootFolder;
+    rootFolder = newRootFolder;
+    if (eNotificationRequired())
+    {
+      eNotify(new ENotificationImpl(this, Notification.SET, TargletPackage.DROPIN_LOCATION__ROOT_FOLDER, oldRootFolder, rootFolder));
+    }
+  }
+
+  /**
+   * <!-- begin-user-doc -->
+   * <!-- end-user-doc -->
+   * @generated
+   */
+  @Override
+  public Object eGet(int featureID, boolean resolve, boolean coreType)
+  {
+    switch (featureID)
+    {
+      case TargletPackage.DROPIN_LOCATION__ROOT_FOLDER:
+        return getRootFolder();
+    }
+    return super.eGet(featureID, resolve, coreType);
+  }
+
+  /**
+   * <!-- begin-user-doc -->
+   * <!-- end-user-doc -->
+   * @generated
+   */
+  @Override
+  public void eSet(int featureID, Object newValue)
+  {
+    switch (featureID)
+    {
+      case TargletPackage.DROPIN_LOCATION__ROOT_FOLDER:
+        setRootFolder((String)newValue);
+        return;
+    }
+    super.eSet(featureID, newValue);
+  }
+
+  /**
+   * <!-- begin-user-doc -->
+   * <!-- end-user-doc -->
+   * @generated
+   */
+  @Override
+  public void eUnset(int featureID)
+  {
+    switch (featureID)
+    {
+      case TargletPackage.DROPIN_LOCATION__ROOT_FOLDER:
+        setRootFolder(ROOT_FOLDER_EDEFAULT);
+        return;
+    }
+    super.eUnset(featureID);
+  }
+
+  /**
+   * <!-- begin-user-doc -->
+   * <!-- end-user-doc -->
+   * @generated
+   */
+  @Override
+  public boolean eIsSet(int featureID)
+  {
+    switch (featureID)
+    {
+      case TargletPackage.DROPIN_LOCATION__ROOT_FOLDER:
+        return ROOT_FOLDER_EDEFAULT == null ? rootFolder != null : !ROOT_FOLDER_EDEFAULT.equals(rootFolder);
+    }
+    return super.eIsSet(featureID);
+  }
+
+  /**
+   * <!-- begin-user-doc -->
+   * <!-- end-user-doc -->
+   * @generated
+   */
+  @Override
+  public String toString()
+  {
+    if (eIsProxy())
+    {
+      return super.toString();
+    }
+
+    StringBuffer result = new StringBuffer(super.toString());
+    result.append(" (rootFolder: ");
+    result.append(rootFolder);
+    result.append(')');
+    return result.toString();
+  }
+
+} // DropinLocationImpl
diff --git a/plugins/org.eclipse.oomph.targlets/src/org/eclipse/oomph/targlets/impl/FeatureGeneratorImpl.java b/plugins/org.eclipse.oomph.targlets/src/org/eclipse/oomph/targlets/impl/FeatureGeneratorImpl.java
index bcd6604..463d584 100644
--- a/plugins/org.eclipse.oomph.targlets/src/org/eclipse/oomph/targlets/impl/FeatureGeneratorImpl.java
+++ b/plugins/org.eclipse.oomph.targlets/src/org/eclipse/oomph/targlets/impl/FeatureGeneratorImpl.java
@@ -57,7 +57,7 @@
  */
 public class FeatureGeneratorImpl extends ModelElementImpl implements FeatureGenerator
 {
-  private static final IPath MANIFEST_PATH = new Path("feature.xml");
+  private static final IPath MANIFEST_PATH = new Path(FEATURE_XML);
 
   /**
    * <!-- begin-user-doc -->
diff --git a/plugins/org.eclipse.oomph.targlets/src/org/eclipse/oomph/targlets/impl/TargletFactoryImpl.java b/plugins/org.eclipse.oomph.targlets/src/org/eclipse/oomph/targlets/impl/TargletFactoryImpl.java
index b447931..4f1e4cc 100644
--- a/plugins/org.eclipse.oomph.targlets/src/org/eclipse/oomph/targlets/impl/TargletFactoryImpl.java
+++ b/plugins/org.eclipse.oomph.targlets/src/org/eclipse/oomph/targlets/impl/TargletFactoryImpl.java
@@ -21,6 +21,7 @@
 import org.eclipse.oomph.targlets.ComponentDefinition;
 import org.eclipse.oomph.targlets.ComponentExtGenerator;
 import org.eclipse.oomph.targlets.ComponentExtension;
+import org.eclipse.oomph.targlets.DropinLocation;
 import org.eclipse.oomph.targlets.FeatureGenerator;
 import org.eclipse.oomph.targlets.IUGenerator;
 import org.eclipse.oomph.targlets.PluginGenerator;
@@ -124,6 +125,8 @@
         return createProductGenerator();
       case TargletPackage.PROJECT_NAME_GENERATOR:
         return createProjectNameGenerator();
+      case TargletPackage.DROPIN_LOCATION:
+        return createDropinLocation();
       default:
         throw new IllegalArgumentException("The class '" + eClass.getName() + "' is not a valid classifier");
     }
@@ -228,6 +231,11 @@
       targlet.getInstallableUnitGenerators().add(EcoreUtil.copy(iuGenerator));
     }
 
+    for (DropinLocation dropinLocation : source.getDropinLocations())
+    {
+      targlet.getDropinLocations().add(EcoreUtil.copy(dropinLocation));
+    }
+
     return targlet;
   }
 
@@ -379,6 +387,17 @@
    * <!-- end-user-doc -->
    * @generated
    */
+  public DropinLocation createDropinLocation()
+  {
+    DropinLocationImpl dropinLocation = new DropinLocationImpl();
+    return dropinLocation;
+  }
+
+  /**
+   * <!-- begin-user-doc -->
+   * <!-- end-user-doc -->
+   * @generated
+   */
   public TargletPackage getTargletPackage()
   {
     return (TargletPackage)getEPackage();
diff --git a/plugins/org.eclipse.oomph.targlets/src/org/eclipse/oomph/targlets/impl/TargletImpl.java b/plugins/org.eclipse.oomph.targlets/src/org/eclipse/oomph/targlets/impl/TargletImpl.java
index 916d67f..a31847c 100644
--- a/plugins/org.eclipse.oomph.targlets/src/org/eclipse/oomph/targlets/impl/TargletImpl.java
+++ b/plugins/org.eclipse.oomph.targlets/src/org/eclipse/oomph/targlets/impl/TargletImpl.java
@@ -15,6 +15,7 @@
 import org.eclipse.oomph.p2.RepositoryList;
 import org.eclipse.oomph.p2.Requirement;
 import org.eclipse.oomph.resources.SourceLocator;
+import org.eclipse.oomph.targlets.DropinLocation;
 import org.eclipse.oomph.targlets.IUGenerator;
 import org.eclipse.oomph.targlets.Targlet;
 import org.eclipse.oomph.targlets.TargletPackage;
@@ -50,6 +51,7 @@
  *   <li>{@link org.eclipse.oomph.targlets.impl.TargletImpl#isIncludeSources <em>Include Sources</em>}</li>
  *   <li>{@link org.eclipse.oomph.targlets.impl.TargletImpl#isIncludeAllPlatforms <em>Include All Platforms</em>}</li>
  *   <li>{@link org.eclipse.oomph.targlets.impl.TargletImpl#isIncludeAllRequirements <em>Include All Requirements</em>}</li>
+ *   <li>{@link org.eclipse.oomph.targlets.impl.TargletImpl#getDropinLocations <em>Dropin Locations</em>}</li>
  * </ul>
  *
  * @generated
@@ -197,6 +199,16 @@
   protected boolean includeAllRequirements = INCLUDE_ALL_REQUIREMENTS_EDEFAULT;
 
   /**
+   * The cached value of the '{@link #getDropinLocations() <em>Dropin Locations</em>}' containment reference list.
+   * <!-- begin-user-doc -->
+   * <!-- end-user-doc -->
+   * @see #getDropinLocations()
+   * @generated
+   * @ordered
+   */
+  protected EList<DropinLocation> dropinLocations;
+
+  /**
    * <!-- begin-user-doc -->
    * <!-- end-user-doc -->
    * @generated
@@ -289,6 +301,20 @@
    * <!-- end-user-doc -->
    * @generated
    */
+  public EList<DropinLocation> getDropinLocations()
+  {
+    if (dropinLocations == null)
+    {
+      dropinLocations = new EObjectContainmentEList<DropinLocation>(DropinLocation.class, this, TargletPackage.TARGLET__DROPIN_LOCATIONS);
+    }
+    return dropinLocations;
+  }
+
+  /**
+   * <!-- begin-user-doc -->
+   * <!-- end-user-doc -->
+   * @generated
+   */
   public EList<RepositoryList> getRepositoryLists()
   {
     if (repositoryLists == null)
@@ -464,6 +490,8 @@
         return ((InternalEList<?>)getInstallableUnitGenerators()).basicRemove(otherEnd, msgs);
       case TargletPackage.TARGLET__REPOSITORY_LISTS:
         return ((InternalEList<?>)getRepositoryLists()).basicRemove(otherEnd, msgs);
+      case TargletPackage.TARGLET__DROPIN_LOCATIONS:
+        return ((InternalEList<?>)getDropinLocations()).basicRemove(otherEnd, msgs);
     }
     return super.eInverseRemove(otherEnd, featureID, msgs);
   }
@@ -500,6 +528,8 @@
         return isIncludeAllPlatforms();
       case TargletPackage.TARGLET__INCLUDE_ALL_REQUIREMENTS:
         return isIncludeAllRequirements();
+      case TargletPackage.TARGLET__DROPIN_LOCATIONS:
+        return getDropinLocations();
     }
     return super.eGet(featureID, resolve, coreType);
   }
@@ -546,6 +576,10 @@
       case TargletPackage.TARGLET__INCLUDE_ALL_REQUIREMENTS:
         setIncludeAllRequirements((Boolean)newValue);
         return;
+      case TargletPackage.TARGLET__DROPIN_LOCATIONS:
+        getDropinLocations().clear();
+        getDropinLocations().addAll((Collection<? extends DropinLocation>)newValue);
+        return;
     }
     super.eSet(featureID, newValue);
   }
@@ -587,6 +621,9 @@
       case TargletPackage.TARGLET__INCLUDE_ALL_REQUIREMENTS:
         setIncludeAllRequirements(INCLUDE_ALL_REQUIREMENTS_EDEFAULT);
         return;
+      case TargletPackage.TARGLET__DROPIN_LOCATIONS:
+        getDropinLocations().clear();
+        return;
     }
     super.eUnset(featureID);
   }
@@ -624,6 +661,8 @@
         return includeAllPlatforms != INCLUDE_ALL_PLATFORMS_EDEFAULT;
       case TargletPackage.TARGLET__INCLUDE_ALL_REQUIREMENTS:
         return includeAllRequirements != INCLUDE_ALL_REQUIREMENTS_EDEFAULT;
+      case TargletPackage.TARGLET__DROPIN_LOCATIONS:
+        return dropinLocations != null && !dropinLocations.isEmpty();
     }
     return super.eIsSet(featureID);
   }
diff --git a/plugins/org.eclipse.oomph.targlets/src/org/eclipse/oomph/targlets/impl/TargletPackageImpl.java b/plugins/org.eclipse.oomph.targlets/src/org/eclipse/oomph/targlets/impl/TargletPackageImpl.java
index 0f9fc43..2480c81 100644
--- a/plugins/org.eclipse.oomph.targlets/src/org/eclipse/oomph/targlets/impl/TargletPackageImpl.java
+++ b/plugins/org.eclipse.oomph.targlets/src/org/eclipse/oomph/targlets/impl/TargletPackageImpl.java
@@ -21,6 +21,7 @@
 import org.eclipse.oomph.targlets.ComponentDefinition;
 import org.eclipse.oomph.targlets.ComponentExtGenerator;
 import org.eclipse.oomph.targlets.ComponentExtension;
+import org.eclipse.oomph.targlets.DropinLocation;
 import org.eclipse.oomph.targlets.FeatureGenerator;
 import org.eclipse.oomph.targlets.IUGenerator;
 import org.eclipse.oomph.targlets.PluginGenerator;
@@ -162,6 +163,13 @@
    * <!-- end-user-doc -->
    * @generated
    */
+  private EClass dropinLocationEClass = null;
+
+  /**
+   * <!-- begin-user-doc -->
+   * <!-- end-user-doc -->
+   * @generated
+   */
   private EDataType installableUnitEDataType = null;
 
   /**
@@ -326,6 +334,16 @@
    * <!-- end-user-doc -->
    * @generated
    */
+  public EReference getTarglet_DropinLocations()
+  {
+    return (EReference)targletEClass.getEStructuralFeatures().get(11);
+  }
+
+  /**
+   * <!-- begin-user-doc -->
+   * <!-- end-user-doc -->
+   * @generated
+   */
   public EReference getTarglet_RepositoryLists()
   {
     return (EReference)targletEClass.getEStructuralFeatures().get(4);
@@ -566,6 +584,26 @@
    * <!-- end-user-doc -->
    * @generated
    */
+  public EClass getDropinLocation()
+  {
+    return dropinLocationEClass;
+  }
+
+  /**
+   * <!-- begin-user-doc -->
+   * <!-- end-user-doc -->
+   * @generated
+   */
+  public EAttribute getDropinLocation_RootFolder()
+  {
+    return (EAttribute)dropinLocationEClass.getEStructuralFeatures().get(0);
+  }
+
+  /**
+   * <!-- begin-user-doc -->
+   * <!-- end-user-doc -->
+   * @generated
+   */
   public EDataType getInstallableUnit()
   {
     return installableUnitEDataType;
@@ -630,6 +668,7 @@
     createEAttribute(targletEClass, TARGLET__INCLUDE_SOURCES);
     createEAttribute(targletEClass, TARGLET__INCLUDE_ALL_PLATFORMS);
     createEAttribute(targletEClass, TARGLET__INCLUDE_ALL_REQUIREMENTS);
+    createEReference(targletEClass, TARGLET__DROPIN_LOCATIONS);
 
     componentExtensionEClass = createEClass(COMPONENT_EXTENSION);
     createEReference(componentExtensionEClass, COMPONENT_EXTENSION__REQUIREMENTS);
@@ -661,6 +700,9 @@
 
     projectNameGeneratorEClass = createEClass(PROJECT_NAME_GENERATOR);
 
+    dropinLocationEClass = createEClass(DROPIN_LOCATION);
+    createEAttribute(dropinLocationEClass, DROPIN_LOCATION__ROOT_FOLDER);
+
     // Create data types
     installableUnitEDataType = createEDataType(INSTALLABLE_UNIT);
     stringToVersionMapEDataType = createEDataType(STRING_TO_VERSION_MAP);
@@ -750,6 +792,8 @@
         IS_CHANGEABLE, !IS_UNSETTABLE, !IS_ID, IS_UNIQUE, !IS_DERIVED, IS_ORDERED);
     initEAttribute(getTarglet_IncludeAllRequirements(), ecorePackage.getEBoolean(), "includeAllRequirements", "true", 0, 1, Targlet.class, !IS_TRANSIENT,
         !IS_VOLATILE, IS_CHANGEABLE, !IS_UNSETTABLE, !IS_ID, IS_UNIQUE, !IS_DERIVED, IS_ORDERED);
+    initEReference(getTarglet_DropinLocations(), getDropinLocation(), null, "dropinLocations", null, 0, -1, Targlet.class, !IS_TRANSIENT, !IS_VOLATILE,
+        IS_CHANGEABLE, IS_COMPOSITE, !IS_RESOLVE_PROXIES, !IS_UNSETTABLE, IS_UNIQUE, !IS_DERIVED, IS_ORDERED);
 
     initEClass(componentExtensionEClass, ComponentExtension.class, "ComponentExtension", !IS_ABSTRACT, !IS_INTERFACE, IS_GENERATED_INSTANCE_CLASS);
     initEReference(getComponentExtension_Requirements(), theP2Package.getRequirement(), null, "requirements", null, 0, -1, ComponentExtension.class,
@@ -790,6 +834,10 @@
 
     initEClass(projectNameGeneratorEClass, ProjectNameGenerator.class, "ProjectNameGenerator", !IS_ABSTRACT, !IS_INTERFACE, IS_GENERATED_INSTANCE_CLASS);
 
+    initEClass(dropinLocationEClass, DropinLocation.class, "DropinLocation", !IS_ABSTRACT, !IS_INTERFACE, IS_GENERATED_INSTANCE_CLASS);
+    initEAttribute(getDropinLocation_RootFolder(), ecorePackage.getEString(), "rootFolder", null, 1, 1, DropinLocation.class, !IS_TRANSIENT, !IS_VOLATILE,
+        IS_CHANGEABLE, !IS_UNSETTABLE, !IS_ID, IS_UNIQUE, !IS_DERIVED, IS_ORDERED);
+
     // Initialize data types
     initEDataType(installableUnitEDataType, IInstallableUnit.class, "InstallableUnit", !IS_SERIALIZABLE, !IS_GENERATED_INSTANCE_CLASS);
     initEDataType(stringToVersionMapEDataType, Map.class, "StringToVersionMap", !IS_SERIALIZABLE, !IS_GENERATED_INSTANCE_CLASS,
@@ -852,6 +900,7 @@
     addAnnotation(getTarglet_ActiveRepositoryListName(), source, new String[] { "kind", "attribute", "name", "activeRepositoryList" });
     addAnnotation(getTarglet_ActiveRepositoryList(), source, new String[] { "name", "activeRepository" });
     addAnnotation(getTarglet_ActiveRepositories(), source, new String[] { "name", "activeRepository" });
+    addAnnotation(getTarglet_DropinLocations(), source, new String[] { "name", "dropinLocation" });
     addAnnotation(getComponentExtension_Requirements(), source, new String[] { "name", "requirement" });
     addAnnotation(getComponentDefinition_ID(), source, new String[] { "kind", "attribute", "name", "id" });
   }
diff --git a/plugins/org.eclipse.oomph.targlets/src/org/eclipse/oomph/targlets/util/TargletAdapterFactory.java b/plugins/org.eclipse.oomph.targlets/src/org/eclipse/oomph/targlets/util/TargletAdapterFactory.java
index b628a1f..9a13ef0 100644
--- a/plugins/org.eclipse.oomph.targlets/src/org/eclipse/oomph/targlets/util/TargletAdapterFactory.java
+++ b/plugins/org.eclipse.oomph.targlets/src/org/eclipse/oomph/targlets/util/TargletAdapterFactory.java
@@ -18,6 +18,7 @@
 import org.eclipse.oomph.targlets.ComponentDefinition;
 import org.eclipse.oomph.targlets.ComponentExtGenerator;
 import org.eclipse.oomph.targlets.ComponentExtension;
+import org.eclipse.oomph.targlets.DropinLocation;
 import org.eclipse.oomph.targlets.FeatureGenerator;
 import org.eclipse.oomph.targlets.IUGenerator;
 import org.eclipse.oomph.targlets.PluginGenerator;
@@ -186,6 +187,12 @@
     }
 
     @Override
+    public Adapter caseDropinLocation(DropinLocation object)
+    {
+      return createDropinLocationAdapter();
+    }
+
+    @Override
     public Adapter caseModelElement(ModelElement object)
     {
       return createModelElementAdapter();
@@ -438,6 +445,21 @@
   }
 
   /**
+   * Creates a new adapter for an object of class '{@link org.eclipse.oomph.targlets.DropinLocation <em>Dropin Location</em>}'.
+   * <!-- begin-user-doc -->
+   * This default implementation returns null so that we can easily ignore cases;
+   * it's useful to ignore a case when inheritance will catch all the cases anyway.
+   * <!-- end-user-doc -->
+   * @return the new adapter.
+   * @see org.eclipse.oomph.targlets.DropinLocation
+   * @generated
+   */
+  public Adapter createDropinLocationAdapter()
+  {
+    return null;
+  }
+
+  /**
    * Creates a new adapter for an object of class '{@link org.eclipse.oomph.base.ModelElement <em>Model Element</em>}'.
    * <!-- begin-user-doc -->
    * This default implementation returns null so that we can easily ignore cases;
diff --git a/plugins/org.eclipse.oomph.targlets/src/org/eclipse/oomph/targlets/util/TargletSwitch.java b/plugins/org.eclipse.oomph.targlets/src/org/eclipse/oomph/targlets/util/TargletSwitch.java
index 0acba98..84315bf 100644
--- a/plugins/org.eclipse.oomph.targlets/src/org/eclipse/oomph/targlets/util/TargletSwitch.java
+++ b/plugins/org.eclipse.oomph.targlets/src/org/eclipse/oomph/targlets/util/TargletSwitch.java
@@ -18,6 +18,7 @@
 import org.eclipse.oomph.targlets.ComponentDefinition;
 import org.eclipse.oomph.targlets.ComponentExtGenerator;
 import org.eclipse.oomph.targlets.ComponentExtension;
+import org.eclipse.oomph.targlets.DropinLocation;
 import org.eclipse.oomph.targlets.FeatureGenerator;
 import org.eclipse.oomph.targlets.IUGenerator;
 import org.eclipse.oomph.targlets.PluginGenerator;
@@ -353,6 +354,16 @@
         }
         return result;
       }
+      case TargletPackage.DROPIN_LOCATION:
+      {
+        DropinLocation dropinLocation = (DropinLocation)theEObject;
+        T result = caseDropinLocation(dropinLocation);
+        if (result == null)
+        {
+          result = defaultCase(theEObject);
+        }
+        return result;
+      }
       default:
         return defaultCase(theEObject);
     }
@@ -599,6 +610,22 @@
   }
 
   /**
+   * Returns the result of interpreting the object as an instance of '<em>Dropin Location</em>'.
+   * <!-- begin-user-doc -->
+   * This implementation returns null;
+   * returning a non-null result will terminate the switch.
+   * <!-- end-user-doc -->
+   * @param object the target of the switch.
+   * @return the result of interpreting the object as an instance of '<em>Dropin Location</em>'.
+   * @see #doSwitch(org.eclipse.emf.ecore.EObject) doSwitch(EObject)
+   * @generated
+   */
+  public T caseDropinLocation(DropinLocation object)
+  {
+    return null;
+  }
+
+  /**
    * Returns the result of interpreting the object as an instance of '<em>Model Element</em>'.
    * <!-- begin-user-doc -->
    * This implementation returns null;
diff --git a/setups/models/Targlets.ecore b/setups/models/Targlets.ecore
index 1874b9f..ceb6816 100644
--- a/setups/models/Targlets.ecore
+++ b/setups/models/Targlets.ecore
@@ -77,6 +77,12 @@
     <eStructuralFeatures xsi:type="ecore:EAttribute" name="includeAllPlatforms" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EBoolean"/>
     <eStructuralFeatures xsi:type="ecore:EAttribute" name="includeAllRequirements"
         eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EBoolean" defaultValueLiteral="true"/>
+    <eStructuralFeatures xsi:type="ecore:EReference" name="dropinLocations" upperBound="-1"
+        eType="#//DropinLocation" containment="true">
+      <eAnnotations source="http:///org/eclipse/emf/ecore/util/ExtendedMetaData">
+        <details key="name" value="dropinLocation"/>
+      </eAnnotations>
+    </eStructuralFeatures>
   </eClassifiers>
   <eClassifiers xsi:type="ecore:EClass" name="ComponentExtension" eSuperTypes="http://www.eclipse.org/oomph/base/1.0#//ModelElement">
     <eStructuralFeatures xsi:type="ecore:EReference" name="requirements" upperBound="-1"
@@ -115,6 +121,10 @@
   <eClassifiers xsi:type="ecore:EClass" name="CategoryGenerator" eSuperTypes="#//SiteGenerator"/>
   <eClassifiers xsi:type="ecore:EClass" name="ProductGenerator" eSuperTypes="#//IUGenerator"/>
   <eClassifiers xsi:type="ecore:EClass" name="ProjectNameGenerator" eSuperTypes="#//IUGenerator"/>
+  <eClassifiers xsi:type="ecore:EClass" name="DropinLocation">
+    <eStructuralFeatures xsi:type="ecore:EAttribute" name="rootFolder" lowerBound="1"
+        eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
+  </eClassifiers>
   <eClassifiers xsi:type="ecore:EDataType" name="InstallableUnit" instanceClassName="org.eclipse.equinox.p2.metadata.IInstallableUnit"
       serializable="false"/>
   <eClassifiers xsi:type="ecore:EDataType" name="StringToVersionMap" instanceTypeName="java.util.Map&lt;java.lang.String, org.eclipse.equinox.p2.metadata.Version>"