Repository: karaf
Updated Branches:
  refs/heads/master f40c636b3 -> b8818c0bd


Move more code into FeatureReq

Project: http://git-wip-us.apache.org/repos/asf/karaf/repo
Commit: http://git-wip-us.apache.org/repos/asf/karaf/commit/b8818c0b
Tree: http://git-wip-us.apache.org/repos/asf/karaf/tree/b8818c0b
Diff: http://git-wip-us.apache.org/repos/asf/karaf/diff/b8818c0b

Branch: refs/heads/master
Commit: b8818c0bd2282a2df0c2cb689b10b939639fd48e
Parents: f40c636
Author: Guillaume Nodet <[email protected]>
Authored: Mon Aug 28 17:31:19 2017 +0200
Committer: Guillaume Nodet <[email protected]>
Committed: Mon Aug 28 17:31:19 2017 +0200

----------------------------------------------------------------------
 .../features/internal/service/Deployer.java     |   7 +-
 .../features/internal/service/FeatureReq.java   | 135 ++++++++++++----
 .../internal/service/FeaturesServiceImpl.java   | 155 ++++---------------
 .../karaf/features/internal/util/MapUtils.java  |  24 ++-
 4 files changed, 164 insertions(+), 157 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/karaf/blob/b8818c0b/features/core/src/main/java/org/apache/karaf/features/internal/service/Deployer.java
----------------------------------------------------------------------
diff --git 
a/features/core/src/main/java/org/apache/karaf/features/internal/service/Deployer.java
 
b/features/core/src/main/java/org/apache/karaf/features/internal/service/Deployer.java
index 5f7b105..f17f1e6 100644
--- 
a/features/core/src/main/java/org/apache/karaf/features/internal/service/Deployer.java
+++ 
b/features/core/src/main/java/org/apache/karaf/features/internal/service/Deployer.java
@@ -36,6 +36,7 @@ import java.util.Set;
 import java.util.SortedMap;
 import java.util.TreeMap;
 import java.util.TreeSet;
+import java.util.function.Function;
 
 import org.apache.felix.utils.version.VersionRange;
 import org.apache.felix.utils.version.VersionTable;
@@ -1322,15 +1323,15 @@ public class Deployer {
         return result;
     }
 
