blob: 9a1abfa105f09fa7d2486f93d292369dbea11915 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.kafka.message;
/**
* Creates an if statement based on whether or not the current version
* falls within a given range.
*/
public final class VersionConditional {
/**
* Create a version conditional.
*
* @param containingVersions The versions for which the conditional is true.
* @param possibleVersions The range of possible versions.
* @return The version conditional.
*/
static VersionConditional forVersions(Versions containingVersions,
Versions possibleVersions) {
return new VersionConditional(containingVersions, possibleVersions);
}
private final Versions containingVersions;
private final Versions possibleVersions;
private ClauseGenerator ifMember = null;
private ClauseGenerator ifNotMember = null;
private boolean alwaysEmitBlockScope = false;
private boolean allowMembershipCheckAlwaysFalse = true;
private VersionConditional(Versions containingVersions, Versions possibleVersions) {
this.containingVersions = containingVersions;
this.possibleVersions = possibleVersions;
}
VersionConditional ifMember(ClauseGenerator ifMember) {
this.ifMember = ifMember;
return this;
}
VersionConditional ifNotMember(ClauseGenerator ifNotMember) {
this.ifNotMember = ifNotMember;
return this;
}
/**
* If this is set, we will always create a new block scope, even if there
* are no 'if' statements. This is useful for cases where we want to
* declare variables in the clauses without worrying if they conflict with
* other variables of the same name.
*/
VersionConditional alwaysEmitBlockScope(boolean alwaysEmitBlockScope) {
this.alwaysEmitBlockScope = alwaysEmitBlockScope;
return this;
}
/**
* If this is set, VersionConditional#generate will throw an exception if
* the 'ifMember' clause is never used. This is useful as a sanity check
* in some cases where it doesn't make sense for the condition to always be
* false. For example, when generating a Message#write function,
* we might check that the version we're writing is supported. It wouldn't
* make sense for this check to always be false, since that would mean that
* no versions at all were supported.
*/
VersionConditional allowMembershipCheckAlwaysFalse(boolean allowMembershipCheckAlwaysFalse) {
this.allowMembershipCheckAlwaysFalse = allowMembershipCheckAlwaysFalse;
return this;
}
private void generateFullRangeCheck(Versions ifVersions,
Versions ifNotVersions,
CodeBuffer buffer) {
if (ifMember != null) {
buffer.printf("if ((_version >= %d) && (_version <= %d)) {%n",
containingVersions.lowest(), containingVersions.highest());
buffer.incrementIndent();
ifMember.generate(ifVersions);
buffer.decrementIndent();
if (ifNotMember != null) {
buffer.printf("} else {%n");
buffer.incrementIndent();
ifNotMember.generate(ifNotVersions);
buffer.decrementIndent();
}
buffer.printf("}%n");
} else if (ifNotMember != null) {
buffer.printf("if ((_version < %d) || (_version > %d)) {%n",
containingVersions.lowest(), containingVersions.highest());
buffer.incrementIndent();
ifNotMember.generate(ifNotVersions);
buffer.decrementIndent();
buffer.printf("}%n");
}
}
private void generateLowerRangeCheck(Versions ifVersions,
Versions ifNotVersions,
CodeBuffer buffer) {
if (ifMember != null) {
buffer.printf("if (_version >= %d) {%n", containingVersions.lowest());
buffer.incrementIndent();
ifMember.generate(ifVersions);
buffer.decrementIndent();
if (ifNotMember != null) {
buffer.printf("} else {%n");
buffer.incrementIndent();
ifNotMember.generate(ifNotVersions);
buffer.decrementIndent();
}
buffer.printf("}%n");
} else if (ifNotMember != null) {
buffer.printf("if (_version < %d) {%n", containingVersions.lowest());
buffer.incrementIndent();
ifNotMember.generate(ifNotVersions);
buffer.decrementIndent();
buffer.printf("}%n");
}
}
private void generateUpperRangeCheck(Versions ifVersions,
Versions ifNotVersions,
CodeBuffer buffer) {
if (ifMember != null) {
buffer.printf("if (_version <= %d) {%n", containingVersions.highest());
buffer.incrementIndent();
ifMember.generate(ifVersions);
buffer.decrementIndent();
if (ifNotMember != null) {
buffer.printf("} else {%n");
buffer.incrementIndent();
ifNotMember.generate(ifNotVersions);
buffer.decrementIndent();
}
buffer.printf("}%n");
} else if (ifNotMember != null) {
buffer.printf("if (_version > %d) {%n", containingVersions.highest());
buffer.incrementIndent();
ifNotMember.generate(ifNotVersions);
buffer.decrementIndent();
buffer.printf("}%n");
}
}
private void generateAlwaysTrueCheck(Versions ifVersions, CodeBuffer buffer) {
if (ifMember != null) {
if (alwaysEmitBlockScope) {
buffer.printf("{%n");
buffer.incrementIndent();
}
ifMember.generate(ifVersions);
if (alwaysEmitBlockScope) {
buffer.decrementIndent();
buffer.printf("}%n");
}
}
}
private void generateAlwaysFalseCheck(Versions ifNotVersions, CodeBuffer buffer) {
if (!allowMembershipCheckAlwaysFalse) {
throw new RuntimeException("Version ranges " + containingVersions +
" and " + possibleVersions + " have no versions in common.");
}
if (ifNotMember != null) {
if (alwaysEmitBlockScope) {
buffer.printf("{%n");
buffer.incrementIndent();
}
ifNotMember.generate(ifNotVersions);
if (alwaysEmitBlockScope) {
buffer.decrementIndent();
buffer.printf("}%n");
}
}
}
void generate(CodeBuffer buffer) {
Versions ifVersions = possibleVersions.intersect(containingVersions);
Versions ifNotVersions = possibleVersions.subtract(containingVersions);
// In the case where ifNotVersions would be two ranges rather than one,
// we just pass in the original possibleVersions instead.
// This is slightly less optimal, but allows us to avoid dealing with
// multiple ranges.
if (ifNotVersions == null) {
ifNotVersions = possibleVersions;
}
if (possibleVersions.lowest() < containingVersions.lowest()) {
if (possibleVersions.highest() > containingVersions.highest()) {
generateFullRangeCheck(ifVersions, ifNotVersions, buffer);
} else if (possibleVersions.highest() >= containingVersions.lowest()) {
generateLowerRangeCheck(ifVersions, ifNotVersions, buffer);
} else {
generateAlwaysFalseCheck(ifNotVersions, buffer);
}
} else if (possibleVersions.highest() >= containingVersions.lowest() &&
(possibleVersions.lowest() <= containingVersions.highest())) {
if (possibleVersions.highest() > containingVersions.highest()) {
generateUpperRangeCheck(ifVersions, ifNotVersions, buffer);
} else {
generateAlwaysTrueCheck(ifVersions, buffer);
}
} else {
generateAlwaysFalseCheck(ifNotVersions, buffer);
}
}
}