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 ():
+ *
+ *
+ * - cuts the protocol part, uses default is there is no such thing
+ * - guided by the protocol - detects the parsing service
+ * - uses the service to parse content of the expression
+ *
+ */
+@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());
+ }
+
+}