| /* |
| * 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); |
| } |
| } |
| } |