use of org.batfish.datamodel.routing_policy.statement.If in project batfish by batfish.
the class CiscoConfiguration method toInterface.
private org.batfish.datamodel.Interface toInterface(Interface iface, Map<String, IpAccessList> ipAccessLists, Configuration c) {
String name = iface.getName();
org.batfish.datamodel.Interface newIface = new org.batfish.datamodel.Interface(name, c);
String vrfName = iface.getVrf();
Vrf vrf = _vrfs.computeIfAbsent(vrfName, Vrf::new);
newIface.setDescription(iface.getDescription());
newIface.setActive(iface.getActive());
newIface.setAutoState(iface.getAutoState());
newIface.setVrf(c.getVrfs().get(vrfName));
newIface.setBandwidth(iface.getBandwidth());
if (iface.getDhcpRelayClient()) {
newIface.getDhcpRelayAddresses().addAll(_dhcpRelayServers);
} else {
newIface.getDhcpRelayAddresses().addAll(iface.getDhcpRelayAddresses());
}
newIface.setMtu(getInterfaceMtu(iface));
newIface.setOspfPointToPoint(iface.getOspfPointToPoint());
newIface.setProxyArp(iface.getProxyArp());
newIface.setSpanningTreePortfast(iface.getSpanningTreePortfast());
newIface.setSwitchport(iface.getSwitchport());
newIface.setDeclaredNames(ImmutableSortedSet.copyOf(iface.getDeclaredNames()));
// All prefixes is the combination of the interface prefix + any secondary prefixes.
ImmutableSet.Builder<InterfaceAddress> allPrefixes = ImmutableSet.builder();
if (iface.getAddress() != null) {
newIface.setAddress(iface.getAddress());
allPrefixes.add(iface.getAddress());
}
allPrefixes.addAll(iface.getSecondaryAddresses());
newIface.setAllAddresses(allPrefixes.build());
Long ospfAreaLong = iface.getOspfArea();
if (ospfAreaLong != null) {
OspfProcess proc = vrf.getOspfProcess();
if (proc != null) {
if (iface.getOspfActive()) {
proc.getActiveInterfaceList().add(name);
}
if (iface.getOspfPassive()) {
proc.getPassiveInterfaceList().add(name);
}
for (InterfaceAddress address : newIface.getAllAddresses()) {
Prefix prefix = address.getPrefix();
OspfNetwork ospfNetwork = new OspfNetwork(prefix, ospfAreaLong);
proc.getNetworks().add(ospfNetwork);
}
} else {
_w.redFlag("Interface: '" + name + "' contains OSPF settings, but there is no OSPF process");
}
}
boolean level1 = false;
boolean level2 = false;
IsisProcess isisProcess = vrf.getIsisProcess();
if (isisProcess != null) {
switch(isisProcess.getLevel()) {
case LEVEL_1:
level1 = true;
break;
case LEVEL_1_2:
level1 = true;
level2 = true;
break;
case LEVEL_2:
level2 = true;
break;
default:
throw new VendorConversionException("Invalid IS-IS level");
}
}
if (level1) {
newIface.setIsisL1InterfaceMode(iface.getIsisInterfaceMode());
} else {
newIface.setIsisL1InterfaceMode(IsisInterfaceMode.UNSET);
}
if (level2) {
newIface.setIsisL2InterfaceMode(iface.getIsisInterfaceMode());
} else {
newIface.setIsisL2InterfaceMode(IsisInterfaceMode.UNSET);
}
newIface.setIsisCost(iface.getIsisCost());
newIface.setOspfCost(iface.getOspfCost());
newIface.setOspfDeadInterval(iface.getOspfDeadInterval());
newIface.setOspfHelloMultiplier(iface.getOspfHelloMultiplier());
// switch settings
newIface.setAccessVlan(iface.getAccessVlan());
newIface.setNativeVlan(iface.getNativeVlan());
newIface.setSwitchportMode(iface.getSwitchportMode());
SwitchportEncapsulationType encapsulation = iface.getSwitchportTrunkEncapsulation();
if (encapsulation == null) {
// no encapsulation set, so use default..
// TODO: check if this is OK
encapsulation = SwitchportEncapsulationType.DOT1Q;
}
newIface.setSwitchportTrunkEncapsulation(encapsulation);
newIface.addAllowedRanges(iface.getAllowedVlans());
String incomingFilterName = iface.getIncomingFilter();
if (incomingFilterName != null) {
int incomingFilterLine = iface.getIncomingFilterLine();
IpAccessList incomingFilter = ipAccessLists.get(incomingFilterName);
if (incomingFilter == null) {
undefined(CiscoStructureType.IP_ACCESS_LIST, incomingFilterName, CiscoStructureUsage.INTERFACE_INCOMING_FILTER, incomingFilterLine);
} else {
String msg = "incoming acl for interface: " + iface.getName();
ExtendedAccessList incomingExtendedAccessList = _extendedAccessLists.get(incomingFilterName);
if (incomingExtendedAccessList != null) {
incomingExtendedAccessList.getReferers().put(iface, msg);
}
StandardAccessList incomingStandardAccessList = _standardAccessLists.get(incomingFilterName);
if (incomingStandardAccessList != null) {
incomingStandardAccessList.getReferers().put(iface, msg);
}
}
newIface.setIncomingFilter(incomingFilter);
}
String outgoingFilterName = iface.getOutgoingFilter();
if (outgoingFilterName != null) {
int outgoingFilterLine = iface.getOutgoingFilterLine();
IpAccessList outgoingFilter = ipAccessLists.get(outgoingFilterName);
if (outgoingFilter == null) {
undefined(CiscoStructureType.IP_ACCESS_LIST, outgoingFilterName, CiscoStructureUsage.INTERFACE_OUTGOING_FILTER, outgoingFilterLine);
} else {
String msg = "outgoing acl for interface: " + iface.getName();
ExtendedAccessList outgoingExtendedAccessList = _extendedAccessLists.get(outgoingFilterName);
if (outgoingExtendedAccessList != null) {
outgoingExtendedAccessList.getReferers().put(iface, msg);
}
StandardAccessList outgoingStandardAccessList = _standardAccessLists.get(outgoingFilterName);
if (outgoingStandardAccessList != null) {
outgoingStandardAccessList.getReferers().put(iface, msg);
}
}
newIface.setOutgoingFilter(outgoingFilter);
}
List<CiscoSourceNat> origSourceNats = iface.getSourceNats();
if (origSourceNats != null) {
// Process each of the CiscoSourceNats:
// 1) Collect references to ACLs and NAT pools.
// 2) For valid CiscoSourceNat rules, add them to the newIface source NATs list.
newIface.setSourceNats(origSourceNats.stream().map(nat -> processSourceNat(nat, iface, ipAccessLists)).filter(Objects::nonNull).collect(ImmutableList.toImmutableList()));
}
String routingPolicyName = iface.getRoutingPolicy();
if (routingPolicyName != null) {
int routingPolicyLine = iface.getRoutingPolicyLine();
RouteMap routingPolicyRouteMap = _routeMaps.get(routingPolicyName);
if (routingPolicyRouteMap == null) {
undefined(CiscoStructureType.ROUTE_MAP, routingPolicyName, CiscoStructureUsage.INTERFACE_POLICY_ROUTING_MAP, routingPolicyLine);
} else {
routingPolicyRouteMap.getReferers().put(iface, "routing policy for interface: " + iface.getName());
}
newIface.setRoutingPolicy(routingPolicyName);
}
return newIface;
}
use of org.batfish.datamodel.routing_policy.statement.If in project batfish by batfish.
the class CiscoConfiguration method toRoutingPolicy.
private RoutingPolicy toRoutingPolicy(Configuration c, RoutePolicy routePolicy) {
String name = routePolicy.getName();
RoutingPolicy rp = new RoutingPolicy(name, c);
List<Statement> statements = rp.getStatements();
for (RoutePolicyStatement routePolicyStatement : routePolicy.getStatements()) {
routePolicyStatement.applyTo(statements, this, c, _w);
}
If endPolicy = new If();
If nonBoolean = new If();
endPolicy.setGuard(BooleanExprs.CallExprContext.toStaticBooleanExpr());
endPolicy.setTrueStatements(Collections.singletonList(Statements.ReturnLocalDefaultAction.toStaticStatement()));
endPolicy.setFalseStatements(Collections.singletonList(nonBoolean));
nonBoolean.setGuard(BooleanExprs.CallStatementContext.toStaticBooleanExpr());
nonBoolean.setTrueStatements(Collections.singletonList(Statements.Return.toStaticStatement()));
nonBoolean.setFalseStatements(Collections.singletonList(Statements.DefaultAction.toStaticStatement()));
return rp;
}
use of org.batfish.datamodel.routing_policy.statement.If in project batfish by batfish.
the class CiscoConfiguration method toRipProcess.
private org.batfish.datamodel.RipProcess toRipProcess(RipProcess proc, String vrfName, Configuration c, CiscoConfiguration oldConfig) {
org.batfish.datamodel.RipProcess newProcess = new org.batfish.datamodel.RipProcess();
org.batfish.datamodel.Vrf vrf = c.getVrfs().get(vrfName);
// establish areas and associated interfaces
SortedSet<Prefix> networks = proc.getNetworks();
for (Entry<String, org.batfish.datamodel.Interface> e : vrf.getInterfaces().entrySet()) {
String ifaceName = e.getKey();
org.batfish.datamodel.Interface i = e.getValue();
InterfaceAddress interfaceAddress = i.getAddress();
if (interfaceAddress == null) {
continue;
}
Prefix interfaceNetwork = interfaceAddress.getPrefix();
if (networks.contains(interfaceNetwork)) {
newProcess.getInterfaces().add(ifaceName);
i.setRipEnabled(true);
boolean passive = proc.getPassiveInterfaceList().contains(i.getName()) || (proc.getPassiveInterfaceDefault() && !proc.getActiveInterfaceList().contains(ifaceName));
i.setOspfPassive(passive);
}
}
String ripExportPolicyName = "~RIP_EXPORT_POLICY:" + vrfName + "~";
RoutingPolicy ripExportPolicy = new RoutingPolicy(ripExportPolicyName, c);
c.getRoutingPolicies().put(ripExportPolicyName, ripExportPolicy);
List<Statement> ripExportStatements = ripExportPolicy.getStatements();
newProcess.setExportPolicy(ripExportPolicyName);
// policy map for default information
if (proc.getDefaultInformationOriginate()) {
If ripExportDefault = new If();
ripExportStatements.add(ripExportDefault);
ripExportDefault.setComment("RIP export default route");
Conjunction ripExportDefaultConditions = new Conjunction();
List<Statement> ripExportDefaultStatements = ripExportDefault.getTrueStatements();
ripExportDefaultConditions.getConjuncts().add(new MatchPrefixSet(new DestinationNetwork(), new ExplicitPrefixSet(new PrefixSpace(Collections.singleton(new PrefixRange(Prefix.ZERO, new SubRange(0, 0)))))));
long metric = proc.getDefaultInformationMetric();
ripExportDefaultStatements.add(new SetMetric(new LiteralLong(metric)));
// add default export map with metric
String defaultOriginateMapName = proc.getDefaultInformationOriginateMap();
if (defaultOriginateMapName != null) {
int defaultOriginateMapLine = proc.getDefaultInformationOriginateMapLine();
RoutingPolicy ripDefaultGenerationPolicy = c.getRoutingPolicies().get(defaultOriginateMapName);
if (ripDefaultGenerationPolicy == null) {
undefined(CiscoStructureType.ROUTE_MAP, defaultOriginateMapName, CiscoStructureUsage.RIP_DEFAULT_ORIGINATE_ROUTE_MAP, defaultOriginateMapLine);
} else {
RouteMap generationRouteMap = _routeMaps.get(defaultOriginateMapName);
generationRouteMap.getReferers().put(proc, "rip default-originate route-map");
GeneratedRoute.Builder route = new GeneratedRoute.Builder();
route.setNetwork(Prefix.ZERO);
route.setAdmin(MAX_ADMINISTRATIVE_COST);
route.setGenerationPolicy(defaultOriginateMapName);
newProcess.getGeneratedRoutes().add(route.build());
}
} else {
// add generated aggregate with no precondition
GeneratedRoute.Builder route = new GeneratedRoute.Builder();
route.setNetwork(Prefix.ZERO);
route.setAdmin(MAX_ADMINISTRATIVE_COST);
newProcess.getGeneratedRoutes().add(route.build());
}
ripExportDefaultConditions.getConjuncts().add(new MatchProtocol(RoutingProtocol.AGGREGATE));
ripExportDefaultStatements.add(Statements.ExitAccept.toStaticStatement());
ripExportDefault.setGuard(ripExportDefaultConditions);
}
// policy for redistributing connected routes
RipRedistributionPolicy rcp = proc.getRedistributionPolicies().get(RoutingProtocol.CONNECTED);
if (rcp != null) {
If ripExportConnected = new If();
ripExportConnected.setComment("RIP export connected routes");
Conjunction ripExportConnectedConditions = new Conjunction();
ripExportConnectedConditions.getConjuncts().add(new MatchProtocol(RoutingProtocol.CONNECTED));
List<Statement> ripExportConnectedStatements = ripExportConnected.getTrueStatements();
Long metric = rcp.getMetric();
boolean explicitMetric = metric != null;
if (!explicitMetric) {
metric = RipRedistributionPolicy.DEFAULT_REDISTRIBUTE_CONNECTED_METRIC;
}
ripExportStatements.add(new SetMetric(new LiteralLong(metric)));
ripExportStatements.add(ripExportConnected);
// add default export map with metric
String exportConnectedRouteMapName = rcp.getRouteMap();
if (exportConnectedRouteMapName != null) {
int exportConnectedRouteMapLine = rcp.getRouteMapLine();
RouteMap exportConnectedRouteMap = _routeMaps.get(exportConnectedRouteMapName);
if (exportConnectedRouteMap == null) {
undefined(CiscoStructureType.ROUTE_MAP, exportConnectedRouteMapName, CiscoStructureUsage.RIP_REDISTRIBUTE_CONNECTED_MAP, exportConnectedRouteMapLine);
} else {
exportConnectedRouteMap.getReferers().put(proc, "rip redistribute connected route-map");
ripExportConnectedConditions.getConjuncts().add(new CallExpr(exportConnectedRouteMapName));
}
}
ripExportConnectedStatements.add(Statements.ExitAccept.toStaticStatement());
ripExportConnected.setGuard(ripExportConnectedConditions);
}
// policy map for redistributing static routes
RipRedistributionPolicy rsp = proc.getRedistributionPolicies().get(RoutingProtocol.STATIC);
if (rsp != null) {
If ripExportStatic = new If();
ripExportStatic.setComment("RIP export static routes");
Conjunction ripExportStaticConditions = new Conjunction();
ripExportStaticConditions.getConjuncts().add(new MatchProtocol(RoutingProtocol.STATIC));
List<Statement> ripExportStaticStatements = ripExportStatic.getTrueStatements();
ripExportStaticConditions.getConjuncts().add(new Not(new MatchPrefixSet(new DestinationNetwork(), new ExplicitPrefixSet(new PrefixSpace(Collections.singleton(new PrefixRange(Prefix.ZERO, new SubRange(0, 0))))))));
Long metric = rsp.getMetric();
boolean explicitMetric = metric != null;
if (!explicitMetric) {
metric = RipRedistributionPolicy.DEFAULT_REDISTRIBUTE_STATIC_METRIC;
}
ripExportStatements.add(new SetMetric(new LiteralLong(metric)));
ripExportStatements.add(ripExportStatic);
// add export map with metric
String exportStaticRouteMapName = rsp.getRouteMap();
if (exportStaticRouteMapName != null) {
int exportStaticRouteMapLine = rsp.getRouteMapLine();
RouteMap exportStaticRouteMap = _routeMaps.get(exportStaticRouteMapName);
if (exportStaticRouteMap == null) {
undefined(CiscoStructureType.ROUTE_MAP, exportStaticRouteMapName, CiscoStructureUsage.RIP_REDISTRIBUTE_STATIC_MAP, exportStaticRouteMapLine);
} else {
exportStaticRouteMap.getReferers().put(proc, "rip redistribute static route-map");
ripExportStaticConditions.getConjuncts().add(new CallExpr(exportStaticRouteMapName));
}
}
ripExportStaticStatements.add(Statements.ExitAccept.toStaticStatement());
ripExportStatic.setGuard(ripExportStaticConditions);
}
// policy map for redistributing bgp routes
RipRedistributionPolicy rbp = proc.getRedistributionPolicies().get(RoutingProtocol.BGP);
if (rbp != null) {
If ripExportBgp = new If();
ripExportBgp.setComment("RIP export bgp routes");
Conjunction ripExportBgpConditions = new Conjunction();
ripExportBgpConditions.getConjuncts().add(new MatchProtocol(RoutingProtocol.BGP));
List<Statement> ripExportBgpStatements = ripExportBgp.getTrueStatements();
ripExportBgpConditions.getConjuncts().add(new Not(new MatchPrefixSet(new DestinationNetwork(), new ExplicitPrefixSet(new PrefixSpace(Collections.singleton(new PrefixRange(Prefix.ZERO, new SubRange(0, 0))))))));
Long metric = rbp.getMetric();
boolean explicitMetric = metric != null;
if (!explicitMetric) {
metric = RipRedistributionPolicy.DEFAULT_REDISTRIBUTE_BGP_METRIC;
}
ripExportStatements.add(new SetMetric(new LiteralLong(metric)));
ripExportStatements.add(ripExportBgp);
// add export map with metric
String exportBgpRouteMapName = rbp.getRouteMap();
if (exportBgpRouteMapName != null) {
int exportBgpRouteMapLine = rbp.getRouteMapLine();
RouteMap exportBgpRouteMap = _routeMaps.get(exportBgpRouteMapName);
if (exportBgpRouteMap == null) {
undefined(CiscoStructureType.ROUTE_MAP, exportBgpRouteMapName, CiscoStructureUsage.RIP_REDISTRIBUTE_BGP_MAP, exportBgpRouteMapLine);
} else {
exportBgpRouteMap.getReferers().put(proc, "rip redistribute bgp route-map");
ripExportBgpConditions.getConjuncts().add(new CallExpr(exportBgpRouteMapName));
}
}
ripExportBgpStatements.add(Statements.ExitAccept.toStaticStatement());
ripExportBgp.setGuard(ripExportBgpConditions);
}
return newProcess;
}
use of org.batfish.datamodel.routing_policy.statement.If in project batfish by batfish.
the class CiscoConfiguration method toRoutingPolicies.
private RoutingPolicy toRoutingPolicies(Configuration c, RouteMap map) {
RoutingPolicy output = new RoutingPolicy(map.getName(), c);
List<Statement> statements = output.getStatements();
Map<Integer, RoutingPolicy> clauses = new HashMap<>();
// descend map so continue targets are available
RoutingPolicy followingClause = null;
Integer followingClauseNumber = null;
for (Entry<Integer, RouteMapClause> e : map.getClauses().descendingMap().entrySet()) {
int clauseNumber = e.getKey();
RouteMapClause rmClause = e.getValue();
String clausePolicyName = getRouteMapClausePolicyName(map, clauseNumber);
Conjunction conj = new Conjunction();
// match ipv4s must be disjoined with match ipv6
Disjunction matchIpOrPrefix = new Disjunction();
for (RouteMapMatchLine rmMatch : rmClause.getMatchList()) {
BooleanExpr matchExpr = rmMatch.toBooleanExpr(c, this, _w);
if (rmMatch instanceof RouteMapMatchIpAccessListLine || rmMatch instanceof RouteMapMatchIpPrefixListLine || rmMatch instanceof RouteMapMatchIpv6AccessListLine || rmMatch instanceof RouteMapMatchIpv6PrefixListLine) {
matchIpOrPrefix.getDisjuncts().add(matchExpr);
} else {
conj.getConjuncts().add(matchExpr);
}
}
if (!matchIpOrPrefix.getDisjuncts().isEmpty()) {
conj.getConjuncts().add(matchIpOrPrefix);
}
RoutingPolicy clausePolicy = new RoutingPolicy(clausePolicyName, c);
c.getRoutingPolicies().put(clausePolicyName, clausePolicy);
If ifStatement = new If();
clausePolicy.getStatements().add(ifStatement);
clauses.put(clauseNumber, clausePolicy);
ifStatement.setComment(clausePolicyName);
ifStatement.setGuard(conj);
List<Statement> onMatchStatements = ifStatement.getTrueStatements();
for (RouteMapSetLine rmSet : rmClause.getSetList()) {
rmSet.applyTo(onMatchStatements, this, c, _w);
}
RouteMapContinue continueStatement = rmClause.getContinueLine();
Integer continueTarget = null;
RoutingPolicy continueTargetPolicy = null;
if (continueStatement != null) {
continueTarget = continueStatement.getTarget();
int statementLine = continueStatement.getStatementLine();
if (continueTarget == null) {
continueTarget = followingClauseNumber;
}
if (continueTarget != null) {
if (continueTarget <= clauseNumber) {
throw new BatfishException("Can only continue to later clause");
}
continueTargetPolicy = clauses.get(continueTarget);
if (continueTargetPolicy == null) {
String name = "clause: '" + continueTarget + "' in route-map: '" + map.getName() + "'";
undefined(CiscoStructureType.ROUTE_MAP_CLAUSE, name, CiscoStructureUsage.ROUTE_MAP_CONTINUE, statementLine);
continueStatement = null;
}
} else {
continueStatement = null;
}
}
switch(rmClause.getAction()) {
case ACCEPT:
if (continueStatement == null) {
onMatchStatements.add(Statements.ReturnTrue.toStaticStatement());
} else {
onMatchStatements.add(Statements.SetLocalDefaultActionAccept.toStaticStatement());
onMatchStatements.add(new CallStatement(continueTargetPolicy.getName()));
}
break;
case REJECT:
onMatchStatements.add(Statements.ReturnFalse.toStaticStatement());
break;
default:
throw new BatfishException("Invalid action");
}
if (followingClause != null) {
ifStatement.getFalseStatements().add(new CallStatement(followingClause.getName()));
} else {
ifStatement.getFalseStatements().add(Statements.ReturnLocalDefaultAction.toStaticStatement());
}
followingClause = clausePolicy;
followingClauseNumber = clauseNumber;
}
statements.add(new CallStatement(followingClause.getName()));
return output;
}
use of org.batfish.datamodel.routing_policy.statement.If in project batfish by batfish.
the class CiscoConfiguration method toRoutingPolicy.
private RoutingPolicy toRoutingPolicy(final Configuration c, RouteMap map) {
boolean hasContinue = map.getClauses().values().stream().anyMatch(clause -> clause.getContinueLine() != null);
if (hasContinue) {
return toRoutingPolicies(c, map);
}
RoutingPolicy output = new RoutingPolicy(map.getName(), c);
List<Statement> statements = output.getStatements();
Map<Integer, If> clauses = new HashMap<>();
// descend map so continue targets are available
If followingClause = null;
for (Entry<Integer, RouteMapClause> e : map.getClauses().descendingMap().entrySet()) {
int clauseNumber = e.getKey();
RouteMapClause rmClause = e.getValue();
String clausePolicyName = getRouteMapClausePolicyName(map, clauseNumber);
Conjunction conj = new Conjunction();
// match ipv4s must be disjoined with match ipv6
Disjunction matchIpOrPrefix = new Disjunction();
for (RouteMapMatchLine rmMatch : rmClause.getMatchList()) {
BooleanExpr matchExpr = rmMatch.toBooleanExpr(c, this, _w);
if (rmMatch instanceof RouteMapMatchIpAccessListLine || rmMatch instanceof RouteMapMatchIpPrefixListLine || rmMatch instanceof RouteMapMatchIpv6AccessListLine || rmMatch instanceof RouteMapMatchIpv6PrefixListLine) {
matchIpOrPrefix.getDisjuncts().add(matchExpr);
} else {
conj.getConjuncts().add(matchExpr);
}
}
if (!matchIpOrPrefix.getDisjuncts().isEmpty()) {
conj.getConjuncts().add(matchIpOrPrefix);
}
If ifExpr = new If();
clauses.put(clauseNumber, ifExpr);
ifExpr.setComment(clausePolicyName);
ifExpr.setGuard(conj);
List<Statement> matchStatements = ifExpr.getTrueStatements();
for (RouteMapSetLine rmSet : rmClause.getSetList()) {
rmSet.applyTo(matchStatements, this, c, _w);
}
switch(rmClause.getAction()) {
case ACCEPT:
matchStatements.add(Statements.ReturnTrue.toStaticStatement());
break;
case REJECT:
matchStatements.add(Statements.ReturnFalse.toStaticStatement());
break;
default:
throw new BatfishException("Invalid action");
}
if (followingClause != null) {
ifExpr.getFalseStatements().add(followingClause);
} else {
ifExpr.getFalseStatements().add(Statements.ReturnLocalDefaultAction.toStaticStatement());
}
followingClause = ifExpr;
}
statements.add(followingClause);
return output;
}
Aggregations