/* * Copyright 2000-2009 JetBrains s.r.o. * * Licensed 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 com.intellij.psi.impl.source.tree.injected; import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.Key; import com.intellij.openapi.util.TextRange; import com.intellij.psi.LiteralTextEscaper; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiLanguageInjectionHost; import com.intellij.psi.impl.source.tree.ForeignLeafPsiElement; import com.intellij.psi.impl.source.tree.LeafElement; import com.intellij.psi.impl.source.tree.RecursiveTreeElementWalkingVisitor; import gnu.trove.THashMap; import java.util.List; import java.util.Map; /** * @author cdr */ class LeafPatcher extends RecursiveTreeElementWalkingVisitor { private int shredNo; private String hostText; private TextRange rangeInHost; private final Place myShreds; private final List<LiteralTextEscaper<? extends PsiLanguageInjectionHost>> myEscapers; final Map<LeafElement, String> newTexts = new THashMap<>(); final StringBuilder catLeafs = new StringBuilder(); LeafPatcher(Place shreds, List<LiteralTextEscaper<? extends PsiLanguageInjectionHost>> escapers) { myShreds = shreds; myEscapers = escapers; } @Override public void visitLeaf(LeafElement leaf) { String leafText = leaf instanceof ForeignLeafPsiElement ? "" : leaf.getText(); catLeafs.append(leafText); final TextRange leafRange = leaf.getTextRange(); StringBuilder leafEncodedText = constructTextFromHostPSI(leafRange.getStartOffset(), leafRange.getEndOffset()); if (!Comparing.equal(leafText, leafEncodedText)) { newTexts.put(leaf, leafEncodedText.toString()); storeUnescapedTextFor(leaf, leafText); } } private StringBuilder constructTextFromHostPSI(int startOffset, int endOffset) { boolean firstTimer = false; PsiLanguageInjectionHost.Shred current = myShreds.get(shredNo); if (hostText == null) { hostText = current.getHost().getText(); rangeInHost = current.getRangeInsideHost(); firstTimer = true; } StringBuilder text = new StringBuilder(endOffset-startOffset); while (startOffset < endOffset) { TextRange shredRange = current.getRange(); String prefix = current.getPrefix(); if (startOffset >= shredRange.getEndOffset()) { current = myShreds.get(++shredNo); hostText = current.getHost().getText(); rangeInHost = current.getRangeInsideHost(); firstTimer = true; continue; } assert startOffset >= shredRange.getStartOffset(); if (startOffset - shredRange.getStartOffset() < prefix.length()) { // inside prefix TextRange rangeInPrefix = new TextRange(startOffset - shredRange.getStartOffset(), Math.min(prefix.length(), endOffset - shredRange.getStartOffset())); text.append(prefix, rangeInPrefix.getStartOffset(), rangeInPrefix.getEndOffset()); startOffset += rangeInPrefix.getLength(); continue; } String suffix = current.getSuffix(); if (startOffset < shredRange.getEndOffset() - suffix.length()) { // inside host body, cut out from the host text int startOffsetInHost = myEscapers.get(shredNo).getOffsetInHost( startOffset - shredRange.getStartOffset() - prefix.length(), rangeInHost); int endOffsetCut = Math.min(endOffset, shredRange.getEndOffset() - suffix.length()); int endOffsetInHost = myEscapers.get(shredNo).getOffsetInHost( endOffsetCut - shredRange.getStartOffset() - prefix.length(), rangeInHost); if (endOffsetInHost != -1) { if (firstTimer ) text.append(hostText, rangeInHost.getStartOffset(), startOffsetInHost); text.append(hostText, startOffsetInHost, endOffsetInHost); startOffset = endOffsetCut; // todo what about lastTimer? continue; } } // inside suffix TextRange rangeInSuffix = new TextRange(suffix.length() - shredRange.getEndOffset() + startOffset, Math.min(suffix.length(), endOffset + suffix.length() - shredRange.getEndOffset())); text.append(suffix, rangeInSuffix.getStartOffset(), rangeInSuffix.getEndOffset()); startOffset += rangeInSuffix.getLength(); } return text; } static final Key<String> UNESCAPED_TEXT = Key.create("INJECTED_UNESCAPED_TEXT"); private static void storeUnescapedTextFor(final LeafElement leaf, final String leafText) { PsiElement psi = leaf.getPsi(); if (psi != null) { psi.putCopyableUserData(UNESCAPED_TEXT, leafText); } } }