Skip to content

Commit

Permalink
Merge pull request #295 from eclipse-passage/565011
Browse files Browse the repository at this point in the history
Bug 565011 rebuild the core of condition expression evaluation
  • Loading branch information
eparovyshnaya authored Jul 8, 2020
2 parents d0c1c02 + f4145de commit a94bf24
Show file tree
Hide file tree
Showing 10 changed files with 473 additions and 0 deletions.
1 change: 1 addition & 0 deletions bundles/org.eclipse.passage.lic.base/META-INF/MANIFEST.MF
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
@@ -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.
* <p>
* FIXME: contract: <no checks> situation must cause failure - cover it with a
* contract test
* </p>
*/
@Override
public ParsedExpression parsed(String expression) throws ExpressionParsingException {
Objects.requireNonNull(expression);
Map<String, String> checks = new HashMap<>();
// FIXME: ytbd: do further work: split and fill the map
return new SimpleMapExpression(protocol, checks);
}

}
Original file line number Diff line number Diff line change
@@ -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
}
Original file line number Diff line number Diff line change
@@ -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;
}

}
Original file line number Diff line number Diff line change
@@ -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<Condition> 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);
}

}
Original file line number Diff line number Diff line change
@@ -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;

/**
* <p>
* Turns the given raw {@code expression} string into a workable
* {@linkplain ParsedExpression} instance. Is aware of top-level expression
* syntax (<protocol><separator><content>):
* </p>
* <ul>
* <li>cuts the protocol part, uses default is there is no such thing</li>
* <li>guided by the protocol - detects the parsing service</li>
* <li>uses the service to parse content of the expression</li>
* </ul>
*/
@SuppressWarnings("restriction")
final class FormalizedExpression {

private final String raw;
private final String separator;
private final Registry<ExpressionProtocol, ExpressionPasringService> parsers;

FormalizedExpression(String raw, String separator, Registry<ExpressionProtocol, ExpressionPasringService> 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<ExpressionProtocol, ExpressionPasringService> 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());
}

}
Loading

0 comments on commit a94bf24

Please sign in to comment.