diff options
author | 2018-04-12 15:46:31 +0300 | |
---|---|---|
committer | 2018-04-12 15:49:38 +0300 | |
commit | 5032434b101f25fa44d2e1f8dc8393e30af1ed4f (patch) | |
tree | 2dc7d37a8048e025c7412af080640da4c9a22b65 /dcaedt_catalog/commons/src | |
parent | 2205633792f95f46a02bbf8f87f0c2637265d924 (diff) |
DCAE-D be initial commit
DCAE-D be initial commit
Issue-ID: SDC-1218
Change-Id: Id18ba96c499e785aa9ac395fbaf32d57f08c281b
Signed-off-by: Stone, Avi (as206k) <as206k@att.com>
Diffstat (limited to 'dcaedt_catalog/commons/src')
15 files changed, 1664 insertions, 0 deletions
diff --git a/dcaedt_catalog/commons/src/main/java/org/onap/sdc/dcae/catalog/commons/Action.java b/dcaedt_catalog/commons/src/main/java/org/onap/sdc/dcae/catalog/commons/Action.java new file mode 100644 index 0000000..fb36950 --- /dev/null +++ b/dcaedt_catalog/commons/src/main/java/org/onap/sdc/dcae/catalog/commons/Action.java @@ -0,0 +1,11 @@ +package org.onap.sdc.dcae.catalog.commons; + +import org.onap.sdc.dcae.catalog.commons.Future; + +/** + */ +public interface Action<T> { + + public Future<T> execute(); + +} diff --git a/dcaedt_catalog/commons/src/main/java/org/onap/sdc/dcae/catalog/commons/Actions.java b/dcaedt_catalog/commons/src/main/java/org/onap/sdc/dcae/catalog/commons/Actions.java new file mode 100644 index 0000000..132b0c0 --- /dev/null +++ b/dcaedt_catalog/commons/src/main/java/org/onap/sdc/dcae/catalog/commons/Actions.java @@ -0,0 +1,201 @@ +package org.onap.sdc.dcae.catalog.commons; + +import java.util.List; +import java.util.LinkedList; +import java.util.ArrayList; +import java.util.Collections; +import java.util.concurrent.CountDownLatch; + +import org.onap.sdc.common.onaplog.OnapLoggerDebug; +import org.onap.sdc.common.onaplog.OnapLoggerError; +import org.onap.sdc.common.onaplog.Enums.LogLevel; +import org.onap.sdc.dcae.catalog.commons.Action; +import org.onap.sdc.dcae.catalog.commons.Future; +import org.onap.sdc.dcae.catalog.commons.FutureHandler; +import org.onap.sdc.dcae.catalog.commons.Futures; + +/** + */ +public interface Actions { + + /** */ + public static interface CompoundAction<T> extends Action<List<T>> { + + public CompoundAction<T> addAction(Action<T> theAction); + + public List<Action<T>> actions(); + + public Future<List<T>> execute(); + } + + + public static class BasicCompoundAction<T> implements CompoundAction<T> { + + private LinkedList<Action<T>> actions = new LinkedList<Action<T>>(); + + + + public CompoundAction<T> addAction(Action<T> theAction) { + this.actions.add(theAction); + return this; + } + + public List<Action<T>> actions() { + return this.actions; + } + + public Future<List<T>> execute() { + CompoundFuture<T> cf = new CompoundFuture<T>(this.actions.size()); + for (Action a: this.actions) + cf.addFuture(a.execute()); + return cf; + } + } + + + public static class CompoundFuture<T> extends Futures.BasicFuture<List<T>> { + + private static OnapLoggerError errLogger = OnapLoggerError.getInstance(); + private static OnapLoggerDebug debugLogger = OnapLoggerDebug.getInstance(); + + private LinkedList<Future<T>> futures = new LinkedList<Future<T>>(); + private FutureHandler<T> hnd; + + CompoundFuture(int theActionCount) { + + hnd = new Futures.BasicHandler<T>(new CountDownLatch(theActionCount)) { + + private List<T> results = new ArrayList<T>(Collections.nCopies(theActionCount, null)); + + protected void process(Future<T> theResult) { + synchronized(CompoundFuture.this) { + if (theResult.failed()) { + CompoundFuture.this.cause(theResult.cause()); + //and stop processing of other results + this.results = null; + //?? + } + else { + if (this.results != null) + this.results.set(futures.indexOf(theResult), theResult.result()); + debugLogger.log(LogLevel.DEBUG, this.getClass().getName(), "Got result for action {}. Count at {}", futures.indexOf(theResult), this.latch.getCount()); + } + if (this.latch.getCount() == 1) {//this was the last result + debugLogger.log(LogLevel.DEBUG, this.getClass().getName(), "Got all results: {}", this.results); + CompoundFuture.this.result(this.results); + } + } + } + }; + } + + CompoundFuture<T> addFuture(Future<T> theFuture) { + synchronized(this) { + futures.add(theFuture); + theFuture.setHandler(this.hnd); + } + return this; + } + + } + +/* + public static class CompoundFutureHandler<T> implements FutureHandler<T> { + + protected List<T> result = null; + protected List<Throwable> error = null; + protected CountDownLatch latch = null; + + CompoundFutureHandler(int theResultCount) { + this(new CountDownLatch(theResultCount)); + } + + public void handle(Future<T> theResult) { + if (this.latch != null) { + this.latch.countDown(); + } + } + + public T result() + throws InterruptedException, RuntimeException { + return result(true); + } + + public BasicHandler<T> waitForCompletion() throws InterruptedException { + this.latch.await(); + return this; + } + + } +*/ + + public static class Sequence<T> implements Action<List<T>> { + + private static OnapLoggerError errLogger = OnapLoggerError.getInstance(); + private static OnapLoggerDebug debugLogger = OnapLoggerDebug.getInstance(); + + private List<Action<T>> actions = new LinkedList<Action<T>>(); + private int current = 0; + private SequenceFuture<T> future = new SequenceFuture<T>(); + + public Sequence<T> add(Action<T> theAction) { + if (this.current > 0) + throw new IllegalStateException("In execution"); + this.actions.add(theAction); + return this; + } + + /* we allow 'early' access to the future so that a client can pass its reference while + * it still builds the sequence, for example. + */ + public Future<List<T>> future() { + return this.future; + } + + //need to add protection when for the 'no action' case + public Future<List<T>> execute() { + debugLogger.log(LogLevel.DEBUG, this.getClass().getName(), "Starting serialized execution of {}", actions); + if (hasNext()) + next().execute().setHandler(future.hnd); + return this.future; + } + + protected boolean hasNext() { + return this.current < actions.size(); + } + + protected Action next() { + return actions.get(this.current++); + } + + private class SequenceFuture<T> extends Futures.BasicFuture<List<T>> { + + private List<T> results = new LinkedList<T>(); + private FutureHandler<T> hnd = new Futures.BasicHandler<T>() { + + protected void process(Future<T> theResult) { + + if (theResult.failed()) { + SequenceFuture.this.cause(theResult.cause()); + //and stop processing of other results + } + else { + SequenceFuture.this.results.add(theResult.result()); + if (Sequence.this.hasNext()) { + Sequence.this.next().execute().setHandler(this); + } + else { + SequenceFuture.this.result(SequenceFuture.this.results); + } + } + } + }; + + + } + + + + } + +} diff --git a/dcaedt_catalog/commons/src/main/java/org/onap/sdc/dcae/catalog/commons/Future.java b/dcaedt_catalog/commons/src/main/java/org/onap/sdc/dcae/catalog/commons/Future.java new file mode 100644 index 0000000..c50f467 --- /dev/null +++ b/dcaedt_catalog/commons/src/main/java/org/onap/sdc/dcae/catalog/commons/Future.java @@ -0,0 +1,35 @@ +package org.onap.sdc.dcae.catalog.commons; + +import org.onap.sdc.dcae.catalog.commons.Future; +import org.onap.sdc.dcae.catalog.commons.FutureHandler; + +/** + * Modeled after the vertx future + */ +public interface Future<T> { + + public T result(); + + public Future<T> result(T theResult); + +//rename 'cause' to 'failure' + + public Throwable cause(); + + public Future<T> cause(Throwable theError); + + public boolean succeeded(); + + public boolean failed(); + + public boolean complete(); + + public T waitForResult() throws Exception; + + //public T waitForResult(long theTimeout) throws Exception; + + public Future<T> waitForCompletion() throws InterruptedException; + + public Future<T> setHandler(FutureHandler<T> theHandler); + +} diff --git a/dcaedt_catalog/commons/src/main/java/org/onap/sdc/dcae/catalog/commons/FutureHandler.java b/dcaedt_catalog/commons/src/main/java/org/onap/sdc/dcae/catalog/commons/FutureHandler.java new file mode 100644 index 0000000..b689412 --- /dev/null +++ b/dcaedt_catalog/commons/src/main/java/org/onap/sdc/dcae/catalog/commons/FutureHandler.java @@ -0,0 +1,13 @@ +package org.onap.sdc.dcae.catalog.commons; + +import org.onap.sdc.dcae.catalog.commons.Future; + +/** + * Modeled after the vertx future + */ +@FunctionalInterface +public interface FutureHandler<T> { + + public void handle(Future<T> theResult); + +} diff --git a/dcaedt_catalog/commons/src/main/java/org/onap/sdc/dcae/catalog/commons/Futures.java b/dcaedt_catalog/commons/src/main/java/org/onap/sdc/dcae/catalog/commons/Futures.java new file mode 100644 index 0000000..ffaf42b --- /dev/null +++ b/dcaedt_catalog/commons/src/main/java/org/onap/sdc/dcae/catalog/commons/Futures.java @@ -0,0 +1,257 @@ +package org.onap.sdc.dcae.catalog.commons; + +import java.util.List; +import java.util.LinkedList; +import java.util.Collections; + +import java.util.concurrent.CountDownLatch; +import java.util.function.Function; + +import org.onap.sdc.common.onaplog.OnapLoggerDebug; +import org.onap.sdc.common.onaplog.OnapLoggerError; +import org.onap.sdc.dcae.catalog.commons.Future; +import org.onap.sdc.dcae.catalog.commons.FutureHandler; +import org.onap.sdc.common.onaplog.Enums.LogLevel; + + +/** + */ +public class Futures<T> { + + private Futures() { + } + + + public static <T> Future<T> failedFuture(Throwable theError) { + return new BasicFuture<T>() + .cause(theError); + } + + public static <T> Future<T> succeededFuture(T theResult) { + return new BasicFuture<T>() + .result(theResult); + } + + public static <T> Future<T> future() { + return new BasicFuture<T>(); + } + + public static <U,V> Future<V> advance(Future<U> theStep, + final Function<U,V> theResultFunction) { + return advance(theStep, theResultFunction, Function.identity()); + } + + public static <U,V> Future<V> advance(Future<U> theStep, + final Function<U,V> theResultFunction, + final Function<Throwable, Throwable> theErrorFunction) { + final Future<V> adv = new BasicFuture<V>(); + theStep.setHandler(new FutureHandler<U>() { + public void handle(Future<U> theResult) { + if (theResult.failed()) + adv.cause(theErrorFunction.apply(theResult.cause())); + else + adv.result(theResultFunction.apply(theResult.result())); + } + }); + return adv; + } + + /** */ + public static class BasicFuture<T> implements Future<T> { + + protected boolean succeeded, + failed; + + protected FutureHandler<T> handler; + protected Throwable cause; + protected T result; + + + protected BasicFuture() { + } + + public T result() { + return this.result; + } + + public Future<T> result(T theResult) { + this.result = theResult; + this.succeeded = true; + this.cause = null; + this.failed = false; + callHandler(); + return this; + } + + public Throwable cause() { + return this.cause; + } + + public Future<T> cause(Throwable theCause) { + this.cause = theCause; + this.failed = true; + this.result = null; + this.succeeded = false; + callHandler(); + return this; + } + + public boolean succeeded() { + return this.succeeded; + } + + public boolean failed() { + return this.failed; + } + + public boolean complete() { + return this.failed || this.succeeded; + } + + public Future<T> setHandler(FutureHandler<T> theHandler) { + this.handler = theHandler; + callHandler(); + return this; + } + + public T waitForResult() throws Exception { + BasicHandler<T> hnd = buildHandler(); + setHandler(hnd); + hnd.waitForCompletion(); + if (failed()) + throw (Exception)cause(); + else + return result(); + } + + public Future<T> waitForCompletion() throws InterruptedException { + BasicHandler<T> hnd = buildHandler(); + setHandler(hnd); + hnd.waitForCompletion(); + return this; + } + + protected void callHandler() { + if (this.handler != null && complete()) { + this.handler.handle(this); + } + } + + protected BasicHandler<T> buildHandler() { + return new BasicHandler<T>(); + } + } + + + /** */ + public static class BasicHandler<T> + implements FutureHandler<T> { + + protected T result = null; + protected Throwable error = null; + protected CountDownLatch latch = null; + + BasicHandler() { + this(new CountDownLatch(1)); + } + + BasicHandler(CountDownLatch theLatch) { + this.latch = theLatch; + } + + public void handle(Future<T> theResult) { + process(theResult); + if (this.latch != null) { + this.latch.countDown(); + } + } + + protected void process(Future<T> theResult) { + if (theResult.failed()) { + this.error = theResult.cause(); + } + else { + this.result = theResult.result(); + } + } + + public T result(boolean doWait) + throws InterruptedException, RuntimeException { + if (doWait) { + waitForCompletion(); + } + if (null == this.error) + return this.result; + + throw new RuntimeException(this.error); + } + + public T result() + throws InterruptedException, RuntimeException { + return result(true); + } + + public BasicHandler<T> waitForCompletion() throws InterruptedException { + this.latch.await(); + return this; + } + } + + /** */ + public static class Accumulator<T> extends BasicFuture<List<T>> + implements Future<List<T>> { + + protected List<Future<T>> futures = new LinkedList<Future<T>>(); + //protected List<T> results = new LinkedList<T>(); + protected BasicHandler<T> handler = null; + + private static OnapLoggerError errLogger = OnapLoggerError.getInstance(); + private static OnapLoggerDebug debugLogger = OnapLoggerDebug.getInstance(); + + public Accumulator() { + this.result = new LinkedList<T>(); + } + + public Accumulator<T> add(Future<T> theFuture) { + debugLogger.log(LogLevel.DEBUG, this.getClass().getName(), "Intersection add"); + this.futures.add(theFuture); + this.result.add(null); + return this; + } + + public Accumulator<T> addAll(Accumulator<T> theFutures) { + + debugLogger.log(LogLevel.DEBUG, this.getClass().getName(), "Intersection addAll"); + + return this; + } + + public Future<List<T>> accumulate() { + this.futures = Collections.unmodifiableList(this.futures); + this.handler = new BasicHandler<T>(new CountDownLatch(this.futures.size())) { + protected void process(Future<T> theResult) { + if (theResult.failed()) { + Accumulator.this.cause = theResult.cause(); + } + else { + Accumulator.this.result.set( + Accumulator.this.futures.indexOf(theResult), theResult.result()); + } + if (this.latch.getCount() == 1) { + if (Accumulator.this.cause != null) + Accumulator.this.cause(Accumulator.this.cause); + else + Accumulator.this.result(Accumulator.this.result); + } + } + }; + futures.stream() + .forEach(f -> f.setHandler(this.handler)); + + return this; + } + + } + + +} diff --git a/dcaedt_catalog/commons/src/main/java/org/onap/sdc/dcae/catalog/commons/Http.java b/dcaedt_catalog/commons/src/main/java/org/onap/sdc/dcae/catalog/commons/Http.java new file mode 100644 index 0000000..0f28495 --- /dev/null +++ b/dcaedt_catalog/commons/src/main/java/org/onap/sdc/dcae/catalog/commons/Http.java @@ -0,0 +1,107 @@ +package org.onap.sdc.dcae.catalog.commons; + +import java.util.List; + +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpMethod; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.http.client.SimpleClientHttpRequestFactory; +import org.springframework.http.converter.HttpMessageConverter; +import org.springframework.util.concurrent.ListenableFutureCallback; +import org.springframework.web.client.AsyncRestTemplate; +import org.springframework.web.client.HttpClientErrorException; +import org.springframework.web.client.RestClientException; +import org.springframework.web.client.RestTemplate; + +public class Http { + + protected Http() { + } + + + public static <T> Future<T> exchange(String theUri, HttpMethod theMethod, HttpEntity theRequest, Class<T> theResponseType) { + + AsyncRestTemplate restTemplate = new AsyncRestTemplate(); + + List<HttpMessageConverter<?>> converters = restTemplate.getMessageConverters(); + converters.add(0, new JSONHttpMessageConverter()); + restTemplate.setMessageConverters(converters); + + HttpFuture<T> result = new HttpFuture<T>(); + try { + restTemplate + .exchange(theUri, theMethod, theRequest, theResponseType) + .addCallback(result.callback); + } + catch (RestClientException rcx) { + return Futures.failedFuture(rcx); + } + catch (Exception x) { + return Futures.failedFuture(x); + } + + return result; + } + + /** + * + * @param theUri + * @param theMethod + * @param theRequest + * @param theResponseType + * @param readTimeOut pass -1 if you dont need to customize the read time out interval + * @return + */ + public static <T> ResponseEntity<T> exchangeSync(String theUri, HttpMethod theMethod, HttpEntity theRequest, Class<T> theResponseType, int readTimeOut) { + + RestTemplate restTemplate = new RestTemplate(); + + if(readTimeOut!=-1){ + SimpleClientHttpRequestFactory rf = (SimpleClientHttpRequestFactory) restTemplate.getRequestFactory(); + rf.setReadTimeout(1 * readTimeOut); + } + + List<HttpMessageConverter<?>> converters = restTemplate.getMessageConverters(); + converters.add(0, new JSONHttpMessageConverter()); + restTemplate.setMessageConverters(converters); + ResponseEntity<T> result = null; + + try { + result = restTemplate.exchange(theUri, theMethod, theRequest, theResponseType); + } + catch (RestClientException rcx) { + return new ResponseEntity<T>((T) rcx.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR); + } + catch (Exception x) { + return new ResponseEntity<T>((T) x.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR); + } + + return result; + } + + + + public static class HttpFuture<T> extends Futures.BasicFuture<T> { + + HttpFuture() { + } + + ListenableFutureCallback<ResponseEntity<T>> callback = new ListenableFutureCallback<ResponseEntity<T>>() { + + public void onSuccess(ResponseEntity<T> theResult) { + HttpFuture.this.result(theResult.getBody()); + } + + public void onFailure(Throwable theError) { + if (theError instanceof HttpClientErrorException) { + HttpFuture.this.cause(new Exception((HttpClientErrorException)theError)); + } + else { + HttpFuture.this.cause(theError); + } + } + }; + + } +} diff --git a/dcaedt_catalog/commons/src/main/java/org/onap/sdc/dcae/catalog/commons/JSONHttpMessageConverter.java b/dcaedt_catalog/commons/src/main/java/org/onap/sdc/dcae/catalog/commons/JSONHttpMessageConverter.java new file mode 100644 index 0000000..e711279 --- /dev/null +++ b/dcaedt_catalog/commons/src/main/java/org/onap/sdc/dcae/catalog/commons/JSONHttpMessageConverter.java @@ -0,0 +1,100 @@ +package org.onap.sdc.dcae.catalog.commons; + +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.io.Reader; +import java.io.Writer; +import java.lang.reflect.Type; +import java.nio.charset.Charset; + +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpInputMessage; +import org.springframework.http.HttpOutputMessage; +import org.springframework.http.MediaType; +import org.springframework.http.converter.AbstractHttpMessageConverter; +import org.springframework.http.converter.HttpMessageNotReadableException; +import org.springframework.http.converter.HttpMessageNotWritableException; + +import org.json.JSONObject; +import org.json.JSONArray; +import org.json.JSONTokener; +import org.json.JSONException; + +/** + */ +public class JSONHttpMessageConverter extends AbstractHttpMessageConverter<Object> { + + public static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8"); + + /** */ + public JSONHttpMessageConverter() { + super(new MediaType("application", "json", DEFAULT_CHARSET)); + } + /* + @Override + public boolean canRead(Class<?> theClazz, MediaType theMediaType) { + return canRead(theMediaType); + } + + @Override + public boolean canWrite(Class<?> theClazz, MediaType theMediaType) { + return canWrite(theMediaType); + } + */ + @Override + protected boolean supports(Class<?> theClazz) { + return theClazz.equals(JSONObject.class) || + theClazz.equals(JSONArray.class); + } + + @Override + protected Object readInternal(Class<?> theClazz, HttpInputMessage theInputMessage) + throws IOException, HttpMessageNotReadableException { + + Reader json = new InputStreamReader(theInputMessage.getBody(), getCharset(theInputMessage.getHeaders())); + + try { + if (theClazz.equals(JSONObject.class)) + return new JSONObject(new JSONTokener(json)); + if (theClazz.equals(JSONArray.class)) + return new JSONArray(new JSONTokener(json)); + + throw new HttpMessageNotReadableException("Could not process input, cannot handle " + theClazz); + } + catch (JSONException jsonx) { + throw new HttpMessageNotReadableException("Could not read JSON: " + jsonx.getMessage(), jsonx); + } + } + + @Override + protected void writeInternal(Object theObject, HttpOutputMessage theOutputMessage) + throws IOException, HttpMessageNotWritableException { + + Writer writer = new OutputStreamWriter(theOutputMessage.getBody(), getCharset(theOutputMessage.getHeaders())); + + try { + if (theObject instanceof JSONObject) { + ((JSONObject)theObject).write(writer); + } + else if (theObject instanceof JSONArray) { + ((JSONArray)theObject).write(writer); + } + + writer.close(); + } + catch(JSONException jsonx) { + throw new HttpMessageNotWritableException("Could not write JSON: " + jsonx.getMessage(), jsonx); + } + } + + private Charset getCharset(HttpHeaders theHeaders) { + if (theHeaders != null && + theHeaders.getContentType() != null && + theHeaders.getContentType().getCharSet() != null) { + return theHeaders.getContentType().getCharSet(); + } + return DEFAULT_CHARSET; + } + +} diff --git a/dcaedt_catalog/commons/src/main/java/org/onap/sdc/dcae/catalog/commons/ListBuilder.java b/dcaedt_catalog/commons/src/main/java/org/onap/sdc/dcae/catalog/commons/ListBuilder.java new file mode 100644 index 0000000..2538893 --- /dev/null +++ b/dcaedt_catalog/commons/src/main/java/org/onap/sdc/dcae/catalog/commons/ListBuilder.java @@ -0,0 +1,59 @@ +package org.onap.sdc.dcae.catalog.commons; + +import java.util.Arrays; +import java.util.List; + +import org.onap.sdc.dcae.catalog.commons.ListBuilder; + +import java.util.LinkedList; + +public class ListBuilder<T> { + + private List<T> list; + + public ListBuilder() { + this.list = new LinkedList<T>(); + } + + public boolean isEmpty() { + return this.list.isEmpty(); + } + + public ListBuilder add(T theValue) { + this.list.add(theValue); + return this; + } + + public ListBuilder addAll(final Iterable<? extends T> theValues) { + for (final T val : theValues) { + this.list.add(val); + } + return this; + } + + public ListBuilder addAll(final List<? extends T> theList) { + this.list.addAll(theList); + return this; + } + + public ListBuilder addAll(final T[] theArray) { + for (T t: theArray) this.list.add(t); + return this; + } + + public List build() { + return this.list; + } + + public List buildOpt() { + return this.list.isEmpty() ? null : this.list; + } + + public static <V> List<V> asList(V[] theArray) { + return Arrays.asList(theArray); + } + + public static <V> List<V> asListOpt(V[] theArray) { + return (theArray != null && theArray.length > 0) ? Arrays.asList(theArray) : null; + } +} diff --git a/dcaedt_catalog/commons/src/main/java/org/onap/sdc/dcae/catalog/commons/MapBuilder.java b/dcaedt_catalog/commons/src/main/java/org/onap/sdc/dcae/catalog/commons/MapBuilder.java new file mode 100644 index 0000000..3aa2a56 --- /dev/null +++ b/dcaedt_catalog/commons/src/main/java/org/onap/sdc/dcae/catalog/commons/MapBuilder.java @@ -0,0 +1,80 @@ +package org.onap.sdc.dcae.catalog.commons; + +import java.util.Map; +import java.util.HashMap; +import java.util.function.Function; + +import org.onap.sdc.dcae.catalog.commons.MapBuilder; + +import java.util.function.BiFunction; + +public class MapBuilder<K,V> { + + private Map<K,V> map; + + public MapBuilder() { + this.map = new HashMap<K,V>(); + } + + public boolean isEmpty() { + return this.map.isEmpty(); + } + + public MapBuilder<K,V> put(K theKey, V theValue) { + this.map.put(theKey, theValue); + return this; + } + + public MapBuilder<K,V> putOpt(K theKey, V theValue) { + if (theValue != null) { + this.map.put(theKey, theValue); + } + return this; + } + + public MapBuilder<K,V> put(final Map.Entry<? extends K, ? extends V> theEntry) { + this.map.put(theEntry.getKey(), theEntry.getValue()); + return this; + } + + public MapBuilder<K,V> putOpt(final Map.Entry<? extends K, ? extends V> theEntry) { + if (theEntry != null) { + this.map.put(theEntry.getKey(), theEntry.getValue()); + } + return this; + } + + public MapBuilder<K,V> putAll(final Iterable<? extends Map.Entry<? extends K, ? extends V>> theEntries) { + for (final Map.Entry<? extends K, ? extends V> e : theEntries) { + this.map.put(e.getKey(), e.getValue()); + } + return this; + } + + /* If theEntries contains multiple entries with the same key then the key gets a suffix in order to make it unique + .. */ +// public MapBuilder forceAll(final Iterable<? extends Map.Entry<? extends K, ? extends V>> theEntries, + public MapBuilder<K,V> forceAll(final Iterable<? extends Map.Entry<K, V>> theEntries, + Function<Map.Entry<K, V> , K> rekeyFunction) { + for (final Map.Entry<? extends K, ? extends V> e : theEntries) { + K key = e.getKey(); + if (this.map.containsKey(key)) + key = rekeyFunction.apply((Map.Entry<K,V>)e); + this.map.put(key, e.getValue()); + } + return this; + } + + public MapBuilder<K,V> putAll(final Map<? extends K, ? extends V> theMap) { + this.map.putAll(theMap); + return this; + } + + public Map<K,V> build() { + return this.map; + } + + public Map<K,V> buildOpt() { + return this.map.isEmpty() ? null : this.map; + } +} diff --git a/dcaedt_catalog/commons/src/main/java/org/onap/sdc/dcae/catalog/commons/Neo.java b/dcaedt_catalog/commons/src/main/java/org/onap/sdc/dcae/catalog/commons/Neo.java new file mode 100644 index 0000000..f818163 --- /dev/null +++ b/dcaedt_catalog/commons/src/main/java/org/onap/sdc/dcae/catalog/commons/Neo.java @@ -0,0 +1,54 @@ +package org.onap.sdc.dcae.catalog.commons; + +import java.util.Iterator; + +import com.google.common.base.Predicate; +import com.google.common.collect.Iterators; + +import org.json.JSONObject; + + +public class Neo { + + /* + */ + public static String literalMap(JSONObject theProps, + String theNameAlias, + String theValueAlias, + String theAssignmentOp, + String theRelationOp, + Predicate theFieldFilter) { + if(theProps.length() == 0) + return ""; + StringBuilder sb = new StringBuilder(""); + for (Iterator i = Iterators.filter(theProps.keys(), + theFieldFilter); + i.hasNext();) { + String propName = (String)i.next(); + + if (theNameAlias != null) { + sb.append(theNameAlias) + .append('.'); + } + sb.append('`') + .append(propName) + .append('`') + .append(theAssignmentOp) + .append(" {") + .append(theValueAlias) + .append("}.") + .append('`') + .append(propName) + .append('`') + .append(theRelationOp); + } + return sb.substring(0, sb.length() - theRelationOp.length()); + } + + public static String literalMap(JSONObject theProps, + String theAlias) { + return literalMap(theProps, null, theAlias, ":", ",", f -> true); + } + +} + diff --git a/dcaedt_catalog/commons/src/main/java/org/onap/sdc/dcae/catalog/commons/Proxies.java b/dcaedt_catalog/commons/src/main/java/org/onap/sdc/dcae/catalog/commons/Proxies.java new file mode 100644 index 0000000..8983599 --- /dev/null +++ b/dcaedt_catalog/commons/src/main/java/org/onap/sdc/dcae/catalog/commons/Proxies.java @@ -0,0 +1,37 @@ +package org.onap.sdc.dcae.catalog.commons; + +import java.util.Map; +import java.lang.reflect.ParameterizedType; + +import org.json.JSONObject; + +import org.onap.sdc.dcae.catalog.commons.ProxyBuilder; + + +public class Proxies { + + private Proxies() { + } + + + private static ProxyBuilder builder = new ProxyBuilder(); + + public static <T> T build(Map theData, Class<T> theType) { + return builder.build(new JSONObject(theData), theType); + } + + public static <T> T build(Map theData, Map theContextData, Class<T> theType) { + return builder.build(new JSONObject(theData), theContextData, theType); + } + + public static <T> T build(JSONObject theData, Class<T> theType) { + return builder.build(theData, theType); + } + + public static <T> Class<T> typeArgument(Class theType) { + return (Class<T>) + ((ParameterizedType)theType.getGenericSuperclass()). + getActualTypeArguments()[0]; + } + +} diff --git a/dcaedt_catalog/commons/src/main/java/org/onap/sdc/dcae/catalog/commons/Proxy.java b/dcaedt_catalog/commons/src/main/java/org/onap/sdc/dcae/catalog/commons/Proxy.java new file mode 100644 index 0000000..d368886 --- /dev/null +++ b/dcaedt_catalog/commons/src/main/java/org/onap/sdc/dcae/catalog/commons/Proxy.java @@ -0,0 +1,144 @@ +package org.onap.sdc.dcae.catalog.commons; + +import java.util.List; +import java.util.LinkedList; +import java.util.Map; +import java.util.Collections; + +import java.util.stream.Collectors; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import java.lang.reflect.Type; +import java.lang.reflect.Method; +import java.lang.reflect.Array; +import java.lang.reflect.Constructor; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.InvocationTargetException; + +import java.lang.invoke.MethodHandles; + +import com.google.common.reflect.Invokable; +import org.onap.sdc.dcae.catalog.commons.Proxy; +import org.onap.sdc.dcae.catalog.commons.ProxyBuilder; +import com.google.common.reflect.AbstractInvocationHandler; + +import org.apache.commons.beanutils.ConvertUtils; + +import org.json.JSONObject; +import org.json.JSONArray; + +public class Proxy extends AbstractInvocationHandler { + + @Retention(RetentionPolicy.RUNTIME) + @Target(ElementType.METHOD) + + public static @interface DataMap { + + public String map() default ""; + + public boolean proxy() default false; + + public Class elementType() default Void.class; + } + + + public static final Constructor<MethodHandles.Lookup> lookupHandleConstructor; + + static { + try { + lookupHandleConstructor = + MethodHandles.Lookup.class.getDeclaredConstructor(Class.class, + int.class); + + if (!lookupHandleConstructor.isAccessible()) { + lookupHandleConstructor.setAccessible(true); + } + } + catch (Exception x) { + throw new RuntimeException(x); + } + } + + + private JSONObject data; + private ProxyBuilder builder; + + protected Proxy(JSONObject theData, ProxyBuilder theBuilder) { + this.data = theData; + this.builder = theBuilder; + } + + public JSONObject data() { + return this.data; + } + + public ProxyBuilder getBuilder() { + return this.builder; + } + + protected Object handleInvocation( + Object theProxy,Method theMethod,Object[] theArgs) + throws Throwable { + if (theMethod.isDefault()) { + final Class<?> declaringClass = theMethod.getDeclaringClass(); + + return lookupHandleConstructor + .newInstance(declaringClass, MethodHandles.Lookup.PRIVATE) + .unreflectSpecial(theMethod, declaringClass) + .bindTo(theProxy) + .invokeWithArguments(theArgs); + } + + String key = theMethod.getName(); + + Proxy.DataMap dataMap = (Proxy.DataMap)theMethod.getAnnotation(Proxy.DataMap.class); + if (dataMap != null) { + String dataKey = dataMap.map(); + if (dataKey != null && !"".equals(dataKey)) + key = dataKey; + } + + //this is ugly, can this be done through an extension mechanism such as plugging in functions? + if ( builder.hasExtension(key) ) + return this.builder.extension(key).apply(this, theArgs); + + //we give priority to the context (because of the 'catalog' property issue in catalog service) but + //how natural is this? + Object val = this.builder.context(key); + if (val == null) + val = this.data.opt(key); + + if (val == null) + return null; + +//as we create proxies here we should store them back in the 'data' so that we do not do it again +//can we always 'recognize' them? + if (val instanceof String && + String.class != theMethod.getReturnType()) { + //??This will yield a POJO .. + return ConvertUtils.convert((String)val, theMethod.getReturnType()); + } + else if (val instanceof JSONObject) { + if (dataMap != null && dataMap.proxy()) { + return builder.build((JSONObject)val, theMethod.getReturnType()); + } + } + else if (val instanceof JSONArray&& dataMap != null && + dataMap.proxy() && + List.class.isAssignableFrom(theMethod.getReturnType())) { + + List res = (List) theMethod.getReturnType().newInstance(); + for (int i = 0; i < ((JSONArray) val).length(); i++) { + res.add(builder.build(((JSONArray) val).getJSONObject(i), dataMap.elementType())); + } + return res; + + } + return val; + } +} diff --git a/dcaedt_catalog/commons/src/main/java/org/onap/sdc/dcae/catalog/commons/Proxy.pojo b/dcaedt_catalog/commons/src/main/java/org/onap/sdc/dcae/catalog/commons/Proxy.pojo new file mode 100644 index 0000000..b3b5cb9 --- /dev/null +++ b/dcaedt_catalog/commons/src/main/java/org/onap/sdc/dcae/catalog/commons/Proxy.pojo @@ -0,0 +1,145 @@ +package org.onap.sdc.dcae.catalog.commons; + +import java.util.List; +import java.util.Map; +import java.util.Collections; + +import java.util.stream.Collectors; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import java.lang.reflect.Type; +import java.lang.reflect.Method; +import java.lang.reflect.Array; +import java.lang.reflect.Constructor; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.InvocationTargetException; + +import java.lang.invoke.MethodHandles; + +import com.google.common.reflect.Invokable; +import com.google.common.reflect.AbstractInvocationHandler; + +import org.apache.commons.beanutils.ConvertUtils; + + +/** + */ +public class Proxy + extends AbstractInvocationHandler { + + @Retention(RetentionPolicy.RUNTIME) + @Target(ElementType.METHOD) + + public static @interface DataMap { + + public String map() default ""; + + public boolean proxy() default false; + + public Class elementType() default Void.class; + } + + + public static Constructor<MethodHandles.Lookup> lookupHandleConstructor; + + static { + try { + lookupHandleConstructor = + MethodHandles.Lookup.class.getDeclaredConstructor(Class.class, + int.class); + + if (!lookupHandleConstructor.isAccessible()) { + lookupHandleConstructor.setAccessible(true); + } + } + catch (Exception x) { + throw new RuntimeException(x); + } + } + + + private Map data; + private ProxyBuilder builder; + + protected Proxy(Map theData, ProxyBuilder theBuilder) { + this.data = theData; + this.builder = theBuilder; + } + + public Map data() { + return this.data; + } + + public ProxyBuilder getBuilder() { + return this.builder; + } + + protected Object handleInvocation( + Object theProxy,Method theMethod,Object[] theArgs) + throws Throwable { + if (theMethod.isDefault()) { + final Class<?> declaringClass = theMethod.getDeclaringClass(); + /* + return MethodHandles.lookup() + .in(declaringClass) + .unreflectSpecial(theMethod, declaringClass) + .bindTo(theProxy) + .invokeWithArguments(theArgs); + */ + return lookupHandleConstructor + .newInstance(declaringClass, MethodHandles.Lookup.PRIVATE) + .unreflectSpecial(theMethod, declaringClass) + .bindTo(theProxy) + .invokeWithArguments(theArgs); + } + + String key = theMethod.getName(); + + Proxy.DataMap dataMap = (Proxy.DataMap)theMethod.getAnnotation(Proxy.DataMap.class); + if (dataMap != null) { + String dataKey = dataMap.map(); + if (dataKey != null && !"".equals(dataKey)) + key = dataKey; + } + + //this is ugly, can this be done through an extension mechanism such as plugging in functions? + if ( builder.hasExtension(key) ) + return this.builder.extension(key).apply(this, theArgs); + + Object val = this.data.getOrDefault(key, this.builder.context(key)); + +System.out.println("!! " + key + " : " + val); + +//as we create proxies here we should store them back in the 'data' so that we do not do it again +//can we always 'recognize' them? + if (val instanceof String && + String.class != theMethod.getReturnType()) { + return ConvertUtils.convert((String)val, theMethod.getReturnType()); + } + else if (val instanceof Map) { + if (dataMap != null && dataMap.proxy()) { + return builder.build((Map)val, theMethod.getReturnType()); + } + } + else if (val instanceof List) { + if (dataMap != null && dataMap.proxy()) { + return ((List)val) + .stream() + .map(e -> this.builder.build((Map)e, dataMap.elementType())) + .collect(Collectors.toList()); + } + } +/* + else if (val.getClass().isArray()) { + if (dataMap != null && dataMap.proxy()) { + } + } +*/ + return val; + } +} diff --git a/dcaedt_catalog/commons/src/main/java/org/onap/sdc/dcae/catalog/commons/ProxyBuilder.java b/dcaedt_catalog/commons/src/main/java/org/onap/sdc/dcae/catalog/commons/ProxyBuilder.java new file mode 100644 index 0000000..e3a422a --- /dev/null +++ b/dcaedt_catalog/commons/src/main/java/org/onap/sdc/dcae/catalog/commons/ProxyBuilder.java @@ -0,0 +1,92 @@ +package org.onap.sdc.dcae.catalog.commons; + +import java.util.Map; + +import java.util.function.Function; +import java.util.function.BiFunction; + +import org.apache.commons.beanutils.ConvertUtils; +import org.apache.commons.beanutils.Converter; + +import org.json.JSONObject; + +import org.onap.sdc.dcae.catalog.commons.Proxy; +import org.onap.sdc.dcae.catalog.commons.ProxyBuilder; + + +public class ProxyBuilder { + + private Map<String, ?> context; + private Map<String, BiFunction<Proxy, Object[], Object>> extensions; + + public ProxyBuilder() { + } +/* + public <T> T build(Map theData, Class<T> theType) { + return build(theData, this.context, theType); + } + + public <T> T build(Map theData, Map theContextData, Class<T> theType) { + return (T)java.lang.reflect.Proxy.newProxyInstance( + ProxyBuilder.class.getClassLoader(), + new Class[] { theType }, + new Proxy(theData, this)); + } +*/ + public <T> T build(Map theData, Class<T> theType) { + return build(new JSONObject(theData), theType); + } + + public <T> T build(Map theData, Map theContextData, Class<T> theType) { + return build(new JSONObject(theData), theContextData, theType); + } + + public <T> T build(JSONObject theData, Class<T> theType) { + return build(theData, this.context, theType); + } + + public <T> T build(JSONObject theData, Map theContextData, Class<T> theType) { + return (T)java.lang.reflect.Proxy.newProxyInstance( + ProxyBuilder.class.getClassLoader(), + new Class[] { theType }, + new Proxy(theData, this)); + } + + + + + public ProxyBuilder withConverter(final Function<Object, ?> theConverter, Class theType) { + ConvertUtils.register(new Converter() { + public Object convert(Class theToType, Object theValue) { + return theConverter.apply(theValue); + } + }, + theType); + return this; + } + + /* + plug in an extension to the proxy default behaviour. + */ + public ProxyBuilder withExtensions(Map<String, BiFunction<Proxy, Object[], Object>> theExtensions) { + this.extensions = theExtensions; + return this; + } + + public ProxyBuilder withContext(Map<String, ?> theContext) { + this.context = theContext; + return this; + } + + protected Object context(String theName) { + return this.context == null ? null : this.context.get(theName); + } + + protected BiFunction<Proxy, Object[], Object> extension(String theName) { + return this.extensions == null ? null : this.extensions.get(theName); + } + + protected boolean hasExtension(String theName) { + return this.extensions == null ? false : this.extensions.containsKey(theName); + } +} diff --git a/dcaedt_catalog/commons/src/main/java/org/onap/sdc/dcae/catalog/commons/Recycler.java b/dcaedt_catalog/commons/src/main/java/org/onap/sdc/dcae/catalog/commons/Recycler.java new file mode 100644 index 0000000..3493cb1 --- /dev/null +++ b/dcaedt_catalog/commons/src/main/java/org/onap/sdc/dcae/catalog/commons/Recycler.java @@ -0,0 +1,329 @@ +package org.onap.sdc.dcae.catalog.commons; + +import java.io.Reader; +import java.io.IOException; + +import java.util.List; +import java.util.Map; +import java.util.HashMap; +import java.util.AbstractMap; +import java.util.Arrays; +import java.util.Iterator; +import java.util.Collections; +import java.util.Spliterators; +import java.util.stream.Stream; +import java.util.stream.StreamSupport; + +import org.apache.commons.jxpath.Pointer; +import org.apache.commons.jxpath.JXPathContext; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.core.type.TypeReference; + +import org.onap.sdc.common.onaplog.OnapLoggerDebug; +import org.onap.sdc.common.onaplog.Enums.LogLevel; +import org.yaml.snakeyaml.Yaml; + + +/** + * Practically a copy of the Validator's service Recycler, minus the Spring framework aspects + picking up the + * description of every node + */ +public class Recycler { + + private static final String PROPERTIES = "properties"; + private static final String VALUE = "value"; + private static final String ASSIGNMENT = "assignment"; + private static final String CAPABILITY = "capability"; + private static final String RELATIONSHIP = "relationship"; + private static final String NAME = "name"; + private static OnapLoggerDebug debugLogger = OnapLoggerDebug.getInstance(); + private List<Map> imports; + private List<String> metas; + + public Recycler() { + withImports(); + withMetas(null); + } + + public Recycler withImports(String... theImports) { + debugLogger.log(LogLevel.DEBUG, this.getClass().getName(), "Setting imports to {}", theImports); + ListBuilder importsBuilder = new ListBuilder(); + for (int i = 0; i < theImports.length; i++) { + importsBuilder.add(new MapBuilder() + .put("i" + i, theImports[i]) + .build()); + } + this.imports = importsBuilder.build(); + return this; + } + + private List imports() { + ListBuilder importsBuilder = new ListBuilder(); + for (Map e: this.imports) { + importsBuilder.add(new MapBuilder() + .putAll(e) + .build()); + } + return importsBuilder.build(); + } + + public Recycler withMetas(String... theMetas) { + this.metas = (theMetas == null) ? Collections.emptyList() : Arrays.asList(theMetas); + return this; + } + + public Object recycle(final Reader theSource) throws Exception { + return this.recycle(new ObjectMapper().readValue(theSource, (Class)HashMap.class)); + } + + public Object recycle(final Object theDump) { + + final JXPathContext jxroot = JXPathContext.newContext(theDump); + jxroot.setLenient(true); + + final Map<String, Object> nodeTemplates = + (Map<String, Object>)new MapBuilder() + .putAll( + StreamSupport + .stream( + Spliterators.spliteratorUnknownSize((Iterator<Pointer>)jxroot.iteratePointers("/nodes"), 16), false) + .map(p -> { + JXPathContext jxnode = jxroot.getRelativeContext(p); + return new AbstractMap.SimpleEntry<String,Object>( + (String)jxnode.getValue(NAME) + "_" + (String)jxnode.getValue("nid"), + new MapBuilder() + .put("type", jxnode.getValue("type/name")) + .put("description", jxnode.getValue("description")) + .putOpt("metadata", nodeMetadata(jxnode)) + .putOpt(PROPERTIES, nodeProperties(jxnode)) + .putOpt("requirements", nodeRequirements(jxnode)) + .putOpt("capabilities", nodeCapabilities(jxnode)) + .build()); + })::iterator) + .buildOpt(); + + return new MapBuilder() + .put("tosca_definitions_version", "tosca_simple_yaml_1_0_0") + .put("imports", imports()) + .put("topology_template", new MapBuilder() + .putOpt("node_templates", nodeTemplates) + .build()) + .build(); + } + + /* */ + private Object nodeProperties(JXPathContext theNodeContext) { + return + new MapBuilder() + .putAll( + StreamSupport.stream( + Spliterators.spliteratorUnknownSize((Iterator<Map>)theNodeContext.iterate(PROPERTIES), 16), false) + .map(m -> new AbstractMap.SimpleEntry(m.get(NAME), this.nodeProperty(m))) + .filter(e -> e.getValue() != null) + ::iterator) + .buildOpt(); + } + + /* */ + private Object nodeProperty(final Map theSpec) { + Object value = theSpec.get(VALUE); + if (value == null) { + value = theSpec.get("default"); + if (value == null) { + /*final*/ Map assign = (Map)theSpec.get(ASSIGNMENT); + if (assign != null) { + value = assign.get(VALUE); + } + } + } + String type = (String)theSpec.get("type"); + if (value != null && type != null) { + value = getValueByType(value, type); + } + return value; + } + + private Object getValueByType(Object value, String type) { + Object returnValue = null; + try { + if ("map".equals(type) && !(value instanceof Map)) { + returnValue = new ObjectMapper().readValue(value.toString(), new TypeReference<Map>(){}); + } + else if ("list".equals(type) && !(value instanceof List)) { + returnValue = new ObjectMapper().readValue(value.toString(), new TypeReference<List>(){}); + } + else if ("integer".equals(type) && (value instanceof String)) { + returnValue = Integer.valueOf((String)value); + } + else if ("float".equals(type) && (value instanceof String)) { + returnValue = Double.valueOf((String)value); //double because that's how the yaml parser would encode it + } + } + catch (NumberFormatException nfx) { + debugLogger.log(LogLevel.DEBUG, this.getClass().getName(), "Failed to process String representation {} of numeric data: {}", value, nfx); + } + catch (IOException iox) { + debugLogger.log(LogLevel.DEBUG, this.getClass().getName(), "Failed to process {} representation of a collection: {}", value.getClass().getName(), iox); + } + return returnValue; + } + + /* */ + private List nodeRequirements(JXPathContext theNodeContext) { + return + new ListBuilder() + .addAll( + StreamSupport.stream( + Spliterators.spliteratorUnknownSize((Iterator<Map>)theNodeContext.iterate("requirements"), 16), false) + .flatMap(m -> this.nodeRequirement(m, theNodeContext).stream()) + //nicer that the ListBuilder buy cannot handle the empty lists, i.e. it will generate empty requirement lists + // .collect(Collectors.toList()) + .toArray()) + .buildOpt(); + } + + /* + * @param theSpec the requirement entry that appears within the node specification + * @param theNodeContext .. Should I pass the root context instead of assuming that the nodes context has it as parent? + * @return a List as one requirement (definition) could end up being instantiated multiple times + */ + private List nodeRequirement(final Map theSpec, JXPathContext theNodeContext/*Iterator theTargets*/) { + + final ListBuilder value = new ListBuilder(); + + final Map target = (Map)theSpec.get("target"); + final Map capability = (Map)theSpec.get(CAPABILITY); + final Map relationship = (Map)theSpec.get(RELATIONSHIP); + + //this are actual assignments + for (Iterator i = theNodeContext.getParentContext().iterate("/relations[@n2='" + theNodeContext.getValue("nid") + "']/meta[@p2='" + theSpec.get(NAME) +"']"); i.hasNext(); ) { + + String targetNodeName = (String)((Map)i.next()).get("n1"); + + //make sure target exists + Map targetNode = (Map)theNodeContext.getParentContext().getValue("/nodes[@nid='" + targetNodeName + "']"); + if (null == targetNode) { + debugLogger.log(LogLevel.DEBUG, this.getClass().getName(), "Relation points to non-existing node {}", targetNodeName); + continue; //this risks of producing a partial template .. + } + + value.add(new MapBuilder().put(theSpec.get(NAME), new MapBuilder() + .putOpt("node", targetNode.get(NAME) + "_" + targetNode.get("nid")) + .putOpt(CAPABILITY, capability == null ? null : capability.get(NAME)) + .putOpt(RELATIONSHIP, relationship == null ? null : relationship.get("type")) + .build()).build()); + } + addTemporary(theSpec, theNodeContext, value, capability, relationship); + + if (value.isEmpty()) { + value.add(new MapBuilder().put(theSpec.get(NAME), new MapBuilder() + .putOpt("node", target == null ? null : target.get(NAME) + "_" + target.get("nid")) + .putOpt(CAPABILITY, capability == null ? null : capability.get(NAME)) + .putOpt(RELATIONSHIP, relationship == null ? null : relationship.get("type")) + .build()).build()); + } + + return value.build(); + } + + private void addTemporary(Map theSpec, JXPathContext theNodeContext, ListBuilder value, Map capability, Map relationship) { + //temporary + for (Iterator i = theNodeContext.getParentContext().iterate("/relations[@n1='" + theNodeContext.getValue("nid") + "']/meta[@p1='" + theSpec.get(NAME) +"']"); i.hasNext(); ) { + + String targetNodeName = (String)((Map)i.next()).get("n2"); + + Map targetNode = (Map)theNodeContext.getParentContext().getValue("/nodes[@nid='" + targetNodeName + "']"); + //make sure target exists + if (null == targetNode) { + debugLogger.log(LogLevel.DEBUG, this.getClass().getName(), "Relation points to non-existing node {}", targetNode); + continue; //this risks of producing a partial template .. + } + + value.add(new MapBuilder().put(theSpec.get(NAME), new MapBuilder() + .putOpt("node", targetNode.get(NAME) + "_" + targetNode.get("nid")) + .putOpt(CAPABILITY, capability == null ? null : capability.get(NAME)) + .putOpt(RELATIONSHIP, relationship == null ? null : relationship.get("type")) + .build()).build()); + } + //end temporary + } + + /* */ + private Map nodeCapabilities(JXPathContext theNodeContext) { + return + new MapBuilder() + .putAll( + StreamSupport.stream( + Spliterators.spliteratorUnknownSize((Iterator<Map>)theNodeContext.iterate("capabilities"), 16), false) + .map(m -> this.nodeCapability(m)) + .filter(c -> c != null) + ::iterator) + .buildOpt(); + } + + /** + * this handles a capability assignment which only includes properties and attributes so unless there + * are any properties/attributes assignments we might not generate anything + */ + private Map.Entry nodeCapability(final Map theSpec) { + List<Map> properties = (List<Map>) theSpec.get(PROPERTIES); + if (properties == null || properties.isEmpty()) { + return null; + } + + return new AbstractMap.SimpleEntry(theSpec.get(NAME), + new MapBuilder() + .put(PROPERTIES, + new MapBuilder().putAll(properties.stream() + .filter(p -> p.containsKey(ASSIGNMENT) || + p.containsKey(VALUE)) + .map(p -> new AbstractMap.SimpleEntry( + p.get(NAME), + p.containsKey(ASSIGNMENT) ? + ((Map) p.get(ASSIGNMENT)).get(VALUE) + : p.get(VALUE)) + ) + ::iterator) + .build()) + .build()); + } + + + /* */ + private Object nodeMetadata(JXPathContext theNodeContext) { + return + new MapBuilder() + .putAll( + this.metas + .stream() + .flatMap(m -> { + Object v = theNodeContext.getValue(m); + if (v == null) { + return Stream.empty(); + } + if (v instanceof Map) { + return ((Map) v).entrySet() + .stream() + .map(e -> new AbstractMap.SimpleEntry<String, Object> + (((Map.Entry) e).getKey().toString(), + ((Map.Entry) e).getValue().toString())); + } + return Stream.of(new AbstractMap.SimpleEntry<String,Object>(m, v.toString())); + }) + ::iterator) + .buildOpt(); + } + + + public static String toString(Object theVal) { + return new Yaml().dump(theVal); + } + + + public static void main(String[] theArgs) throws Exception { + debugLogger.log(LogLevel.DEBUG, Recycler.class.getName(), + Recycler.toString( + new Recycler().recycle(new java.io.FileReader(theArgs[0])))); + } +} |