diff --git a/bundles/org.eclipse.passage.lic.base/META-INF/MANIFEST.MF b/bundles/org.eclipse.passage.lic.base/META-INF/MANIFEST.MF index 8c4cbf0cc..f4d9cf430 100644 --- a/bundles/org.eclipse.passage.lic.base/META-INF/MANIFEST.MF +++ b/bundles/org.eclipse.passage.lic.base/META-INF/MANIFEST.MF @@ -16,6 +16,7 @@ Export-Package: org.eclipse.passage.lic.base, org.eclipse.passage.lic.base.restrictions, org.eclipse.passage.lic.internal.base;x-internal:=true, org.eclipse.passage.lic.internal.base.conditions;x-internal:=true, + org.eclipse.passage.lic.internal.base.conditions.evaluation;x-internal:=true, org.eclipse.passage.lic.internal.base.conditions.mining;x-internal:=true, org.eclipse.passage.lic.internal.base.i18n;x-internal:=true, org.eclipse.passage.lic.internal.base.io;x-internal:=true, diff --git a/bundles/org.eclipse.passage.lic.base/src/org/eclipse/passage/lic/base/access/BasePermissionEmitter.java b/bundles/org.eclipse.passage.lic.base/src/org/eclipse/passage/lic/base/access/BasePermissionEmitter.java index b0bfdb250..47452df2c 100644 --- a/bundles/org.eclipse.passage.lic.base/src/org/eclipse/passage/lic/base/access/BasePermissionEmitter.java +++ b/bundles/org.eclipse.passage.lic.base/src/org/eclipse/passage/lic/base/access/BasePermissionEmitter.java @@ -29,6 +29,10 @@ import org.eclipse.passage.lic.base.conditions.LicensingConditions; import org.eclipse.passage.lic.internal.base.i18n.BaseMessages; +/** + * @deprecated use internal 1.0 BaseEmittedPermission service + */ +@Deprecated public abstract class BasePermissionEmitter implements PermissionEmitter { @Override diff --git a/bundles/org.eclipse.passage.lic.base/src/org/eclipse/passage/lic/internal/base/conditions/evaluation/AndsProtocolExpressionParseService.java b/bundles/org.eclipse.passage.lic.base/src/org/eclipse/passage/lic/internal/base/conditions/evaluation/AndsProtocolExpressionParseService.java new file mode 100644 index 000000000..c84102b06 --- /dev/null +++ b/bundles/org.eclipse.passage.lic.base/src/org/eclipse/passage/lic/internal/base/conditions/evaluation/AndsProtocolExpressionParseService.java @@ -0,0 +1,53 @@ +/******************************************************************************* + * Copyright (c) 2020 ArSysOp + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * https://www.eclipse.org/legal/epl-2.0/. + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * ArSysOp - initial API and implementation + *******************************************************************************/ +package org.eclipse.passage.lic.internal.base.conditions.evaluation; + +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; + +import org.eclipse.passage.lic.internal.api.conditions.evaluation.ExpressionParsingException; +import org.eclipse.passage.lic.internal.api.conditions.evaluation.ExpressionPasringService; +import org.eclipse.passage.lic.internal.api.conditions.evaluation.ExpressionProtocol; +import org.eclipse.passage.lic.internal.api.conditions.evaluation.ParsedExpression; + +/** + * + */ +@SuppressWarnings("restriction") +public final class AndsProtocolExpressionParseService implements ExpressionPasringService { + + private final ExpressionProtocol protocol = new ExpressionProtocol.Ands(); + + @Override + public ExpressionProtocol id() { + return protocol; + } + + /** + * Expect the incoming {@code expression} to be a semicolon-separated + * {@code key=value} pairs meaning {@code AND}-ed equality checks. + *

+ * FIXME: contract: situation must cause failure - cover it with a + * contract test + *

+ */ + @Override + public ParsedExpression parsed(String expression) throws ExpressionParsingException { + Objects.requireNonNull(expression); + Map checks = new HashMap<>(); + // FIXME: ytbd: do further work: split and fill the map + return new SimpleMapExpression(protocol, checks); + } + +} diff --git a/bundles/org.eclipse.passage.lic.base/src/org/eclipse/passage/lic/internal/base/conditions/evaluation/BaseEmissionFailureDiagnostic.java b/bundles/org.eclipse.passage.lic.base/src/org/eclipse/passage/lic/internal/base/conditions/evaluation/BaseEmissionFailureDiagnostic.java new file mode 100644 index 000000000..099da6a7c --- /dev/null +++ b/bundles/org.eclipse.passage.lic.base/src/org/eclipse/passage/lic/internal/base/conditions/evaluation/BaseEmissionFailureDiagnostic.java @@ -0,0 +1,20 @@ +/******************************************************************************* + * Copyright (c) 2020 ArSysOp + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * https://www.eclipse.org/legal/epl-2.0/. + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * ArSysOp - initial API and implementation + *******************************************************************************/ +package org.eclipse.passage.lic.internal.base.conditions.evaluation; + +import org.eclipse.passage.lic.internal.api.conditions.evaluation.EmissionFailureDiagnostic; + +@SuppressWarnings("restriction") +final class BaseEmissionFailureDiagnostic implements EmissionFailureDiagnostic { + // FIXME: ytbd +} diff --git a/bundles/org.eclipse.passage.lic.base/src/org/eclipse/passage/lic/internal/base/conditions/evaluation/BasePermission.java b/bundles/org.eclipse.passage.lic.base/src/org/eclipse/passage/lic/internal/base/conditions/evaluation/BasePermission.java new file mode 100644 index 000000000..60cf96483 --- /dev/null +++ b/bundles/org.eclipse.passage.lic.base/src/org/eclipse/passage/lic/internal/base/conditions/evaluation/BasePermission.java @@ -0,0 +1,64 @@ +/******************************************************************************* + * Copyright (c) 2020 ArSysOp + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * https://www.eclipse.org/legal/epl-2.0/. + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * ArSysOp - initial API and implementation + *******************************************************************************/ +package org.eclipse.passage.lic.internal.base.conditions.evaluation; + +import java.time.ZonedDateTime; +import java.util.Objects; + +import org.eclipse.passage.lic.internal.api.LicensedProduct; +import org.eclipse.passage.lic.internal.api.conditions.Condition; +import org.eclipse.passage.lic.internal.api.conditions.evaluation.Permission; + +@SuppressWarnings("restriction") +public final class BasePermission implements Permission { + + private final LicensedProduct product; + private final Condition condition; + private final ZonedDateTime lease; + private final ZonedDateTime expiration; + + public BasePermission(LicensedProduct product, Condition condition, ZonedDateTime lease, ZonedDateTime expiration) { + Objects.requireNonNull(product, "BasePermission::product"); //$NON-NLS-1$ + Objects.requireNonNull(condition, "BasePermission::condition"); //$NON-NLS-1$ + Objects.requireNonNull(lease, "BasePermission::lease"); //$NON-NLS-1$ + Objects.requireNonNull(expiration, "BasePermission::expiration"); //$NON-NLS-1$ + if (!lease.isBefore(expiration)) { + throw new IllegalArgumentException("`Lease` date must strictly less than `expriation` date."); //$NON-NLS-1$ + } + this.product = product; + this.condition = condition; + this.lease = lease; + this.expiration = expiration; + } + + @Override + public LicensedProduct product() { + return product; + } + + @Override + public Condition condition() { + return condition; + } + + @Override + public ZonedDateTime leaseDate() { + return lease; + } + + @Override + public ZonedDateTime expireDate() { + return expiration; + } + +} diff --git a/bundles/org.eclipse.passage.lic.base/src/org/eclipse/passage/lic/internal/base/conditions/evaluation/BasePermissionEmittingService.java b/bundles/org.eclipse.passage.lic.base/src/org/eclipse/passage/lic/internal/base/conditions/evaluation/BasePermissionEmittingService.java new file mode 100644 index 000000000..676224e37 --- /dev/null +++ b/bundles/org.eclipse.passage.lic.base/src/org/eclipse/passage/lic/internal/base/conditions/evaluation/BasePermissionEmittingService.java @@ -0,0 +1,119 @@ +/******************************************************************************* + * Copyright (c) 2020 ArSysOp + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * https://www.eclipse.org/legal/epl-2.0/. + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * ArSysOp - initial API and implementation + *******************************************************************************/ +package org.eclipse.passage.lic.internal.base.conditions.evaluation; + +import java.time.ZonedDateTime; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Objects; + +import org.eclipse.passage.lic.internal.api.LicensedProduct; +import org.eclipse.passage.lic.internal.api.LicensingException; +import org.eclipse.passage.lic.internal.api.conditions.Condition; +import org.eclipse.passage.lic.internal.api.conditions.EvaluationType; +import org.eclipse.passage.lic.internal.api.conditions.ValidityPeriod; +import org.eclipse.passage.lic.internal.api.conditions.ValidityPeriodClosed; +import org.eclipse.passage.lic.internal.api.conditions.evaluation.Emission; +import org.eclipse.passage.lic.internal.api.conditions.evaluation.ExpressionEvaluationService; +import org.eclipse.passage.lic.internal.api.conditions.evaluation.ExpressionEvaluatorsRegistry; +import org.eclipse.passage.lic.internal.api.conditions.evaluation.ExpressionParsingException; +import org.eclipse.passage.lic.internal.api.conditions.evaluation.ExpressionPasringRegistry; +import org.eclipse.passage.lic.internal.api.conditions.evaluation.ExpressionTokenAssessmentService; +import org.eclipse.passage.lic.internal.api.conditions.evaluation.ExpressionTokenAssessorsRegistry; +import org.eclipse.passage.lic.internal.api.conditions.evaluation.ParsedExpression; +import org.eclipse.passage.lic.internal.api.conditions.evaluation.PermissionEmittingService; +import org.eclipse.passage.lic.internal.api.registry.StringServiceId; + +@SuppressWarnings("restriction") +public final class BasePermissionEmittingService implements PermissionEmittingService { + + private final StringServiceId id = new StringServiceId("default-emitter"); //$NON-NLS-1$ + private final ExpressionPasringRegistry parsers; + private final ExpressionTokenAssessorsRegistry assessors; + private ExpressionEvaluatorsRegistry evaluators; + + public BasePermissionEmittingService(// + ExpressionPasringRegistry parsers, // + ExpressionTokenAssessorsRegistry assessors, // + ExpressionEvaluatorsRegistry evaluators) { + Objects.requireNonNull(parsers); + Objects.requireNonNull(assessors); + Objects.requireNonNull(evaluators); + this.assessors = assessors; + this.parsers = parsers; + this.evaluators = evaluators; + } + + @Override + public StringServiceId id() { + return id; + } + + @Override + public Emission emit(Collection conditions, LicensedProduct product) { + return conditions.stream() // + .map(condition -> emitFor(condition, product))// + .reduce(// + new Emission.Successful(Collections.emptyList()), // + new SumOfEmissions()); + } + + private Emission emitFor(Condition condition, LicensedProduct product) { + boolean satisfied = false; + try { + satisfied = expressionIsSatisfied(condition); + } catch (ExpressionParsingException e) { + new Emission.Failed(new BaseEmissionFailureDiagnostic()); // FIXME: ytbd: explain + } catch (LicensingException e) { + new Emission.Failed(new BaseEmissionFailureDiagnostic()); // FIXME: ytbd: explain + } + if (satisfied) { + return new Emission.Successful(Arrays.asList(// + new BasePermission(// + product, // + condition, // + ZonedDateTime.now(), // + expiration(condition.validityPeriod())))); + } else { + return new Emission.Failed(new BaseEmissionFailureDiagnostic()); // FIXME: ytbd: explain + } + } + + private boolean expressionIsSatisfied(Condition condition) throws LicensingException, ExpressionParsingException { + ExpressionTokenAssessmentService assessor = // + evaluator(condition.evaluationInstructions().type()); + ParsedExpression expression = new FormalizedExpression( // + condition.evaluationInstructions().expression(), // + parsers.get()).get(); + ExpressionEvaluationService evaluator = evaluators.get().service(expression.protocol()); + return evaluator.evaluate(expression, assessor); + } + + private ExpressionTokenAssessmentService evaluator(EvaluationType type) throws LicensingException { + if (!assessors.get().hasService(type)) { + throw new LicensingException(String.format( + "Expression of [%s] evaluation type cannot be asessed: no evaluation services are geristered for the type", //$NON-NLS-1$ FIXME + type)); + } + return assessors.get().service(type); + } + + private ZonedDateTime expiration(ValidityPeriod period) { + if (ValidityPeriodClosed.class.isInstance(period)) { + return ((ValidityPeriodClosed) period).to(); + } + return ZonedDateTime.now().plusDays(1); + } + +} diff --git a/bundles/org.eclipse.passage.lic.base/src/org/eclipse/passage/lic/internal/base/conditions/evaluation/FormalizedExpression.java b/bundles/org.eclipse.passage.lic.base/src/org/eclipse/passage/lic/internal/base/conditions/evaluation/FormalizedExpression.java new file mode 100644 index 000000000..35ea1818f --- /dev/null +++ b/bundles/org.eclipse.passage.lic.base/src/org/eclipse/passage/lic/internal/base/conditions/evaluation/FormalizedExpression.java @@ -0,0 +1,80 @@ +/******************************************************************************* + * Copyright (c) 2020 ArSysOp + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * https://www.eclipse.org/legal/epl-2.0/. + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * ArSysOp - initial API and implementation + *******************************************************************************/ +package org.eclipse.passage.lic.internal.base.conditions.evaluation; + +import java.util.Objects; + +import org.eclipse.passage.lic.internal.api.conditions.evaluation.ExpressionParsingException; +import org.eclipse.passage.lic.internal.api.conditions.evaluation.ExpressionPasringService; +import org.eclipse.passage.lic.internal.api.conditions.evaluation.ExpressionProtocol; +import org.eclipse.passage.lic.internal.api.conditions.evaluation.ParsedExpression; +import org.eclipse.passage.lic.internal.api.registry.Registry; + +/** + *

+ * Turns the given raw {@code expression} string into a workable + * {@linkplain ParsedExpression} instance. Is aware of top-level expression + * syntax (): + *

+ * + */ +@SuppressWarnings("restriction") +final class FormalizedExpression { + + private final String raw; + private final String separator; + private final Registry parsers; + + FormalizedExpression(String raw, String separator, Registry parsers) { + Objects.requireNonNull(raw, "RetrievedExpression::raw"); //$NON-NLS-1$ + Objects.requireNonNull(separator, "RetrievedExpression::separator"); //$NON-NLS-1$ + Objects.requireNonNull(parsers, "RetrievedExpression::parsers"); //$NON-NLS-1$ + this.raw = raw; + this.separator = separator; + this.parsers = parsers; + } + + FormalizedExpression(String raw, Registry parsers) { + this(raw, "!!", parsers); //$NON-NLS-1$ + } + + ParsedExpression get() throws ExpressionParsingException { + return service().parsed(content()); + } + + private ExpressionPasringService service() { + ExpressionProtocol protocol = protocol(); + return parsers.hasService(protocol) // + ? parsers.service(protocol) // + : parsers.service(new ExpressionProtocol.Default()); // guaranteed by Framework contract + } + + private ExpressionProtocol protocol() { + int index = raw.indexOf(separator); + return index <= 0 // + ? new ExpressionProtocol.Default() // + : new ExpressionProtocol.Of(raw.substring(0, index)); + } + + private String content() { + int index = raw.indexOf(separator); + return index <= 0 // + ? raw // + : raw.substring(index + separator.length()); + } + +} diff --git a/bundles/org.eclipse.passage.lic.base/src/org/eclipse/passage/lic/internal/base/conditions/evaluation/SimpleMapExpression.java b/bundles/org.eclipse.passage.lic.base/src/org/eclipse/passage/lic/internal/base/conditions/evaluation/SimpleMapExpression.java new file mode 100644 index 000000000..42fb48dee --- /dev/null +++ b/bundles/org.eclipse.passage.lic.base/src/org/eclipse/passage/lic/internal/base/conditions/evaluation/SimpleMapExpression.java @@ -0,0 +1,49 @@ +/******************************************************************************* + * Copyright (c) 2020 ArSysOp + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * https://www.eclipse.org/legal/epl-2.0/. + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * ArSysOp - initial API and implementation + *******************************************************************************/ +package org.eclipse.passage.lic.internal.base.conditions.evaluation; + +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; + +import org.eclipse.passage.lic.internal.api.conditions.evaluation.ExpressionProtocol; +import org.eclipse.passage.lic.internal.api.conditions.evaluation.ParsedExpression; + +@SuppressWarnings("restriction") +final class SimpleMapExpression implements ParsedExpression { + + private final Map checks; + private final ExpressionProtocol format; + + SimpleMapExpression(ExpressionProtocol format, Map checks) { + Objects.requireNonNull(format); + Objects.requireNonNull(checks); + this.format = format; + this.checks = new HashMap<>(checks); + } + + @Override + public ExpressionProtocol protocol() { + return format; + } + + public Collection keys() { + return checks.keySet(); + } + + public String expected(String key) { + return checks.get(key); + } + +} diff --git a/bundles/org.eclipse.passage.lic.base/src/org/eclipse/passage/lic/internal/base/conditions/evaluation/SimpleMapExpressionEvaluationService.java b/bundles/org.eclipse.passage.lic.base/src/org/eclipse/passage/lic/internal/base/conditions/evaluation/SimpleMapExpressionEvaluationService.java new file mode 100644 index 000000000..e94006561 --- /dev/null +++ b/bundles/org.eclipse.passage.lic.base/src/org/eclipse/passage/lic/internal/base/conditions/evaluation/SimpleMapExpressionEvaluationService.java @@ -0,0 +1,39 @@ +/******************************************************************************* + * Copyright (c) 2020 ArSysOp + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * https://www.eclipse.org/legal/epl-2.0/. + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * ArSysOp - initial API and implementation + *******************************************************************************/ +package org.eclipse.passage.lic.internal.base.conditions.evaluation; + +import org.eclipse.passage.lic.internal.api.conditions.evaluation.ExpressionEvaluationService; +import org.eclipse.passage.lic.internal.api.conditions.evaluation.ExpressionProtocol; +import org.eclipse.passage.lic.internal.api.conditions.evaluation.ExpressionTokenAssessmentService; +import org.eclipse.passage.lic.internal.api.conditions.evaluation.ParsedExpression; + +@SuppressWarnings("restriction") +public final class SimpleMapExpressionEvaluationService implements ExpressionEvaluationService { + + private final ExpressionProtocol format = new ExpressionProtocol.Ands(); + + @Override + public ExpressionProtocol id() { + return format; + } + + @Override + public boolean evaluate(ParsedExpression expression, ExpressionTokenAssessmentService evaluator) { + // FIXME: ytbd: if not instanceof - swear to diagnose + SimpleMapExpression simple = (SimpleMapExpression) expression; + // FIXME: ytbd: need report which check is failed + return simple.keys().stream() // + .allMatch(key -> evaluator.equal(key, simple.expected(key))); + } + +} diff --git a/bundles/org.eclipse.passage.lic.base/src/org/eclipse/passage/lic/internal/base/conditions/evaluation/SumOfEmissions.java b/bundles/org.eclipse.passage.lic.base/src/org/eclipse/passage/lic/internal/base/conditions/evaluation/SumOfEmissions.java new file mode 100644 index 000000000..e324572b5 --- /dev/null +++ b/bundles/org.eclipse.passage.lic.base/src/org/eclipse/passage/lic/internal/base/conditions/evaluation/SumOfEmissions.java @@ -0,0 +1,44 @@ +/******************************************************************************* + * Copyright (c) 2020 ArSysOp + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * https://www.eclipse.org/legal/epl-2.0/. + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * ArSysOp - initial API and implementation + *******************************************************************************/ +package org.eclipse.passage.lic.internal.base.conditions.evaluation; + +import java.util.Arrays; +import java.util.Collection; +import java.util.function.BinaryOperator; +import java.util.stream.Collectors; + +import org.eclipse.passage.lic.internal.api.conditions.evaluation.Emission; +import org.eclipse.passage.lic.internal.api.conditions.evaluation.EmissionFailureDiagnostic; +import org.eclipse.passage.lic.internal.api.conditions.evaluation.Permission; + +@SuppressWarnings("restriction") +public final class SumOfEmissions implements BinaryOperator { + + @Override + public Emission apply(Emission first, Emission second) { + return (first.failed() || second.failed()) // + ? new Emission.Failed(sumDiagnostic(first, second))// + : new Emission.Successful(sumPermissions(first, second)); + } + + private EmissionFailureDiagnostic sumDiagnostic(Emission first, Emission second) { + return new BaseEmissionFailureDiagnostic(); // FIXME: implement + } + + private Collection sumPermissions(Emission first, Emission second) { + return Arrays.asList(first.permissions(), second.permissions()).stream()// + .flatMap(Collection::stream) // + .collect(Collectors.toList()); + } + +}