/*
* 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.aries.osgi.functional.internal;
import org.apache.aries.osgi.functional.OSGi;
import org.apache.aries.osgi.functional.OSGiResult;
import org.osgi.framework.BundleContext;
import org.osgi.framework.Filter;
import org.osgi.framework.InvalidSyntaxException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;
/**
* @author Carlos Sierra Andrés
*/
public class OSGiImpl<T> implements OSGi<T> {
public OSGiOperationImpl<T> _operation;
public OSGiImpl(OSGiOperationImpl<T> operation) {
_operation = operation;
}
@Override
public <S> OSGiImpl<S> flatMap(Function<? super T, OSGi<? extends S>> fun) {
return new OSGiImpl<>(
((bundleContext) -> {
Map<Object, OSGiResult<? extends S>> identities =
new IdentityHashMap<>();
AtomicReference<Runnable> closeReference =
new AtomicReference<>(NOOP);
Pipe<Tuple<S>, Tuple<S>> added = Pipe.create();
Consumer<Tuple<S>> addedSource = added.getSource();
OSGiResultImpl<S> osgiResult = new OSGiResultImpl<>(
added, Pipe.create(), null,
() -> {
synchronized (identities) {
identities.values().forEach(OSGiResult::close);
}
closeReference.get().run();
});
osgiResult.start = () -> {
OSGiResultImpl<T> or1 = _operation.run(bundleContext);
closeReference.set(or1.close);
or1.added.map(t -> {
OSGi<? extends S> program = fun.apply(t.t);
OSGiResult<? extends S> or2 = program.run(
bundleContext,
s -> addedSource.accept(Tuple.create(s)));
identities.put(t.original, or2);
return null;
});
or1.removed.map(t -> {
synchronized (identities) {
OSGiResult<? extends S> osgiResult1 =
identities.remove(t.original);
if (osgiResult1 != null) {
osgiResult1.close();
}
}
return null;
});
or1.start.run();
};
return osgiResult;
}
));
}
@Override
public OSGi<Void> foreach(Consumer<? super T> onAdded) {
return foreach(onAdded, ign -> {});
}
@Override
public OSGi<Void> foreach(
Consumer<? super T> onAdded, Consumer<? super T> onRemoved) {
return new OSGiImpl<>(((bundleContext) -> {
OSGiResultImpl<T> osgiResult = _operation.run(bundleContext);
return new OSGiResultImpl<>(
osgiResult.added.map(
t -> t.map(o -> {onAdded.accept(o); return null;})),
osgiResult.removed.map(
t -> t.map(o -> {onRemoved.accept(o); return null;})),
osgiResult.start, osgiResult.close);
}));
}
@Override
public <S> OSGi<S> map(Function<? super T, ? extends S> function) {
return new OSGiImpl<>(((bundleContext) -> {
OSGiResultImpl<T> osgiResult = _operation.run(bundleContext);
return new OSGiResultImpl<>(
osgiResult.added.map(t -> t.map(function)),
osgiResult.removed.map(t -> t.map(function)),
osgiResult.start, osgiResult.close);
}));
}
@Override
public OSGiResult<T> run(BundleContext bundleContext) {
return run(bundleContext, x -> {});
}
@Override
public OSGiResult<T> run(BundleContext bundleContext, Consumer<T> andThen) {
OSGiResultImpl<T> osgiResult = _operation.run(bundleContext);
osgiResult.added.map(x -> {andThen.accept(x.t); return null;});
osgiResult.start.run();
return new OSGiResultImpl<>(
osgiResult.added, osgiResult.removed,
osgiResult.start, osgiResult.close);
}
@Override
public <S> OSGi<S> then(OSGi<S> next) {
return flatMap(ignored -> next);
}
static Filter buildFilter(
BundleContext bundleContext, String filterString, Class<?> clazz) {
Filter filter;
String string = buildFilterString(filterString, clazz);
try {
filter = bundleContext.createFilter(string);
}
catch (InvalidSyntaxException e) {
throw new RuntimeException(e);
}
return filter;
}
static String buildFilterString(String filterString, Class<?> clazz) {
if (filterString == null && clazz == null) {
throw new IllegalArgumentException(
"Both filterString and clazz can't be null");
}
StringBuilder stringBuilder = new StringBuilder();
if (filterString != null) {
stringBuilder.append(filterString);
}
if (clazz != null) {
boolean extend = !(stringBuilder.length() == 0);
if (extend) {
stringBuilder.insert(0, "(&");
}
stringBuilder.
append("(objectClass=").
append(clazz.getName()).
append(")");
if (extend) {
stringBuilder.append(")");
}
}
return stringBuilder.toString();
}
@Override
public OSGi<T> filter(Predicate<T> predicate) {
return flatMap(t -> {
if (predicate.test(t)) {
return OSGi.just(t);
}
else {
return OSGi.nothing();
}
});
}
@Override
@SafeVarargs
final public OSGi<Void> distribute(Function<T, OSGi<?>>... funs) {
return new OSGiImpl<>(bundleContext -> {
ArrayList<OSGiResult> results = new ArrayList<>();
Pipe<Tuple<Void>, Tuple<Void>> added = Pipe.create();
Consumer<Tuple<Void>> addedSource = added.getSource();
return new OSGiResultImpl<>(
added, Pipe.create(),
() -> {
List<OSGiResult> results2 = Arrays.stream(funs).
map(this::flatMap).
map(o -> o.run(bundleContext)).
collect(Collectors.toList());
results.addAll(results2);
addedSource.accept(Tuple.create(null));
},
() -> results.stream().forEach(OSGiResult::close)
);
});
}
}