/* * * 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.qpid.gentools; import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.TreeMap; @SuppressWarnings("serial") public class AmqpFieldMap implements VersionConsistencyCheck { private final TreeMap _map = new TreeMap(); private final AmqpVersionSet _versionSet = new AmqpVersionSet(); public void removeVersion(AmqpVersion version) { String[] fieldNameArray = new String[size()]; _map.keySet().toArray(fieldNameArray); Iterator> iter = _map.entrySet().iterator(); while (iter.hasNext()) { Entry entry = iter.next(); entry.getValue().removeVersion(version); iter.remove(); } } public int size() { return _map.size(); } public AmqpFieldMap getFieldMapForOrdinal(int ordinal) { AmqpFieldMap newMap = new AmqpFieldMap(); for (AmqpField field : _map.values()) { TreeMap ordinalMap = field.getOrdinalMap(); AmqpVersionSet ordinalVersions = ordinalMap.get(ordinal); if (ordinalVersions != null) { newMap.add(field.getName(), field); } } return newMap; } public void add(String name, AmqpField field) { _versionSet.addAll(field.getVersionSet()); _map.put(name, field); } public AmqpOrdinalFieldMap getMapForVersion(AmqpVersion version, boolean codeTypeFlag, LanguageConverter converter) { // TODO: REVIEW THIS! There may be a bug here that affects C++ generation (only with >1 version)... // If version == null (a common scenario) then the version map is built up on the // basis of first found item, and ignores other version variations. // This should probably be disallowed by throwing an NPE, as AmqpOrdinalFieldMap cannot // represent these possibilities. // *OR* // Change the structure of AmqpOrdianlFieldMap to allow for the various combinations that // will result from version variation - but that is what AmqpFieldMap is... :-$ AmqpOrdinalFieldMap ordinalFieldMap = new AmqpOrdinalFieldMap(); for (AmqpField field : _map.values()) { if (version == null || field.getVersionSet().contains(version)) { // 1. Search for domain name in field domain map with version that matches String domain = ""; boolean dFound = false; for (String thisDomainName : field.getDomainMap().keySet()) { domain = thisDomainName; AmqpVersionSet versionSet = field.getDomainMap().get(domain); if (version == null || versionSet.contains(version)) { if (codeTypeFlag) { domain = converter.getGeneratedType(domain, version); } dFound = true; } } // 2. Search for ordinal in field ordianl map with version that matches int ordinal = -1; boolean oFound = false; for (Integer thisOrdinal : field.getOrdinalMap().keySet()) { ordinal = thisOrdinal; AmqpVersionSet versionSet = field.getOrdinalMap().get(ordinal); if (version == null || versionSet.contains(version)) { oFound = true; } } if (dFound && oFound) { String[] fieldDomainPair = {field.getName(), domain}; ordinalFieldMap.put(ordinal, fieldDomainPair); } } } return ordinalFieldMap; } public boolean isDomainConsistent(Generator generator, AmqpVersionSet versionSet) throws AmqpTypeMappingException { if (size() != 1) // Only one field for this ordinal { return false; } return _map.get(_map.firstKey()).isConsistent(generator); } public int getNumFields(AmqpVersion version) { int fCntr = 0; for (AmqpField field : _map.values()) { if (field.getVersionSet().contains(version)) { fCntr++; } } return fCntr; } public String parseFieldMap(CommandGenerateMethod commonGenerateMethod, MangledGenerateMethod mangledGenerateMethod, int indentSize, int tabSize, LanguageConverter converter) { String indent = Utils.createSpaces(indentSize); String cr = Utils.LINE_SEPARATOR; StringBuffer sb = new StringBuffer(); if (commonGenerateMethod == null) { // Generate warnings in code if required methods are null. sb.append(indent + "/*********************************************************" + cr); sb.append(indent + " * WARNING: Generated code could be missing." + cr); sb.append(indent + " * In call to parseFieldMap(), generation method was null." + cr); sb.append(indent + " * Check for NoSuchMethodException on startup." + cr); sb.append(indent + " *********************************************************/" + cr); } Iterator> itr = _map.entrySet().iterator(); while (itr.hasNext()) { Entry entry = itr.next(); String fieldName = entry.getKey(); AmqpField field = entry.getValue(); if (field.isCodeTypeConsistent(converter)) { // All versions identical - Common declaration String domainName = field.getDomainMap().firstKey(); AmqpVersionSet versionSet = field.getDomainMap().get(domainName); String codeType = converter.getGeneratedType(domainName, versionSet.first()); if (commonGenerateMethod != null) { sb.append(commonGenerateMethod.generate(codeType, field, versionSet, indentSize, tabSize, itr.hasNext())); } } else if (mangledGenerateMethod != null) // Version-mangled { sb.append(mangledGenerateMethod.generate(field, indentSize, tabSize, itr.hasNext())); } } return sb.toString(); } public String parseFieldMapOrdinally(GenerateMethod generateMethod, BitFieldGenerateMethod bitGenerateMethod, int indentSize, int tabSize, Generator codeGenerator) { String indent = Utils.createSpaces(indentSize); String cr = Utils.LINE_SEPARATOR; StringBuffer sb = new StringBuffer(); // Generate warnings in code if required methods are null. if (generateMethod == null || bitGenerateMethod == null) { sb.append(indent + "/***********************************************" + cr); sb.append(indent + " * WARNING: In call to parseFieldMapOrdinally():" + cr); if (generateMethod == null) { sb.append(indent + " * => generateMethod is null." + cr); } if (bitGenerateMethod == null) { sb.append(indent + " * => bitGenerateMethod is null." + cr); } sb.append(indent + " * Generated code could be missing." + cr); sb.append(indent + " * Check for NoSuchMethodException on startup." + cr); sb.append(indent + " ***********************************************/" + cr); } /* We must process elements in ordinal order because adjacent booleans (bits) * must be combined into a single byte (in groups of up to 8). Start with shared * declarations until an ordinal divergence is found. (For most methods where * there is no difference between versions, this will simplify the generated * code. */ ArrayList bitFieldList = new ArrayList(); boolean ordinalDivergenceFlag = false; int ordinal = 0; while (ordinal < size() && !ordinalDivergenceFlag) { /* Since the getFieldMapOrdinal() function may map more than one Field to * an ordinal, the number of ordinals may be less than the total number of * fields in the fieldMap. Check for empty fieldmaps... */ AmqpFieldMap ordinalFieldMap = getFieldMapForOrdinal(ordinal); if (ordinalFieldMap.size() > 0) { if (ordinalFieldMap.isDomainConsistent(codeGenerator, getVersionSet())) { String fieldName = ordinalFieldMap.getFirstFieldName(); String domain = ordinalFieldMap._map.get(fieldName).getDomainMap().firstKey(); String domainType = codeGenerator.getDomainType(domain, codeGenerator.getVersionSet().first()); if (domainType.compareTo("bit") == 0) { bitFieldList.add(fieldName); } else if (bitFieldList.size() > 0) { // End of bit types - handle deferred bit type generation if (bitGenerateMethod != null) { sb.append(bitGenerateMethod.generate(bitFieldList, ordinal, indentSize, tabSize)); } bitFieldList.clear(); } if (!ordinalDivergenceFlag) { // Defer generation of bit types until all adjacent bits have been // accounted for. if (bitFieldList.size() == 0 && generateMethod != null) { sb.append(generateMethod.generate(domainType, fieldName, ordinal, indentSize, tabSize)); } } ordinal++; } else { ordinalDivergenceFlag = true; } } } // Check if there is still more to do under a version-specific breakout if (ordinalDivergenceFlag && ordinal < size()) { // 1. Cycle through all versions in order, create outer if(version) structure AmqpVersion[] versionArray = new AmqpVersion[getVersionSet().size()]; getVersionSet().toArray(versionArray); for (int v = 0; v < versionArray.length; v++) { sb.append(indent); if (v > 0) { sb.append("else "); } sb.append("if (major == " + versionArray[v].getMajor() + " && minor == " + versionArray[v].getMinor() + ")" + cr); sb.append(indent + "{" + cr); // 2. Cycle though each ordinal from where we left off in the loop above. ArrayList bitFieldList2 = new ArrayList(bitFieldList); for (int o = ordinal; o < size(); o++) { AmqpFieldMap ordinalFieldMap = getFieldMapForOrdinal(o); if (ordinalFieldMap.size() > 0) { // 3. Cycle through each of the fields that have this ordinal. Iterator> i = ordinalFieldMap._map.entrySet().iterator(); while (i.hasNext()) { Map.Entry entry = i.next(); AmqpField field = entry.getValue(); String fieldName = entry.getKey(); // 4. Some fields may have more than one ordinal - match by both // ordinal and version. Iterator j = field.getOrdinalMap().keySet().iterator(); while (j.hasNext()) { int thisOrdinal = j.next(); AmqpVersionSet v1 = field.getOrdinalMap().get(thisOrdinal); if (thisOrdinal == o && v1.contains(versionArray[v])) { // 5. Now get the domain for this version int domainCntr = 0; Iterator k = field.getDomainMap().keySet().iterator(); while (k.hasNext()) { // Mangle domain-divergent field names String mangledFieldName = fieldName; if (field.getDomainMap().size() > 1) { mangledFieldName += "_" + (domainCntr++); } String domainName = k.next(); AmqpVersionSet v2 = field.getDomainMap().get(domainName); if (v2.contains(versionArray[v])) { // 6. (Finally!!) write the declaration String domainType = codeGenerator.getDomainType(domainName, versionArray[v]); if (domainType.compareTo("bit") == 0) { bitFieldList2.add(mangledFieldName); } else if (bitFieldList2.size() > 0) { // End of bit types - handle deferred bit type generation if (bitGenerateMethod != null) { sb.append(bitGenerateMethod.generate( bitFieldList2, o, indentSize + tabSize, tabSize)); } bitFieldList2.clear(); } // Defer generation of bit types until all adjacent bits have // been accounted for. if (bitFieldList2.size() == 0 && generateMethod != null) { sb.append(generateMethod.generate(domainType, mangledFieldName, o, indentSize + tabSize, tabSize)); } } } } } } } } // Check for remaining deferred bits if (bitFieldList2.size() > 0 && bitGenerateMethod != null) { sb.append(bitGenerateMethod.generate(bitFieldList2, size(), indentSize + tabSize, tabSize)); } sb.append(indent + "}" + cr); } } // Check for remaining deferred bits else if (bitFieldList.size() > 0 && bitGenerateMethod != null) { sb.append(bitGenerateMethod.generate(bitFieldList, size(), indentSize, tabSize)); } return sb.toString(); } private String getFirstFieldName() { return _map.firstKey(); } public boolean isVersionConsistent(AmqpVersionSet globalVersionSet) { for (String thisFieldName : _map.keySet()) { AmqpField field = _map.get(thisFieldName); if (!field.isVersionConsistent(globalVersionSet)) { return false; } } return true; } public boolean isVersionInterfaceConsistent(AmqpVersionSet globalVersionSet) { for (String thisFieldName : _map.keySet()) { AmqpField field = _map.get(thisFieldName); if (!field.isVersionInterfaceConsistent(globalVersionSet)) { return false; } } return true; } public AmqpVersionSet getVersionSet() { return _versionSet; } public Collection values() { return _map.values(); } public AmqpField get(String fieldName) { return _map.get(fieldName); } public void remove(String fieldName) { _map.remove(fieldName); } public Set keySet() { return _map.keySet(); } }