Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bug 565011 rebuild the core of condition expression evaluation #295

Merged
merged 1 commit into from
Jul 8, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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