1 19 20 package org.netbeans.lib.lexer; 21 22 import java.beans.PropertyChangeEvent ; 23 import java.beans.PropertyChangeListener ; 24 import java.util.Collections ; 25 import java.util.HashSet ; 26 import java.util.Set ; 27 import org.netbeans.api.lexer.Language; 28 import org.netbeans.api.lexer.LanguagePath; 29 import org.netbeans.api.lexer.Token; 30 import org.netbeans.api.lexer.TokenId; 31 import org.netbeans.lib.editor.util.CharSequenceUtilities; 32 import org.netbeans.lib.lexer.token.DefaultToken; 33 import org.netbeans.spi.lexer.LanguageHierarchy; 34 import org.netbeans.lib.lexer.token.TextToken; 35 import org.netbeans.spi.lexer.LanguageEmbedding; 36 import org.netbeans.spi.lexer.TokenFactory; 37 import org.netbeans.spi.lexer.TokenValidator; 38 import org.openide.util.WeakListeners; 39 40 46 47 public final class LanguageOperation<T extends TokenId> implements PropertyChangeListener { 48 49 private static final int MAX_START_SKIP_LENGTH_CACHED = 10; 50 51 private static final int MAX_END_SKIP_LENGTH_CACHED = 10; 52 53 private static final TokenValidator<TokenId> NULL_VALIDATOR 54 = new TokenValidator<TokenId>() { 55 public Token<TokenId> validateToken(Token<TokenId> token, 56 TokenFactory<TokenId> factory, 57 CharSequence tokenText, int modRelOffset, 58 int removedLength, CharSequence removedText, 59 int insertedLength, CharSequence insertedText) { 60 return null; 61 } 62 }; 63 64 84 public static <T extends TokenId> void findLanguagePaths( 85 Set <LanguagePath> existingLanguagePaths, Set <LanguagePath> newLanguagePaths, 86 Set <Language<? extends TokenId>> exploredLanguages, LanguagePath lp, 87 Language<T> language) { 88 if (!existingLanguagePaths.contains(lp)) { 90 newLanguagePaths.add(lp); 91 } 92 if (!exploredLanguages.contains(language)) { 93 exploredLanguages.add(language); 94 Set <T> ids = language.tokenIds(); 95 for (T id : ids) { 96 DefaultToken<T> emptyToken = new DefaultToken<T>(id); 98 LanguageEmbedding<? extends TokenId> embedding 99 = LexerUtilsConstants.findEmbedding(emptyToken, lp, null); 100 if (embedding != null) { 101 LanguagePath elp = LanguagePath.get(lp, embedding.language()); 102 findLanguagePaths(existingLanguagePaths, newLanguagePaths, 103 exploredLanguages, elp, embedding.language()); 104 } 105 } 106 } 107 } 108 109 110 private LanguageHierarchy<T> languageHierarchy; 111 112 private Language<T> language; 113 114 115 private LanguageEmbedding<T>[][] cachedEmbeddings; 116 117 private LanguageEmbedding<T>[][] cachedJoinSectionsEmbeddings; 118 119 private TokenValidator<T>[] tokenValidators; 120 121 private Set <LanguagePath> languagePaths; 122 123 private Set <Language<? extends TokenId>> exploredLanguages; 124 125 private FlyItem<T>[] flyItems; 126 127 public LanguageOperation(LanguageHierarchy<T> languageHierarchy) { 128 this.languageHierarchy = languageHierarchy; 129 LanguageManager.getInstance().addPropertyChangeListener( 131 WeakListeners.create(PropertyChangeListener .class, this, LanguageManager.getInstance())); 132 } 133 134 public LanguageHierarchy<T> languageHierarchy() { 135 return languageHierarchy; 136 } 137 138 143 public synchronized Language<T> language() { 144 if (language == null) { 145 try { 147 Class.forName(Language.class.getName(), true, LanguageOperation.class.getClassLoader()); 148 } catch (ClassNotFoundException e) { 149 } 151 152 language = LexerApiPackageAccessor.get().createLanguage( 155 languageHierarchy); 156 } 157 return language; 158 } 159 160 public synchronized TokenValidator<T> tokenValidator(T id) { 161 if (tokenValidators == null) { 162 tokenValidators = allocateTokenValidatorArray(language.maxOrdinal() + 1); 163 } 164 TokenValidator<T> validator = tokenValidators[id.ordinal()]; 166 if (validator == null) { 167 validator = LexerSpiPackageAccessor.get().createTokenValidator(languageHierarchy(), id); 168 if (validator == null) { 169 validator = nullValidator(); 170 } 171 tokenValidators[id.ordinal()] = validator; 172 } 173 return (validator == nullValidator()) ? null : validator; 174 } 175 176 public synchronized TextToken<T> getFlyweightToken(T id, String text) { 177 TextToken<T> token; 178 if (flyItems == null) { 179 @SuppressWarnings ("unchecked") 181 FlyItem<T>[] arr = (FlyItem<T>[])new FlyItem[language.maxOrdinal() + 1]; 182 flyItems = arr; 183 } 184 FlyItem<T> item = flyItems[id.ordinal()]; 185 if (item == null) { 186 token = new TextToken<T>(id, text); token.makeFlyweight(); 188 flyItems[id.ordinal()] = new FlyItem<T>(token); 189 } else { token = item.token(); 191 if (token.text() != text) { 192 token = item.token2(); 193 if (token == null || token.text() != text) { 194 token = item.token(); 195 if (!CharSequenceUtilities.textEquals(token.text(), text)) { 196 token = item.token2(); 197 if (token == null || !CharSequenceUtilities.textEquals(token.text(), text)) { 198 token = new TextToken<T>(id, text); 200 token.makeFlyweight(); 201 } 202 item.pushToken(token); 203 } 204 } else { item.pushToken(token); 206 } 207 } 208 } 209 assert (token != null); return token; 211 } 212 213 218 public synchronized LanguageEmbedding<T> getEmbedding( 219 int startSkipLength, int endSkipLength, boolean joinSections) { 220 LanguageEmbedding<T>[][] ce = joinSections ? cachedJoinSectionsEmbeddings : cachedEmbeddings; 221 if (ce == null || startSkipLength >= ce.length) { 222 if (startSkipLength > MAX_START_SKIP_LENGTH_CACHED) 223 return createEmbedding(startSkipLength, endSkipLength, joinSections); 224 @SuppressWarnings ("unchecked") 225 LanguageEmbedding<T>[][] tmp = (LanguageEmbedding<T>[][]) 226 new LanguageEmbedding[startSkipLength + 1][]; 227 if (ce != null) 228 System.arraycopy(ce, 0, tmp, 0, ce.length); 229 ce = tmp; 230 if (joinSections) 231 cachedJoinSectionsEmbeddings = ce; 232 else 233 cachedEmbeddings = ce; 234 } 235 LanguageEmbedding<T>[] byESL = ce[startSkipLength]; 236 if (byESL == null || endSkipLength >= byESL.length) { if (endSkipLength > MAX_END_SKIP_LENGTH_CACHED) 238 return createEmbedding(startSkipLength, endSkipLength, joinSections); 239 @SuppressWarnings ("unchecked") 240 LanguageEmbedding<T>[] tmp = (LanguageEmbedding<T>[]) 241 new LanguageEmbedding[endSkipLength + 1]; 242 if (byESL != null) 243 System.arraycopy(byESL, 0, tmp, 0, byESL.length); 244 byESL = tmp; 245 ce[startSkipLength] = byESL; 246 } 247 LanguageEmbedding<T> e = byESL[endSkipLength]; 248 if (e == null) { 249 e = createEmbedding(startSkipLength, endSkipLength, joinSections); 250 byESL[endSkipLength] = e; 251 } 252 return e; 253 } 254 255 private LanguageEmbedding<T> createEmbedding(int startSkipLength, int endSkipLength, boolean joinSections) { 256 return LexerSpiPackageAccessor.get().createLanguageEmbedding( 257 language(), startSkipLength, endSkipLength, joinSections); 258 } 259 260 263 public Set <LanguagePath> languagePaths() { 264 Set <LanguagePath> lps; 265 synchronized (this) { 266 lps = languagePaths; 267 } 268 if (lps == null) { 269 lps = new HashSet <LanguagePath>(); 270 Set <LanguagePath> existingLps = Collections.emptySet(); 271 Set <Language<? extends TokenId>> exploredLangs = new HashSet <Language<? extends TokenId>>(); 272 findLanguagePaths(existingLps, lps, exploredLangs, LanguagePath.get(language()), language()); 273 synchronized (this) { 274 languagePaths = lps; 275 exploredLanguages = exploredLangs; 276 } 277 } 278 return lps; 279 } 280 281 public Set <Language<? extends TokenId>> exploredLanguages() { 282 languagePaths(); return exploredLanguages; 284 } 285 286 public void propertyChange(PropertyChangeEvent evt) { 287 synchronized (this) { 288 languagePaths = null; 289 exploredLanguages = null; 290 } 291 } 292 293 @SuppressWarnings ("unchecked") 294 private final TokenValidator<T> nullValidator() { 295 return (TokenValidator<T>)NULL_VALIDATOR; 296 } 297 298 @SuppressWarnings ("unchecked") 299 private final TokenValidator<T>[] allocateTokenValidatorArray(int length) { 300 return (TokenValidator<T>[]) new TokenValidator[length]; 301 } 302 303 private static final class FlyItem<T extends TokenId> { 304 305 private TextToken<T> token; 306 307 private TextToken<T> token2; 308 309 public FlyItem(TextToken<T> token) { 310 this.token = token; 311 } 312 313 public TextToken<T> token() { 314 return token; 315 } 316 317 public TextToken<T> token2() { 318 return token2; 319 } 320 321 public void pushToken(TextToken<T> token) { 322 this.token2 = this.token; 323 this.token = token; 324 } 325 326 } 327 328 } 329 | Popular Tags |