-    protected <T> MapUtils.Function<Bundle, T> adapt(final Class<T> clazz) {
+    protected <T> Function<Bundle, T> adapt(final Class<T> clazz) {
         return bundle -> bundle.adapt(clazz);
     }
 
-    protected MapUtils.Function<Bundle, Long> bundleId() {
+    protected Function<Bundle, Long> bundleId() {
         return Bundle::getBundleId;
     }
 
-    protected MapUtils.Function<Resource, String> featureId() {
+    protected Function<Resource, String> featureId() {
         return ResourceUtils::getFeatureId;
     }
 

http://git-wip-us.apache.org/repos/asf/karaf/blob/b8818c0b/features/core/src/main/java/org/apache/karaf/features/internal/service/FeatureReq.java
----------------------------------------------------------------------
diff --git 
a/features/core/src/main/java/org/apache/karaf/features/internal/service/FeatureReq.java
 
b/features/core/src/main/java/org/apache/karaf/features/internal/service/FeatureReq.java
index 1bac816..a573642 100644
--- 
a/features/core/src/main/java/org/apache/karaf/features/internal/service/FeatureReq.java
+++ 
b/features/core/src/main/java/org/apache/karaf/features/internal/service/FeatureReq.java
@@ -16,10 +16,24 @@
  */
 package org.apache.karaf.features.internal.service;
 
+import org.apache.felix.utils.version.VersionTable;
 import org.apache.karaf.features.Feature;
 import org.osgi.framework.Version;
 import org.osgi.framework.VersionRange;
 
+import java.util.Collection;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.Set;
+import java.util.function.Function;
+import java.util.function.Predicate;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import static org.apache.karaf.features.internal.util.MapUtils.filter;
+
 /**
  * Requirement for a feature
  * 
@@ -33,12 +47,27 @@ import org.osgi.framework.VersionRange;
  * - range: Like defined in OSGi VersionRange. Example: [1.0.0, 1.1.0)  
  */
 public class FeatureReq {
+
     public static final String VERSION_SEPARATOR = "/";
-    private static Version HIGHEST = new Version(Integer.MAX_VALUE, 
Integer.MAX_VALUE, Integer.MAX_VALUE);
-    private static final VersionRange RANGE_ALL = new 
VersionRange(VersionRange.LEFT_CLOSED, Version.emptyVersion, HIGHEST, 
VersionRange.RIGHT_CLOSED);
+
+    private static final VersionRange RANGE_ALL = new 
VersionRange(VersionRange.LEFT_CLOSED, Version.emptyVersion, null, 
VersionRange.RIGHT_OPEN);
+    private static final String FEATURE_OSGI_REQUIREMENT_PREFIX = "feature:";
+
     private String name;
     private VersionRange versionRange;
-    
+
+    public static FeatureReq parseRequirement(String featureReq) {
+        if (!featureReq.startsWith(FEATURE_OSGI_REQUIREMENT_PREFIX)) {
+            return null;
+        }
+        String featureReq1 = 
featureReq.substring(FEATURE_OSGI_REQUIREMENT_PREFIX.length());
+        return parseNameAndRange(featureReq1);
+    }
+
+    public static FeatureReq parseNameAndRange(String nameAndRange) {
+        return new FeatureReq(nameAndRange);
+    }
+
     public FeatureReq(String nameAndRange) {
         String[] parts = nameAndRange.trim().split(VERSION_SEPARATOR);
         this.name = parts[0];
@@ -50,28 +79,6 @@ public class FeatureReq {
         this.versionRange = range(versionRange);
     }
     
-    private VersionRange range(String versionRange) {
-        if (versionRange == null) {
-            return RANGE_ALL;
-        }
-        versionRange = versionRange.trim();
-        if ("0.0.0".equals(versionRange)) {
-            return RANGE_ALL;
-        }
-        if (versionRange.contains(",")) {
-            return new VersionRange(versionRange);
-        } else {
-            return exactVersion(versionRange);
-        }
-    }
-
-    private static VersionRange exactVersion(String versionRange) {
-        return new VersionRange(VersionRange.LEFT_CLOSED, 
-                                new Version(versionRange), 
-                                new Version(versionRange), 
-                                VersionRange.RIGHT_CLOSED);
-    }
-
     public FeatureReq(String name, VersionRange versionRange) {
         this.name = name;
         this.versionRange = versionRange;
@@ -88,9 +95,85 @@ public class FeatureReq {
     public VersionRange getVersionRange() {
         return versionRange;
     }
-    
+
+    public Set<FeatureReq> getMatchingRequirements(Set<FeatureReq> reqs) {
+        Pattern pattern = Pattern.compile(name);
+        // TODO: should we use the intersection of the 2 ranges ?
+        return filter(reqs, fr -> pattern.matcher(fr.getName()).matches()
+                                && 
versionRange.includes(fr.getVersionRange().getLeft()));
+    }
+
+    public Stream<Feature> getMatchingFeatures(Map<String, Map<String, 
Feature>> allFeatures) {
+        Pattern pattern = Pattern.compile(name);
+        Function<String, Optional<Feature>> func = featureName -> {
+            Feature matchingFeature = null;
+            if (pattern.matcher(featureName).matches()) {
+                Map<String, Feature> versions = allFeatures.get(featureName);
+                matchingFeature = getLatestFeature(versions, versionRange);
+            }
+            return Optional.ofNullable(matchingFeature);
+        };
+        return 
allFeatures.keySet().stream().map(func).filter(Optional::isPresent).map(Optional::get);
+    }
+
+    private static Feature getLatestFeature(Map<String, Feature> versions, 
VersionRange versionRange) {
+        Feature feature = null;
+        if (versions != null && !versions.isEmpty()) {
+            Version latest = Version.emptyVersion;
+            for (String available : versions.keySet()) {
+                Version availableVersion = VersionTable.getVersion(available);
+                if (availableVersion.compareTo(latest) >= 0 && 
versionRange.includes(availableVersion)) {
+                    feature = versions.get(available);
+                    latest = availableVersion;
+                }
+            }
+        }
+        return feature;
+    }
+
     @Override
     public String toString() {
         return this.name + "/" + this.getVersionRange().toString();
     }
+
+    public String toRequirement() {
+        return FEATURE_OSGI_REQUIREMENT_PREFIX + toString();
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        FeatureReq that = (FeatureReq) o;
+        return Objects.equals(name, that.name) &&
+               Objects.equals(versionRange, that.versionRange);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(name, versionRange);
+    }
+
+    private static VersionRange range(String versionRange) {
+        if (versionRange == null) {
+            return RANGE_ALL;
+        }
+        versionRange = versionRange.trim();
+        if ("0.0.0".equals(versionRange)) {
+            return RANGE_ALL;
+        }
+        if (versionRange.contains(",")) {
+            return new VersionRange(versionRange);
+        } else {
+            return exactVersion(versionRange);
+        }
+    }
+
+    private static VersionRange exactVersion(String versionRange) {
+        return new VersionRange(VersionRange.LEFT_CLOSED,
+                new Version(versionRange),
+                new Version(versionRange),
+                VersionRange.RIGHT_CLOSED);
+    }
+
 }

http://git-wip-us.apache.org/repos/asf/karaf/blob/b8818c0b/features/core/src/main/java/org/apache/karaf/features/internal/service/FeaturesServiceImpl.java
----------------------------------------------------------------------
diff --git 
a/features/core/src/main/java/org/apache/karaf/features/internal/service/FeaturesServiceImpl.java
 
b/features/core/src/main/java/org/apache/karaf/features/internal/service/FeaturesServiceImpl.java
index 574998a..05177fb 100644
--- 
a/features/core/src/main/java/org/apache/karaf/features/internal/service/FeaturesServiceImpl.java
+++ 
b/features/core/src/main/java/org/apache/karaf/features/internal/service/FeaturesServiceImpl.java
@@ -43,13 +43,10 @@ import java.util.concurrent.Executors;
 import java.util.concurrent.ScheduledThreadPoolExecutor;
 import java.util.function.Function;
 import java.util.function.Predicate;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
 
 import org.apache.felix.utils.version.VersionCleaner;
-import org.apache.felix.utils.version.VersionTable;
 import org.apache.karaf.features.DeploymentEvent;
 import org.apache.karaf.features.DeploymentListener;
 import org.apache.karaf.features.Feature;
@@ -73,8 +70,6 @@ import org.ops4j.pax.url.mvn.MavenResolvers;
 import org.osgi.framework.Bundle;
 import org.osgi.framework.BundleException;
 import org.osgi.framework.InvalidSyntaxException;
-import org.osgi.framework.Version;
-import org.osgi.framework.VersionRange;
 import org.osgi.resource.Resource;
 import org.osgi.resource.Wire;
 import org.osgi.service.cm.Configuration;
@@ -86,9 +81,7 @@ import org.slf4j.LoggerFactory;
 import static java.util.Collections.emptyMap;
 import static java.util.stream.Collectors.toSet;
 import static 
org.apache.karaf.features.internal.service.StateStorage.toStringStringSetMap;
-import static org.apache.karaf.features.internal.util.MapUtils.add;
-import static org.apache.karaf.features.internal.util.MapUtils.copy;
-import static org.apache.karaf.features.internal.util.MapUtils.remove;
+import static org.apache.karaf.features.internal.util.MapUtils.*;
 
 /**
  *
@@ -97,7 +90,6 @@ public class FeaturesServiceImpl implements FeaturesService, 
Deployer.DeployCall
 
     private static final String RESOLVE_FILE = "resolve";
     private static final Logger LOGGER = 
LoggerFactory.getLogger(FeaturesServiceImpl.class);
-    private static final String FEATURE_OSGI_REQUIREMENT_PREFIX = "feature:";
     private static final String VERSION_SEPARATOR = "/";
 
     /**
@@ -432,7 +424,7 @@ public class FeaturesServiceImpl implements 
FeaturesService, Deployer.DeployCall
             return Stream.of(repo.getFeatures())
                     .filter(this::isRequired)
                     .map(Feature::getId)
-                    .collect(Collectors.toSet());
+                    .collect(toSet());
         }
     }
 
@@ -520,60 +512,17 @@ public class FeaturesServiceImpl implements 
FeaturesService, Deployer.DeployCall
 
     @Override
     public Feature[] getFeatures(String nameOrId) throws Exception {
-        return toArray(getFeatures(new FeatureReq(nameOrId)));
+        return getFeatures(FeatureReq.parseNameAndRange(nameOrId));
     }
 
     @Override
     public Feature[] getFeatures(String name, String version) throws Exception 
{
-        return toArray(getFeatures(new FeatureReq(name, version)));
+        return getFeatures(new FeatureReq(name, version));
     }
     
-    private Collection<Feature> getFeatures(FeatureReq featureReq) throws 
Exception {
-        List<Feature> features = new ArrayList<>();
-        Pattern pattern = Pattern.compile(featureReq.getName());
+    private Feature[] getFeatures(FeatureReq featureReq) throws Exception {
         Map<String, Map<String, Feature>> allFeatures = getFeatureCache();
-        for (String featureName : allFeatures.keySet()) {
-            Matcher matcher = pattern.matcher(featureName);
-            if (matcher.matches()) {
-                Feature matchingFeature = getFeatureMatching(featureName, 
featureReq.getVersionRange());
-                if (matchingFeature != null) {
-                    features.add(matchingFeature);
-                }
-            }
-        }
-        return features;
-    }
-    
-    private Feature[] toArray(Collection<Feature> features) {
-        return features.toArray(new Feature[features.size()]);
-    }
-
-    private Feature getFeatureMatching(String featureName, VersionRange 
version) throws Exception {
-        Map<String, Map<String, Feature>> allFeatures = getFeatureCache();
-        Map<String, Feature> versions = allFeatures.get(featureName);
-        if (versions == null || versions.isEmpty()) {
-            return null;
-        }
-        return getLatestFeature(versions, version);
-    }
-
-    private Feature getLatestFeature(Map<String, Feature> versions, 
VersionRange versionRange) {
-        Version latest = Version.emptyVersion;
-        Feature feature = null;
-        for (String available : versions.keySet()) {
-            Version availableVersion = VersionTable.getVersion(available);
-            if (availableVersion.compareTo(latest) >= 0 && 
versionRange.includes(availableVersion)) {
-                feature = versions.get(available);
-                latest = availableVersion;
-            }
-        }
-        return feature;
-    }
-
-    @Override
-    public Feature[] listFeatures() throws Exception {
-        Map<String, Map<String, Feature>> allFeatures = getFeatureCache();
-        return flattenFeatures(allFeatures);
+        return 
featureReq.getMatchingFeatures(allFeatures).toArray(Feature[]::new);
     }
     
     private void ensureCacheLoaded() throws Exception {
@@ -654,6 +603,12 @@ public class FeaturesServiceImpl implements 
FeaturesService, Deployer.DeployCall
     //
 
     @Override
+    public Feature[] listFeatures() throws Exception {
+        Map<String, Map<String, Feature>> allFeatures = getFeatureCache();
+        return flattenFeatures(allFeatures, f -> true);
+    }
+
+    @Override
     public Feature[] listInstalledFeatures() throws Exception {
         Map<String, Map<String, Feature>> allFeatures = getFeatureCache();
         synchronized (lock) {
@@ -669,10 +624,6 @@ public class FeaturesServiceImpl implements 
FeaturesService, Deployer.DeployCall
         }
     }
 
-    private Feature[] flattenFeatures(Map<String, Map<String, Feature>> 
features) {
-        return flattenFeatures(features, f -> true /* include all */);
-    }
-
     private Feature[] flattenFeatures(Map<String, Map<String, Feature>> 
features, Predicate<Feature> pred) {
         return features.values().stream()
                 .map(Map::values)
@@ -706,7 +657,7 @@ public class FeaturesServiceImpl implements 
FeaturesService, Deployer.DeployCall
 
     @Override
     public boolean isRequired(Feature f) {
-        String id = FEATURE_OSGI_REQUIREMENT_PREFIX + new 
FeatureReq(f).toString();
+        String id = new FeatureReq(f).toRequirement();
         synchronized (lock) {
             Set<String> features = state.requirements.get(ROOT_REGION);
             return features != null && features.contains(id);
@@ -793,25 +744,22 @@ public class FeaturesServiceImpl implements 
FeaturesService, Deployer.DeployCall
 
     @Override
     public void installFeatures(Set<String> featuresIn, String region, 
EnumSet<Option> options) throws Exception {
-        Set<FeatureReq> toInstall = new HashSet<>();
-        for (String feature : featuresIn) {
-            toInstall.add(new FeatureReq(feature));
-        }
+        Set<FeatureReq> toInstall = map(featuresIn, 
FeatureReq::parseNameAndRange);
         State state = copyState();
         Map<String, Set<String>> requires = copy(state.requirements);
         if (region == null || region.isEmpty()) {
             region = ROOT_REGION;
         }
         Set<String> requirements = requires.computeIfAbsent(region, k -> new 
HashSet<>());
-        Set<FeatureReq> existingFeatures = requirements.stream().map(r -> 
toFeatureReq(r)).collect(toSet());
+        Set<FeatureReq> existingFeatures = map(requirements, 
FeatureReq::parseRequirement);
 
         Set<FeatureReq> toAdd = computeFeaturesToAdd(options, toInstall);
-        toAdd.stream().forEach(f -> requirements.add(toRequirement(f)));
+        toAdd.forEach(f -> requirements.add(f.toRequirement()));
         print("Adding features: " + join(toAdd), 
options.contains(Option.Verbose));
         
         if (options.contains(Option.Upgrade)) {
             Set<FeatureReq> toRemove = computeFeaturesToRemoveOnUpdate(toAdd, 
existingFeatures);
-            toRemove.stream().forEach(f -> 
requirements.remove(toRequirement(f)));
+            toRemove.forEach(f -> requirements.remove(f.toRequirement()));
             if (!toRemove.isEmpty()) {
                 print("Removing features: " + join(toRemove), 
options.contains(Option.Verbose));
             }
@@ -822,17 +770,14 @@ public class FeaturesServiceImpl implements 
FeaturesService, Deployer.DeployCall
     
     private Set<FeatureReq> computeFeaturesToAdd(EnumSet<Option> options, 
                                                  Set<FeatureReq> toInstall) 
throws Exception {
+        Map<String, Map<String, Feature>> allFeatures = getFeatureCache();
         Feature[] installedFeatures = listInstalledFeatures();
         Set<FeatureReq> toAdd = new HashSet<>();
         for (FeatureReq featureReq : toInstall) {
-            Collection<Feature> matching = getFeatures(featureReq);
-            for (Feature f: matching) {
+            Collection<Feature> matching = 
featureReq.getMatchingFeatures(allFeatures).collect(toSet());
+            for (Feature f : matching) {
                 toAdd.add(new FeatureReq(f));
-                for (Feature installedFeature : installedFeatures) {
-                    if (isSameFeature(f, installedFeature)) {
-                        logInstalledOrUpdated(f);
-                    }
-                }
+                Arrays.stream(installedFeatures).filter(fi -> isSameFeature(f, 
fi)).forEach(this::logInstalledOrUpdated);
             }
             if (matching.isEmpty() && 
!options.contains(Option.NoFailOnFeatureNotFound)) {
                 throw new IllegalArgumentException("No matching features for " 
+ featureReq);
@@ -851,67 +796,37 @@ public class FeaturesServiceImpl implements 
FeaturesService, Deployer.DeployCall
     }
 
     private Set<FeatureReq> computeFeaturesToRemoveOnUpdate(Set<FeatureReq> 
featuresToAdd,
-                                             Set<FeatureReq> existingFeatures) 
throws Exception {
-        Set<String> namesToAdd = featuresToAdd.stream().map(f -> 
f.getName()).collect(toSet());
-        return existingFeatures.stream()
-            .filter(f -> namesToAdd.contains(f.getName()) && 
!featuresToAdd.contains(f))
-            .collect(toSet());
-    }
-
-    private String toRequirement(FeatureReq feature) {
-        return FEATURE_OSGI_REQUIREMENT_PREFIX + feature.toString();
+                                                            Set<FeatureReq> 
existingFeatures) throws Exception {
+        Set<String> namedToAdd = map(featuresToAdd, FeatureReq::getName);
+        return filter(existingFeatures, f -> namedToAdd.contains(f.getName()) 
&& !featuresToAdd.contains(f));
     }
 
     @Override
     public void uninstallFeatures(Set<String> featuresIn, String region, 
EnumSet<Option> options) throws Exception {
-        Set<FeatureReq> featureReqs = new HashSet<>();
-        for (String feature : featuresIn) {
-            featureReqs.add(new FeatureReq(feature));
-        }
+        Set<FeatureReq> featureReqs = map(featuresIn, 
FeatureReq::parseNameAndRange);
         State state = copyState();
         Map<String, Set<String>> required = copy(state.requirements);
         if (region == null || region.isEmpty()) {
             region = ROOT_REGION;
         }
         Set<String> requirements = required.computeIfAbsent(region, k -> new 
HashSet<>());
-        Set<FeatureReq> existingFeatures = requirements.stream().map(r -> 
toFeatureReq(r)).collect(toSet());
+        Set<FeatureReq> existingFeatures = map(requirements, 
FeatureReq::parseRequirement);
         Set<FeatureReq> featuresToRemove = new HashSet<>();
-        for (FeatureReq feature : featureReqs) {
-            List<FeatureReq> toRemove = getMatching(existingFeatures, feature);
+        for (FeatureReq featureReq : featureReqs) {
+            Collection<FeatureReq> toRemove = 
featureReq.getMatchingRequirements(existingFeatures);
             if (toRemove.isEmpty()) {
-                throw new IllegalArgumentException("Feature named '" + feature 
+ "' is not installed");
+                throw new IllegalArgumentException("Feature named '" + 
featureReq + "' is not installed");
             }
             featuresToRemove.addAll(toRemove);
         }
         print("Removing features: " + join(featuresToRemove), 
options.contains(Option.Verbose));
-        featuresToRemove.stream().forEach(f -> 
requirements.remove(toRequirement(f)));
+        featuresToRemove.forEach(f -> requirements.remove(f.toRequirement()));
         if (requirements.isEmpty()) {
             required.remove(region);
         }
         doProvisionInThread(required, emptyMap(), state, getFeaturesById(), 
options);
     }
 
-    private List<FeatureReq> getMatching(Set<FeatureReq> existingFeatures, 
FeatureReq feature) {
-        Pattern pattern = Pattern.compile(feature.getName());
-        List<FeatureReq> matching = new ArrayList<>();
-        for (FeatureReq existingFeatureReq : existingFeatures) {
-            Matcher matcher = pattern.matcher(existingFeatureReq.getName());
-            Version existingVersion = 
existingFeatureReq.getVersionRange().getLeft();  
-            if (matcher.matches() && 
feature.getVersionRange().includes(existingVersion)) {
-                matching.add(existingFeatureReq);
-            }
-        }
-        return matching;
-    }
-
-    private FeatureReq toFeatureReq(String featureReq) {
-        if (!featureReq.startsWith(FEATURE_OSGI_REQUIREMENT_PREFIX)) {
-            return null;
-        }
-        String featureReq1 = 
featureReq.substring(FEATURE_OSGI_REQUIREMENT_PREFIX.length());
-        return new FeatureReq(featureReq1);
-    }
-
     @Override
     public void updateFeaturesState(Map<String, Map<String, FeatureState>> 
stateChanges, EnumSet<Option> options) throws Exception {
         State state = copyState();
@@ -939,7 +854,7 @@ public class FeaturesServiceImpl implements 
FeaturesService, Deployer.DeployCall
         State stateCopy;
         synchronized (lock) {
             // Remove repo
-            Set<String> reps = 
repos.stream().map(URI::toString).collect(Collectors.toSet());
+            Set<String> reps = map(repos, URI::toString);
             Set<String> toRemove = diff(state.repositories, reps);
             Set<String> toAdd = diff(reps, state.repositories);
             state.repositories.removeAll(toRemove);
@@ -957,12 +872,6 @@ public class FeaturesServiceImpl implements 
FeaturesService, Deployer.DeployCall
         doProvisionInThread(requirements, emptyMap(), stateCopy, 
getFeaturesById(), options);
     }
 
-    private <T> Set<T> diff(Set<T> s1, Set<T> s2) {
-        Set<T> s = new HashSet<>(s1);
-        s.removeAll(s2);
-        return s;
-    }
-
     @Override
     public Repository createRepository(URI uri) throws Exception {
         return repositories.create(uri, true);
@@ -1194,6 +1103,6 @@ public class FeaturesServiceImpl implements 
FeaturesService, Deployer.DeployCall
     }
 
     private String join(Collection<FeatureReq> reqs) {
-        return 
reqs.stream().map(f->f.toString()).collect(Collectors.joining(","));
+        return 
reqs.stream().map(FeatureReq::toString).collect(Collectors.joining(","));
     }
 }

http://git-wip-us.apache.org/repos/asf/karaf/blob/b8818c0b/features/core/src/main/java/org/apache/karaf/features/internal/util/MapUtils.java
----------------------------------------------------------------------
diff --git 
a/features/core/src/main/java/org/apache/karaf/features/internal/util/MapUtils.java
 
b/features/core/src/main/java/org/apache/karaf/features/internal/util/MapUtils.java
index 32607e4..92128f0 100644
--- 
a/features/core/src/main/java/org/apache/karaf/features/internal/util/MapUtils.java
+++ 
b/features/core/src/main/java/org/apache/karaf/features/internal/util/MapUtils.java
@@ -24,16 +24,16 @@ import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.function.Function;
+import java.util.function.Predicate;
+
+import static java.util.stream.Collectors.toSet;
 
 public final class MapUtils {
 
     private MapUtils() {
     }
 
-    public interface Function<T, U> {
-        U apply(T t);
-    }
-
     public static <S, T> Map<S, Set<T>> invert(Map<T, S> map) {
         Map<S, Set<T>> inverted = new HashMap<>(map.size());
         for (Map.Entry<T, S> entry : map.entrySet()) {
@@ -77,7 +77,7 @@ public final class MapUtils {
         return s -> f2.apply(f1.apply(s));
     }
 
-    public static <T, U> MapUtils.Function<T, U> map(final Map<T, U> map) {
+    public static <T, U> Function<T, U> map(final Map<T, U> map) {
         return map::get;
     }
 
@@ -199,4 +199,18 @@ public final class MapUtils {
         }
     }
 
+    public static <T> Set<T> diff(Set<T> s1, Set<T> s2) {
+        Set<T> s = new HashSet<>(s1);
+        s.removeAll(s2);
+        return s;
+    }
+
+    public static <S, T> Set<T> map(Set<S> s, Function<S, T> mapper) {
+        return s.stream().map(mapper).collect(toSet());
+    }
+
+    public static <S> Set<S> filter(Set<S> s, Predicate<S> predicate) {
+        return s.stream().filter(predicate).collect(toSet());
+    }
+
 }

Reply via email to