001/*-------------------------------------------------------------------------+ 002| | 003| Copyright 2005-2011 the ConQAT Project | 004| | 005| Licensed under the Apache License, Version 2.0 (the "License"); | 006| you may not use this file except in compliance with the License. | 007| You may obtain a copy of the License at | 008| | 009| http://www.apache.org/licenses/LICENSE-2.0 | 010| | 011| Unless required by applicable law or agreed to in writing, software | 012| distributed under the License is distributed on an "AS IS" BASIS, | 013| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 014| See the License for the specific language governing permissions and | 015| limitations under the License. | 016+-------------------------------------------------------------------------*/ 017package eu.cqse.check.framework.shallowparser.util; 018 019import java.util.List; 020 021import org.conqat.lib.commons.assertion.CCSMAssert; 022import org.conqat.lib.commons.collections.CollectionUtils; 023import org.conqat.lib.commons.collections.UnmodifiableList; 024 025import eu.cqse.check.framework.scanner.ELanguage; 026import eu.cqse.check.framework.scanner.ETokenType; 027import eu.cqse.check.framework.scanner.IToken; 028import eu.cqse.check.framework.shallowparser.framework.EShallowEntityType; 029import eu.cqse.check.framework.shallowparser.framework.ShallowEntity; 030 031/** 032 * Utility methods that can be used to extract the fully qualified names of 033 * elements in the shallow AST. 034 */ 035public class ShallowParsedNameUtils { 036 037 /** C++ scope separator */ 038 private static final String CPP_SCOPE = "::"; 039 040 /** 041 * Returns the fully qualified name of the programming construct the given 042 * entity represents. This includes surrounding classes and namespaces. This 043 * method employs various language-specific heuristics and thus probably 044 * needs to be extended for new languages after extending the parser. 045 * 046 * @param entity 047 * the entity must be of type MODULE, TYPE, METHOD, or ATTRIBUTE. 048 */ 049 public static String getFullyQualifiedName(ShallowEntity entity) { 050 return getFullyQualifiedName(entity, determineLanguage(entity)); 051 } 052 053 /** 054 * Returns the fully qualified name of the programming construct the given 055 * entity represents. This includes surrounding classes and namespaces. This 056 * method employs various language-specific heuristics and thus probably 057 * needs to be extended for new languages after extending the parser. 058 * 059 * @param entity 060 * the entity must be of type MODULE, TYPE, METHOD, or ATTRIBUTE. 061 */ 062 public static String getFullyQualifiedName(ShallowEntity entity, ELanguage language) { 063 064 EShallowEntityType type = entity.getType(); 065 CCSMAssert.isTrue( 066 type == EShallowEntityType.MODULE || type == EShallowEntityType.TYPE 067 || type == EShallowEntityType.METHOD || type == EShallowEntityType.ATTRIBUTE, 068 "May only pass MODULE, TYPE, METHOD, or ATTRIBUTE!"); 069 070 String name = entity.getName(); 071 072 if (language == ELanguage.CPP) { 073 if (type == EShallowEntityType.METHOD || type == EShallowEntityType.ATTRIBUTE) { 074 name = extractCppMethodOrAttributeName(entity); 075 } 076 } 077 078 // the previous code dealt with local name expansion, while the final 079 // call adds global expansion by adding the names of parent types and 080 // modules (think surrounding class). 081 return expandParentNames(name, entity, language); 082 } 083 084 /** 085 * Returns the string obtained by prefixing the given name by the names of 086 * all TYPE/MODULE parent entities using the correct separator for the 087 * language. 088 */ 089 private static String expandParentNames(String name, ShallowEntity entity, ELanguage language) { 090 091 String separator; 092 switch (language) { 093 case CPP: 094 separator = CPP_SCOPE; 095 break; 096 case JAVA: 097 case CS: 098 separator = "."; 099 break; 100 default: 101 // we use a non-standard default separator for unknown languages to 102 // encourage updating the code but not hindering experiments on new 103 // languages. 104 separator = "/"; 105 } 106 107 ShallowEntity parent = entity.getParent(); 108 while (parent != null) { 109 if (parent.getType() == EShallowEntityType.TYPE || parent.getType() == EShallowEntityType.MODULE) { 110 name = parent.getName() + separator + name; 111 } 112 113 parent = parent.getParent(); 114 } 115 116 return name; 117 } 118 119 /** Determines the programming language of an entity. */ 120 private static ELanguage determineLanguage(ShallowEntity entity) { 121 UnmodifiableList<IToken> tokens = entity.includedTokens(); 122 CCSMAssert.isFalse(tokens.isEmpty(), "Shallow entities must have underlying tokens!"); 123 return CollectionUtils.getAny(tokens).getLanguage(); 124 } 125 126 /** 127 * Extracts the complete name of a C++ method or attribute. This includes 128 * modifiers (e.g. for destructors) and the complete type prefix for fully 129 * qualified method names (e.g. A::B::c). 130 */ 131 private static String extractCppMethodOrAttributeName(ShallowEntity methodOrAttribute) { 132 133 CCSMAssert.isTrue( 134 methodOrAttribute.getType() == EShallowEntityType.METHOD 135 || methodOrAttribute.getType() == EShallowEntityType.ATTRIBUTE, 136 "May only be used for method or attribute!"); 137 138 List<IToken> tokens = methodOrAttribute.includedTokens(); 139 String name = methodOrAttribute.getName(); 140 141 int index = 0; 142 143 // We advance the index to find the position of the name in the token 144 // stream. As for constructors the name of the method and the class are 145 // the same, the second part of the condition checks if the identifier 146 // is followed by the scope separator ("::"), which indicates that this 147 // is a class name. 148 while ((index < tokens.size() && !tokens.get(index).getText().equals(name)) 149 || (index + 1 < tokens.size() && tokens.get(index + 1).getType() == ETokenType.SCOPE)) { 150 index += 1; 151 } 152 153 if (index >= tokens.size()) { 154 return name; 155 } 156 157 // for destructors, the scanner produces a separate token for the "~" 158 // character, which thus must be handled explicitly here 159 if (index >= 1 && tokens.get(index - 1).getType() == ETokenType.COMP) { 160 index -= 1; 161 name = "~" + name; 162 } 163 164 // expand the name while the current position is prepended by a scope 165 // marker ("::") 166 while (index >= 2 && tokens.get(index - 1).getType() == ETokenType.SCOPE) { 167 index -= 2; 168 name = tokens.get(index).getText() + CPP_SCOPE + name; 169 } 170 171 return name; 172 } 173}