public void update( int x, int y, boolean b0, boolean b1, boolean b2, boolean onScreen, boolean[] keysDown, String typed) { b0Clicked = !this.b0 && b0; b1Clicked = !this.b1 && b1; b2Clicked = !this.b2 && b2; b0Released = this.b0 && !b0; b1Released = this.b1 && !b1; b2Released = this.b2 && !b2; this.x = x; this.y = y; this.b0 = b0; this.b1 = b1; this.b2 = b2; this.onScreen = onScreen; this.typed = ""; for (Key key : keys) { key.tick(keysDown); } }
/** * Method decrypts the data with the RSA private key corresponding to this certificate (which was * used to encrypt it). Decryption will be done with keystore * * @param data data to be decrypted. * @param token index of authentication token * @param pin PIN code * @return decrypted data. * @throws DigiDocException for all decryption errors */ public byte[] decrypt(byte[] data, int token, String pin) throws DigiDocException { try { if (m_keyStore == null) throw new DigiDocException( DigiDocException.ERR_NOT_INITED, "Keystore not initialized", null); String alias = getTokenName(token); if (alias == null) throw new DigiDocException( DigiDocException.ERR_TOKEN_LOGIN, "Invalid token nr: " + token, null); // get key if (m_logger.isDebugEnabled()) m_logger.debug( "loading key: " + alias + " passwd-len: " + ((pin != null) ? pin.length() : 0)); Key key = m_keyStore.getKey(alias, pin.toCharArray()); if (m_logger.isDebugEnabled()) m_logger.debug("Key: " + ((key != null) ? "OK, algorithm: " + key.getAlgorithm() : "NULL")); if (key == null) throw new DigiDocException( DigiDocException.ERR_TOKEN_LOGIN, "Invalid password for token: " + alias, null); Cipher cipher = Cipher.getInstance("RSA"); cipher.init(Cipher.DECRYPT_MODE, key); byte[] decdata = cipher.doFinal(data); if (m_logger.isDebugEnabled()) m_logger.debug("Decrypted len: " + ((decdata != null) ? decdata.length : 0)); return decdata; } catch (Exception ex) { m_logger.error("Error decrypting: " + ex); } return null; }
@Override protected void rehash(int newCapacity) { // rehash should discard gced keys // because otherwise there is a remote probability of // having two (Weak|Soft)Keys with accidentally equal hashCodes and different but gced key // values int oldCapacity = _set.length; Object[] oldKeys = _set; V[] oldVals = _values; _set = new Object[newCapacity]; _values = (V[]) new Object[newCapacity]; for (int i = oldCapacity; i-- > 0; ) { Object o = oldKeys[i]; if (o == null || o == REMOVED) continue; Key<K> k = (Key<K>) o; K key = k.get(); if (key == null) continue; int index = insertionIndex(k); if (index < 0) { // make 'key' alive till this point to not allow 'o.referent' to be gced throwObjectContractViolation(_set[-index - 1], o + "; key: " + key); } _set[index] = o; _values[index] = oldVals[i]; } }
public int hashCode() { int h = 0; for (Entry<Key<K>, V> entry : hashEntrySet) { Key<K> wk = entry.getKey(); if (wk == null) continue; Object v; h += wk.hashCode() ^ ((v = entry.getValue()) == null ? 0 : v.hashCode()); } return h; }
public Key max() { if (size == 0) { return null; } else { Key maximum = list.getFirst(); for (Key p : list) { if (p.compareTo(maximum) == 1) maximum = p; } return maximum; } }
void assertPrivateKs(File file, String pass, String alias) throws Exception { KeyStore ks = loadKeyStore("jceks", file, alias); List aliases = ListUtil.fromIterator(new EnumerationIterator(ks.aliases())); assertEquals(2, aliases.size()); Certificate cert = ks.getCertificate(alias + ".crt"); assertNotNull(cert); assertEquals("X.509", cert.getType()); assertTrue(ks.isKeyEntry(alias + ".key")); assertTrue(ks.isCertificateEntry(alias + ".crt")); Key key = ks.getKey(alias + ".key", pass.toCharArray()); assertNotNull(key); assertEquals("RSA", key.getAlgorithm()); }
public String toString() { StringBuilder b = new StringBuilder(); Key k = KeyFactory.stringToKey(key); b.append("Question (").append(k.getId()).append(") : ").append(" Text=").append(questionText); b.append(" Date Posted=").append(datePosted); b.append(" ParentInfo = ").append(k.getParent().toString()); b.append("\n"); for (Comment c : comments) { b.append("\t\t").append(c.toString()).append("\n"); } return b.toString(); }
public static java.security.Signature sigMeth2SigSignatureInstance(Signature sig, Key key) throws DigiDocException { java.security.Signature instance = null; String sigMeth = null, sigType = null; try { if (sig != null && sig.getSignedInfo() != null && sig.getSignedInfo().getSignatureMethod() != null) sigMeth = sig.getSignedInfo().getSignatureMethod(); sigType = ConfigManager.instance().sigMeth2SigType(sigMeth, true); // allways use cvc ciphers if (m_logger.isDebugEnabled()) m_logger.debug( "Key: " + ((key != null) ? "OK, algorithm: " + key.getAlgorithm() : "NULL") + " method: " + sigMeth + " type: " + sigType); if (sigType == null) throw new DigiDocException( DigiDocException.ERR_SIGNATURE_METHOD, "SignatureMethod not specified!", null); instance = java.security.Signature.getInstance(sigType, ConfigManager.addProvider()); } catch (Exception ex) { m_logger.error("Error constructing signature instance: " + ex); } return instance; }
@NotNull @Override public Navigatable createNavigatable(@NotNull Project project) { if (ALTERNATIVE_SOURCE_KEY.get(myFile) != null) { return new OpenFileDescriptor(project, getFile(), getLine(), 0); } return XSourcePositionImpl.doCreateOpenFileDescriptor(project, this); }
@NotNull @Override public VirtualFile getFile() { VirtualFile file = ALTERNATIVE_SOURCE_KEY.get(myFile); if (file != null) { return file; } return myFile; }
/** * Assigns the given key to the given alias, protecting it with the given password. * * <p>If the given key is of type <code>java.security.PrivateKey</code>, it must be accompanied by * a certificate chain certifying the corresponding public key. * * <p>If the given alias already exists, the keystore information associated with it is overridden * by the given key (and possibly certificate chain). * * @param alias the alias name * @param key the key to be associated with the alias * @param password the password to protect the key * @param chain the certificate chain for the corresponding public key (only required if the given * key is of type <code>java.security.PrivateKey</code>). * @exception KeyStoreException if the given key cannot be protected, or this operation fails for * some other reason */ public void engineSetKeyEntry(String alias, Key key, char[] password, Certificate[] chain) throws KeyStoreException { permissionCheck(); synchronized (entries) { try { KeyEntry entry = new KeyEntry(); entry.date = new Date(); if (key instanceof PrivateKey) { if ((key.getFormat().equals("PKCS#8")) || (key.getFormat().equals("PKCS8"))) { entry.protectedPrivKey = encryptPrivateKey(key.getEncoded(), password); entry.password = password.clone(); } else { throw new KeyStoreException("Private key is not encoded as PKCS#8"); } } else { throw new KeyStoreException("Key is not a PrivateKey"); } // clone the chain if (chain != null) { if ((chain.length > 1) && !validateChain(chain)) { throw new KeyStoreException("Certificate chain does not validate"); } entry.chain = chain.clone(); entry.chainRefs = new long[entry.chain.length]; } String lowerAlias = alias.toLowerCase(); if (entries.get(lowerAlias) != null) { deletedEntries.put(lowerAlias, entries.get(lowerAlias)); } entries.put(lowerAlias, entry); addedEntries.put(lowerAlias, entry); } catch (Exception nsae) { KeyStoreException ke = new KeyStoreException("Key protection algorithm not found: " + nsae); ke.initCause(nsae); throw ke; } } }
private static <T extends Breakpoint> Element getCategoryGroupElement( @NotNull final Map<Key<? extends Breakpoint>, Element> categoryToElementMap, @NotNull final Key<T> category, @NotNull final Element parentNode) { Element group = categoryToElementMap.get(category); if (group == null) { group = new Element(category.toString()); categoryToElementMap.put(category, group); parentNode.addContent(group); } return group; }
public void setBreakpointDefaults( Key<? extends Breakpoint> category, BreakpointDefaults defaults) { Class typeCls = null; if (LineBreakpoint.CATEGORY.toString().equals(category.toString())) { typeCls = JavaLineBreakpointType.class; } else if (MethodBreakpoint.CATEGORY.toString().equals(category.toString())) { typeCls = JavaMethodBreakpointType.class; } else if (FieldBreakpoint.CATEGORY.toString().equals(category.toString())) { typeCls = JavaFieldBreakpointType.class; } else if (ExceptionBreakpoint.CATEGORY.toString().equals(category.toString())) { typeCls = JavaExceptionBreakpointType.class; } if (typeCls != null) { XBreakpointType<XBreakpoint<?>, ?> type = XDebuggerUtil.getInstance().findBreakpointType(typeCls); ((XBreakpointManagerImpl) getXBreakpointManager()) .getBreakpointDefaults(type) .setSuspendPolicy(Breakpoint.transformSuspendPolicy(defaults.getSuspendPolicy())); } // myBreakpointDefaults.put(category, defaults); }
/** * @return breakpoints of one of the category: LINE_BREAKPOINTS, EXCEPTION_BREAKPOINTS, * FIELD_BREAKPOINTS, METHOD_BREAKPOINTS */ public <T extends Breakpoint> Breakpoint[] getBreakpoints(@NotNull final Key<T> category) { ApplicationManager.getApplication().assertIsDispatchThread(); removeInvalidBreakpoints(); final ArrayList<Breakpoint> breakpoints = new ArrayList<Breakpoint>(); for (Breakpoint breakpoint : getBreakpoints()) { if (category.equals(breakpoint.getCategory())) { breakpoints.add(breakpoint); } } return breakpoints.toArray(new Breakpoint[breakpoints.size()]); }
/** @param category breakpoint category, null if the category does not matter */ @Nullable public <T extends BreakpointWithHighlighter> T findBreakpoint( final Document document, final int offset, @Nullable final Key<T> category) { for (final Breakpoint breakpoint : getBreakpoints()) { if (breakpoint instanceof BreakpointWithHighlighter && ((BreakpointWithHighlighter) breakpoint).isAt(document, offset)) { if (category == null || category.equals(breakpoint.getCategory())) { //noinspection CastConflictsWithInstanceof,unchecked return (T) breakpoint; } } } return null; }
@Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { String uri = getDecodedUri(request); try { String destination_frame = request.getParameter("destination_frame"); if (destination_frame == null) { destination_frame = "upload" + Key.rand(); } if (!validKeyName(destination_frame)) { setResponseStatus(response, HttpServletResponse.SC_BAD_REQUEST); response.getWriter().write("Invalid key name, contains illegal characters"); return; } // // Here is an example of how to upload a file from the command line. // // curl -v -F "file=@allyears2k_headers.zip" // "http://localhost:54321/3/PostFile.bin?destination_frame=a.zip" // // JSON Payload returned is: // { "destination_frame": "key_name", "total_bytes": nnn } // InputStream is = extractPartInputStream(request, response); if (is == null) { return; } UploadFileVec.ReadPutStats stats = new UploadFileVec.ReadPutStats(); UploadFileVec.readPut(destination_frame, is, stats); String responsePayload = "{ " + "\"destination_frame\": \"" + destination_frame + "\", " + "\"total_bytes\": " + stats.total_bytes + " " + "}\n"; response.setContentType("application/json"); response.getWriter().write(responsePayload); } catch (Exception e) { sendErrorResponse(response, e, uri); } finally { logRequest("POST", request, response); } }
@Override protected Key parse(String input) { if (_validator != null) _validator.validateRaw(input); Key k = Key.make(input); Value v = DKV.get(k); if (v == null && _mustExist) throw new IllegalArgumentException("Key '" + input + "' does not exist!"); if (_type != null) { if (v != null && !compatible(_type, v.get())) throw new IllegalArgumentException(input + ":" + errors()[0]); if (v == null && _required) throw new IllegalArgumentException("Key '" + input + "' does not exist!"); } return k; }
private static final class CompileStatistics { private static final Key<CompileStatistics> KEY = Key.create("_Compile_Statistics_"); private int myClassesCount; private int myFilesCount; public int getClassesCount() { return myClassesCount; } public int incClassesCount() { return ++myClassesCount; } public int getFilesCount() { return myFilesCount; } public int incFilesCount() { return ++myFilesCount; } }
@Override protected Frame rebalance(final Frame original_fr, boolean local, final String name) { if (original_fr == null) return null; if (_parms._force_load_balance) { int original_chunks = original_fr.anyVec().nChunks(); _job.update(0, "Load balancing " + name.substring(name.length() - 5) + " data..."); int chunks = desiredChunks(original_fr, local); if (!_parms._reproducible) { if (original_chunks >= chunks) { if (!_parms._quiet_mode) Log.info( "Dataset already contains " + original_chunks + " chunks. No need to rebalance."); return original_fr; } } else { // reproducible, set chunks to 1 assert chunks == 1; if (!_parms._quiet_mode) Log.warn("Reproducibility enforced - using only 1 thread - can be slow."); if (original_chunks == 1) return original_fr; } if (!_parms._quiet_mode) Log.info( "Rebalancing " + name.substring(name.length() - 5) + " dataset into " + chunks + " chunks."); Key newKey = Key.make(name + ".chks" + chunks); RebalanceDataSet rb = new RebalanceDataSet(original_fr, newKey, chunks); H2O.submitTask(rb).join(); Frame rebalanced_fr = DKV.get(newKey).get(); Scope.track(rebalanced_fr); return rebalanced_fr; } return original_fr; }
public class GeneralHighlightingPass extends ProgressableTextEditorHighlightingPass implements DumbAware { private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.daemon.impl.GeneralHighlightingPass"); static final String PRESENTABLE_NAME = DaemonBundle.message("pass.syntax"); private static final Key<Boolean> HAS_ERROR_ELEMENT = Key.create("HAS_ERROR_ELEMENT"); private final int myStartOffset; private final int myEndOffset; private final boolean myUpdateAll; private final ProperTextRange myPriorityRange; private final Editor myEditor; private final List<HighlightInfo> myHighlights = new ArrayList<HighlightInfo>(); protected volatile boolean myHasErrorElement; private volatile boolean myErrorFound; private static final Comparator<HighlightVisitor> VISITOR_ORDER_COMPARATOR = new Comparator<HighlightVisitor>() { @Override public int compare(final HighlightVisitor o1, final HighlightVisitor o2) { return o1.order() - o2.order(); } }; private Runnable myApplyCommand; private final EditorColorsScheme myGlobalScheme; private boolean myFailFastOnAcquireReadAction = true; public GeneralHighlightingPass( @NotNull Project project, @NotNull PsiFile file, @NotNull Document document, int startOffset, int endOffset, boolean updateAll) { this( project, file, document, startOffset, endOffset, updateAll, new ProperTextRange(0, document.getTextLength()), null); } public GeneralHighlightingPass( @NotNull Project project, @NotNull PsiFile file, @NotNull Document document, int startOffset, int endOffset, boolean updateAll, @NotNull ProperTextRange priorityRange, @Nullable Editor editor) { super(project, document, PRESENTABLE_NAME, file, true); myStartOffset = startOffset; myEndOffset = endOffset; myUpdateAll = updateAll; myPriorityRange = priorityRange; myEditor = editor; LOG.assertTrue(file.isValid()); setId(Pass.UPDATE_ALL); myHasErrorElement = !isWholeFileHighlighting() && Boolean.TRUE.equals(myFile.getUserData(HAS_ERROR_ELEMENT)); FileStatusMap fileStatusMap = ((DaemonCodeAnalyzerImpl) DaemonCodeAnalyzer.getInstance(myProject)).getFileStatusMap(); myErrorFound = !isWholeFileHighlighting() && fileStatusMap.wasErrorFound(myDocument); myApplyCommand = new Runnable() { @Override public void run() { ProperTextRange range = new ProperTextRange(myStartOffset, myEndOffset); MarkupModel model = DocumentMarkupModel.forDocument(myDocument, myProject, true); UpdateHighlightersUtil.cleanFileLevelHighlights(myProject, Pass.UPDATE_ALL, myFile); final EditorColorsScheme colorsScheme = getColorsScheme(); UpdateHighlightersUtil.setHighlightersInRange( myProject, myDocument, range, colorsScheme, myHighlights, (MarkupModelEx) model, Pass.UPDATE_ALL); } }; // initial guess to show correct progress in the traffic light icon setProgressLimit(document.getTextLength() / 2); // approx number of PSI elements = file length/2 myGlobalScheme = EditorColorsManager.getInstance().getGlobalScheme(); } private static final Key<AtomicInteger> HIGHLIGHT_VISITOR_INSTANCE_COUNT = new Key<AtomicInteger>("HIGHLIGHT_VISITOR_INSTANCE_COUNT"); @NotNull private HighlightVisitor[] getHighlightVisitors() { int oldCount = incVisitorUsageCount(1); HighlightVisitor[] highlightVisitors = createHighlightVisitors(); if (oldCount != 0) { HighlightVisitor[] clones = new HighlightVisitor[highlightVisitors.length]; for (int i = 0; i < highlightVisitors.length; i++) { HighlightVisitor highlightVisitor = highlightVisitors[i]; clones[i] = highlightVisitor.clone(); } highlightVisitors = clones; } return highlightVisitors; } protected HighlightVisitor[] createHighlightVisitors() { return Extensions.getExtensions(HighlightVisitor.EP_HIGHLIGHT_VISITOR, myProject); } // returns old value private int incVisitorUsageCount(int delta) { AtomicInteger count = myProject.getUserData(HIGHLIGHT_VISITOR_INSTANCE_COUNT); if (count == null) { count = ((UserDataHolderEx) myProject) .putUserDataIfAbsent(HIGHLIGHT_VISITOR_INSTANCE_COUNT, new AtomicInteger(0)); } int old = count.getAndAdd(delta); assert old + delta >= 0 : old + ";" + delta; return old; } @Override protected void collectInformationWithProgress(final ProgressIndicator progress) { final Set<HighlightInfo> gotHighlights = new THashSet<HighlightInfo>(100); final Set<HighlightInfo> outsideResult = new THashSet<HighlightInfo>(100); DaemonCodeAnalyzer daemonCodeAnalyzer = DaemonCodeAnalyzer.getInstance(myProject); HighlightVisitor[] highlightVisitors = getHighlightVisitors(); final HighlightVisitor[] filteredVisitors = filterVisitors(highlightVisitors, myFile); final List<PsiElement> inside = new ArrayList<PsiElement>(); final List<PsiElement> outside = new ArrayList<PsiElement>(); try { Divider.divideInsideAndOutside( myFile, myStartOffset, myEndOffset, myPriorityRange, inside, outside, HighlightLevelUtil.AnalysisLevel.HIGHLIGHT, false); setProgressLimit((long) (inside.size() + outside.size())); final boolean forceHighlightParents = forceHighlightParents(); if (!isDumbMode()) { highlightTodos( myFile, myDocument.getCharsSequence(), myStartOffset, myEndOffset, progress, myPriorityRange, gotHighlights, outsideResult); } collectHighlights( inside, new Runnable() { @Override public void run() { // all infos for the "injected fragment for the host which is inside" are indeed // inside // but some of the infos for the "injected fragment for the host which is outside" can // be still inside Set<HighlightInfo> injectedResult = new THashSet<HighlightInfo>(); final Set<PsiFile> injected = new THashSet<PsiFile>(); getInjectedPsiFiles(inside, outside, progress, injected); if (!addInjectedPsiHighlights( injected, progress, Collections.synchronizedSet(injectedResult))) throw new ProcessCanceledException(); final List<HighlightInfo> injectionsOutside = new ArrayList<HighlightInfo>(gotHighlights.size()); Set<HighlightInfo> result; synchronized (injectedResult) { // sync here because all writes happened in another thread result = injectedResult; } for (HighlightInfo info : result) { if (myPriorityRange.containsRange(info.getStartOffset(), info.getEndOffset())) { gotHighlights.add(info); } else { // nonconditionally apply injected results regardless whether they are in // myStartOffset,myEndOffset injectionsOutside.add(info); } } if (outsideResult.isEmpty() && injectionsOutside.isEmpty()) { return; // apply only result (by default apply command) and only within inside } final ProperTextRange priorityIntersection = myPriorityRange.intersection(new TextRange(myStartOffset, myEndOffset)); if ((!inside.isEmpty() || !gotHighlights.isEmpty()) && priorityIntersection != null) { // do not apply when there were no elements to highlight // clear infos found in visible area to avoid applying them twice final List<HighlightInfo> toApplyInside = new ArrayList<HighlightInfo>(gotHighlights); myHighlights.addAll(toApplyInside); gotHighlights.clear(); gotHighlights.addAll(outsideResult); final long modificationStamp = myDocument.getModificationStamp(); UIUtil.invokeLaterIfNeeded( new Runnable() { @Override public void run() { if (myProject.isDisposed() || modificationStamp != myDocument.getModificationStamp()) return; MarkupModel markupModel = DocumentMarkupModel.forDocument(myDocument, myProject, true); UpdateHighlightersUtil.setHighlightersInRange( myProject, myDocument, priorityIntersection, getColorsScheme(), toApplyInside, (MarkupModelEx) markupModel, Pass.UPDATE_ALL); if (myEditor != null) { new ShowAutoImportPass(myProject, myFile, myEditor) .applyInformationToEditor(); } } }); } myApplyCommand = new Runnable() { @Override public void run() { ProperTextRange range = new ProperTextRange(myStartOffset, myEndOffset); List<HighlightInfo> toApply = new ArrayList<HighlightInfo>(); for (HighlightInfo info : gotHighlights) { if (!range.containsRange(info.getStartOffset(), info.getEndOffset())) continue; if (!myPriorityRange.containsRange( info.getStartOffset(), info.getEndOffset())) { toApply.add(info); } } toApply.addAll(injectionsOutside); UpdateHighlightersUtil.setHighlightersOutsideRange( myProject, myDocument, toApply, getColorsScheme(), myStartOffset, myEndOffset, myPriorityRange, Pass.UPDATE_ALL); } }; } }, outside, progress, filteredVisitors, gotHighlights, forceHighlightParents); if (myUpdateAll) { ((DaemonCodeAnalyzerImpl) daemonCodeAnalyzer) .getFileStatusMap() .setErrorFoundFlag(myDocument, myErrorFound); } } finally { incVisitorUsageCount(-1); } myHighlights.addAll(gotHighlights); } private void getInjectedPsiFiles( @NotNull final List<PsiElement> elements1, @NotNull final List<PsiElement> elements2, @NotNull final ProgressIndicator progress, @NotNull final Set<PsiFile> outInjected) { List<DocumentWindow> injected = InjectedLanguageUtil.getCachedInjectedDocuments(myFile); Collection<PsiElement> hosts = new THashSet<PsiElement>(elements1.size() + elements2.size() + injected.size()); // rehighlight all injected PSI regardless the range, // since change in one place can lead to invalidation of injected PSI in (completely) other // place. for (DocumentWindow documentRange : injected) { progress.checkCanceled(); if (!documentRange.isValid()) continue; PsiFile file = PsiDocumentManager.getInstance(myProject).getPsiFile(documentRange); if (file == null) continue; PsiElement context = file.getContext(); if (context != null && context.isValid() && !file.getProject().isDisposed() && (myUpdateAll || new ProperTextRange(myStartOffset, myEndOffset) .intersects(context.getTextRange()))) { hosts.add(context); } } hosts.addAll(elements1); hosts.addAll(elements2); final PsiLanguageInjectionHost.InjectedPsiVisitor visitor = new PsiLanguageInjectionHost.InjectedPsiVisitor() { @Override public void visit( @NotNull PsiFile injectedPsi, @NotNull List<PsiLanguageInjectionHost.Shred> places) { synchronized (outInjected) { outInjected.add(injectedPsi); } } }; if (!JobUtil.invokeConcurrentlyUnderProgress( new ArrayList<PsiElement>(hosts), progress, false, new Processor<PsiElement>() { @Override public boolean process(PsiElement element) { progress.checkCanceled(); InjectedLanguageUtil.enumerate(element, myFile, false, visitor); return true; } })) throw new ProcessCanceledException(); } // returns false if canceled private boolean addInjectedPsiHighlights( @NotNull final Set<PsiFile> injectedFiles, @NotNull final ProgressIndicator progress, @NotNull final Collection<HighlightInfo> outInfos) { if (injectedFiles.isEmpty()) return true; final InjectedLanguageManager injectedLanguageManager = InjectedLanguageManager.getInstance(myProject); final TextAttributes injectedAttributes = myGlobalScheme.getAttributes(EditorColors.INJECTED_LANGUAGE_FRAGMENT); return JobUtil.invokeConcurrentlyUnderProgress( new ArrayList<PsiFile>(injectedFiles), progress, myFailFastOnAcquireReadAction, new Processor<PsiFile>() { @Override public boolean process(final PsiFile injectedPsi) { DocumentWindow documentWindow = (DocumentWindow) PsiDocumentManager.getInstance(myProject).getCachedDocument(injectedPsi); if (documentWindow == null) return true; Place places = InjectedLanguageUtil.getShreds(injectedPsi); for (PsiLanguageInjectionHost.Shred place : places) { TextRange textRange = place.getRangeInsideHost().shiftRight(place.host.getTextRange().getStartOffset()); if (textRange.isEmpty()) continue; String desc = injectedPsi.getLanguage().getDisplayName() + ": " + injectedPsi.getText(); HighlightInfo info = HighlightInfo.createHighlightInfo( HighlightInfoType.INJECTED_LANGUAGE_FRAGMENT, textRange, null, desc, injectedAttributes); info.fromInjection = true; outInfos.add(info); } HighlightInfoHolder holder = createInfoHolder(injectedPsi); runHighlightVisitorsForInjected(injectedPsi, holder, progress); for (int i = 0; i < holder.size(); i++) { HighlightInfo info = holder.get(i); final int startOffset = documentWindow.injectedToHost(info.startOffset); final TextRange fixedTextRange = getFixedTextRange(documentWindow, startOffset); addPatchedInfos( info, injectedPsi, documentWindow, injectedLanguageManager, fixedTextRange, outInfos); } holder.clear(); highlightInjectedSyntax(injectedPsi, holder); for (int i = 0; i < holder.size(); i++) { HighlightInfo info = holder.get(i); final int startOffset = info.startOffset; final TextRange fixedTextRange = getFixedTextRange(documentWindow, startOffset); if (fixedTextRange == null) { info.fromInjection = true; outInfos.add(info); } else { HighlightInfo patched = new HighlightInfo( info.forcedTextAttributes, info.forcedTextAttributesKey, info.type, fixedTextRange.getStartOffset(), fixedTextRange.getEndOffset(), info.description, info.toolTip, info.type.getSeverity(null), info.isAfterEndOfLine, null, false); patched.fromInjection = true; outInfos.add(patched); } } if (!isDumbMode()) { List<HighlightInfo> todos = new ArrayList<HighlightInfo>(); highlightTodos( injectedPsi, injectedPsi.getText(), 0, injectedPsi.getTextLength(), progress, myPriorityRange, todos, todos); for (HighlightInfo info : todos) { addPatchedInfos( info, injectedPsi, documentWindow, injectedLanguageManager, null, outInfos); } } return true; } }); } private static TextRange getFixedTextRange( @NotNull DocumentWindow documentWindow, int startOffset) { final TextRange fixedTextRange; TextRange textRange = documentWindow.getHostRange(startOffset); if (textRange == null) { // todo[cdr] check this fix. prefix/suffix code annotation case textRange = findNearestTextRange(documentWindow, startOffset); final boolean isBefore = startOffset < textRange.getStartOffset(); fixedTextRange = new ProperTextRange( isBefore ? textRange.getStartOffset() - 1 : textRange.getEndOffset(), isBefore ? textRange.getStartOffset() : textRange.getEndOffset() + 1); } else { fixedTextRange = null; } return fixedTextRange; } private static void addPatchedInfos( @NotNull HighlightInfo info, @NotNull PsiFile injectedPsi, @NotNull DocumentWindow documentWindow, @NotNull InjectedLanguageManager injectedLanguageManager, @Nullable TextRange fixedTextRange, @NotNull Collection<HighlightInfo> out) { ProperTextRange textRange = new ProperTextRange(info.startOffset, info.endOffset); List<TextRange> editables = injectedLanguageManager.intersectWithAllEditableFragments(injectedPsi, textRange); for (TextRange editable : editables) { TextRange hostRange = fixedTextRange == null ? documentWindow.injectedToHost(editable) : fixedTextRange; boolean isAfterEndOfLine = info.isAfterEndOfLine; if (isAfterEndOfLine) { // convert injected afterEndOfLine to either host' afterEndOfLine or not-afterEndOfLine // highlight of the injected fragment boundary int hostEndOffset = hostRange.getEndOffset(); int lineNumber = documentWindow.getDelegate().getLineNumber(hostEndOffset); int hostLineEndOffset = documentWindow.getDelegate().getLineEndOffset(lineNumber); if (hostEndOffset < hostLineEndOffset) { // convert to non-afterEndOfLine isAfterEndOfLine = false; hostRange = new ProperTextRange(hostRange.getStartOffset(), hostEndOffset + 1); } } HighlightInfo patched = new HighlightInfo( info.forcedTextAttributes, info.forcedTextAttributesKey, info.type, hostRange.getStartOffset(), hostRange.getEndOffset(), info.description, info.toolTip, info.type.getSeverity(null), isAfterEndOfLine, null, false); patched.setHint(info.hasHint()); patched.setGutterIconRenderer(info.getGutterIconRenderer()); if (info.quickFixActionRanges != null) { for (Pair<HighlightInfo.IntentionActionDescriptor, TextRange> pair : info.quickFixActionRanges) { TextRange quickfixTextRange = pair.getSecond(); List<TextRange> editableQF = injectedLanguageManager.intersectWithAllEditableFragments( injectedPsi, quickfixTextRange); for (TextRange editableRange : editableQF) { HighlightInfo.IntentionActionDescriptor descriptor = pair.getFirst(); if (patched.quickFixActionRanges == null) patched.quickFixActionRanges = new ArrayList<Pair<HighlightInfo.IntentionActionDescriptor, TextRange>>(); TextRange hostEditableRange = documentWindow.injectedToHost(editableRange); patched.quickFixActionRanges.add(Pair.create(descriptor, hostEditableRange)); } } } patched.fromInjection = true; out.add(patched); } } // finds the first nearest text range private static TextRange findNearestTextRange( final DocumentWindow documentWindow, final int startOffset) { TextRange textRange = null; for (RangeMarker marker : documentWindow.getHostRanges()) { TextRange curRange = ProperTextRange.create(marker); if (curRange.getStartOffset() > startOffset && textRange != null) break; textRange = curRange; } assert textRange != null; return textRange; } private void runHighlightVisitorsForInjected( @NotNull PsiFile injectedPsi, @NotNull final HighlightInfoHolder holder, @NotNull final ProgressIndicator progress) { HighlightVisitor[] visitors = getHighlightVisitors(); try { HighlightVisitor[] filtered = filterVisitors(visitors, injectedPsi); final List<PsiElement> elements = CollectHighlightsUtil.getElementsInRange(injectedPsi, 0, injectedPsi.getTextLength()); for (final HighlightVisitor visitor : filtered) { visitor.analyze( injectedPsi, true, holder, new Runnable() { @Override public void run() { for (PsiElement element : elements) { progress.checkCanceled(); visitor.visit(element); } } }); } } finally { incVisitorUsageCount(-1); } } private void highlightInjectedSyntax(final PsiFile injectedPsi, HighlightInfoHolder holder) { List<Trinity<IElementType, PsiLanguageInjectionHost, TextRange>> tokens = InjectedLanguageUtil.getHighlightTokens(injectedPsi); if (tokens == null) return; final Language injectedLanguage = injectedPsi.getLanguage(); Project project = injectedPsi.getProject(); SyntaxHighlighter syntaxHighlighter = SyntaxHighlighterFactory.getSyntaxHighlighter( injectedLanguage, project, injectedPsi.getVirtualFile()); final TextAttributes defaultAttrs = myGlobalScheme.getAttributes(HighlighterColors.TEXT); for (Trinity<IElementType, PsiLanguageInjectionHost, TextRange> token : tokens) { ProgressManager.checkCanceled(); IElementType tokenType = token.getFirst(); PsiLanguageInjectionHost injectionHost = token.getSecond(); TextRange textRange = token.getThird(); TextAttributesKey[] keys = syntaxHighlighter.getTokenHighlights(tokenType); if (textRange.getLength() == 0) continue; TextRange annRange = textRange.shiftRight(injectionHost.getTextRange().getStartOffset()); // force attribute colors to override host' ones TextAttributes attributes = null; for (TextAttributesKey key : keys) { TextAttributes attrs2 = myGlobalScheme.getAttributes(key); if (attrs2 != null) { attributes = attributes == null ? attrs2 : TextAttributes.merge(attributes, attrs2); } } TextAttributes forcedAttributes; if (attributes == null || attributes.isEmpty() || attributes.equals(defaultAttrs)) { forcedAttributes = TextAttributes.ERASE_MARKER; } else { Color back = attributes.getBackgroundColor() == null ? myGlobalScheme.getDefaultBackground() : attributes.getBackgroundColor(); Color fore = attributes.getForegroundColor() == null ? myGlobalScheme.getDefaultForeground() : attributes.getForegroundColor(); forcedAttributes = new TextAttributes( fore, back, attributes.getEffectColor(), attributes.getEffectType(), attributes.getFontType()); } HighlightInfo info = HighlightInfo.createHighlightInfo( HighlightInfoType.INJECTED_LANGUAGE_FRAGMENT, annRange, null, null, forcedAttributes); holder.add(info); } } private boolean isWholeFileHighlighting() { return myUpdateAll && myStartOffset == 0 && myEndOffset == myDocument.getTextLength(); } @Override protected void applyInformationWithProgress() { myFile.putUserData(HAS_ERROR_ELEMENT, myHasErrorElement); myApplyCommand.run(); if (myUpdateAll) { reportErrorsToWolf(); } } @Override @NotNull public List<HighlightInfo> getInfos() { return new ArrayList<HighlightInfo>(myHighlights); } private void collectHighlights( @NotNull final List<PsiElement> elements1, @NotNull final Runnable after1, @NotNull final List<PsiElement> elements2, @NotNull final ProgressIndicator progress, @NotNull final HighlightVisitor[] visitors, @NotNull final Set<HighlightInfo> gotHighlights, final boolean forceHighlightParents) { final Set<PsiElement> skipParentsSet = new THashSet<PsiElement>(); // TODO - add color scheme to holder final HighlightInfoHolder holder = createInfoHolder(myFile); final int chunkSize = Math.max(1, (elements1.size() + elements2.size()) / 100); // one percent precision is enough final Runnable action = new Runnable() { @Override public void run() { //noinspection unchecked boolean failed = false; for (List<PsiElement> elements : new List[] {elements1, elements2}) { int nextLimit = chunkSize; for (int i = 0; i < elements.size(); i++) { PsiElement element = elements.get(i); progress.checkCanceled(); if (element != myFile && !skipParentsSet.isEmpty() && element.getFirstChild() != null && skipParentsSet.contains(element)) { skipParentsSet.add(element.getParent()); continue; } if (element instanceof PsiErrorElement) { myHasErrorElement = true; } holder.clear(); for (final HighlightVisitor visitor : visitors) { try { visitor.visit(element); } catch (ProcessCanceledException e) { throw e; } catch (IndexNotReadyException e) { throw e; } catch (WolfTheProblemSolverImpl.HaveGotErrorException e) { throw e; } catch (Exception e) { if (!failed) { LOG.error(e); } failed = true; } } if (i == nextLimit) { advanceProgress(chunkSize); nextLimit = i + chunkSize; } //noinspection ForLoopReplaceableByForEach for (int j = 0; j < holder.size(); j++) { final HighlightInfo info = holder.get(j); assert info != null; // have to filter out already obtained highlights if (!gotHighlights.add(info)) continue; boolean isError = info.getSeverity() == HighlightSeverity.ERROR; if (isError) { if (!forceHighlightParents) { skipParentsSet.add(element.getParent()); } myErrorFound = true; } myTransferToEDTQueue.offer(Pair.create(info, progress)); } } advanceProgress(elements.size() - (nextLimit - chunkSize)); if (elements == elements1) after1.run(); } } }; analyzeByVisitors(progress, visitors, holder, 0, action); } // private void collectHighlights(@NotNull final List<PsiElement> elements1, // @NotNull final Runnable after1, // @NotNull final List<PsiElement> elements2, // @NotNull final ProgressIndicator progress, // @NotNull final HighlightVisitor[] visitors, // @NotNull final Set<HighlightInfo> gotHighlights, // final boolean forceHighlightParents) { // final HighlightInfoHolder holder = createInfoHolder(myFile); // // final int chunkSize = Math.max(1, (elements1.size()+elements2.size()) / 100); // one percent // precision is enough // // final Runnable action = new Runnable() { // public void run() { // //noinspection unchecked // boolean failed = false; // PsiNodeTask task = createPsiTask(myFile); // JobSchedulerImpl.submitTask(task); // // for (List<PsiElement> elements : new List[]{elements1, elements2}) { // int nextLimit = chunkSize; // for (int i = 0; i < elements.size(); i++) { // PsiElement element = elements.get(i); // progress.checkCanceled(); // // if (element != myFile && !skipParentsSet.isEmpty() && element.getFirstChild() != null // && skipParentsSet.contains(element)) { // skipParentsSet.add(element.getParent()); // continue; // } // // if (element instanceof PsiErrorElement) { // myHasErrorElement = true; // } // kjlhkjh // if (i == nextLimit) { // advanceProgress(chunkSize); // nextLimit = i + chunkSize; // } // // } // advanceProgress(elements.size() - (nextLimit-chunkSize)); // if (elements == elements1) after1.run(); // } // } // // private PsiNodeTask createPsiTask(@NotNull PsiElement root) { // return new PsiNodeTask(root) { // @Override // public void onEnter(@NotNull PsiElement element) { // if (element instanceof PsiErrorElement) { // myHasErrorElement = true; // } // super.onEnter(element); // } // // @Override // protected PsiNodeTask forkNode(@NotNull PsiElement child) { // return createPsiTask(child); // } // // @Override // protected boolean highlight(PsiElement element) { // holder.clear(); // // for (final HighlightVisitor visitor : visitors) { // try { // visitor.visit(element); // } // catch (ProcessCanceledException e) { // throw e; // } // catch (IndexNotReadyException e) { // throw e; // } // catch (WolfTheProblemSolverImpl.HaveGotErrorException e) { // throw e; // } // catch (Exception e) { // LOG.error(e); // } // } // // //noinspection ForLoopReplaceableByForEach // for (int j = 0; j < holder.size(); j++) { // final HighlightInfo info = holder.get(j); // assert info != null; // // have to filter out already obtained highlights // if (!gotHighlights.add(info)) continue; // boolean isError = info.getSeverity() == HighlightSeverity.ERROR; // if (isError) { // if (!forceHighlightParents) { // skipParentsSet.add(element.getParent()); // } // myErrorFound = true; // } // myTransferToEDTQueue.offer(Pair.create(info, progress)); // } // return true; // } // }; // } // }; // // analyzeByVisitors(progress, visitors, holder, 0, action); // } private final Map<TextRange, RangeMarker> ranges2markersCache = new THashMap<TextRange, RangeMarker>(); private final TransferToEDTQueue<Pair<HighlightInfo, ProgressIndicator>> myTransferToEDTQueue = new TransferToEDTQueue<Pair<HighlightInfo, ProgressIndicator>>( "Apply highlighting results", new Processor<Pair<HighlightInfo, ProgressIndicator>>() { @Override public boolean process(Pair<HighlightInfo, ProgressIndicator> pair) { ApplicationManager.getApplication().assertIsDispatchThread(); ProgressIndicator indicator = pair.getSecond(); if (indicator.isCanceled()) { return false; } HighlightInfo info = pair.getFirst(); final EditorColorsScheme colorsScheme = getColorsScheme(); UpdateHighlightersUtil.addHighlighterToEditorIncrementally( myProject, myDocument, myFile, myStartOffset, myEndOffset, info, colorsScheme, Pass.UPDATE_ALL, ranges2markersCache); return true; } }, myProject.getDisposed(), 200); private void analyzeByVisitors( @NotNull final ProgressIndicator progress, @NotNull final HighlightVisitor[] visitors, @NotNull final HighlightInfoHolder holder, final int i, @NotNull final Runnable action) { if (i == visitors.length) { action.run(); } else { if (!visitors[i].analyze( myFile, myUpdateAll, holder, new Runnable() { @Override public void run() { analyzeByVisitors(progress, visitors, holder, i + 1, action); } })) { cancelAndRestartDaemonLater(progress, myProject, this); } } } private static HighlightVisitor[] filterVisitors( HighlightVisitor[] highlightVisitors, final PsiFile file) { final List<HighlightVisitor> visitors = new ArrayList<HighlightVisitor>(highlightVisitors.length); List<HighlightVisitor> list = Arrays.asList(highlightVisitors); for (HighlightVisitor visitor : DumbService.getInstance(file.getProject()).filterByDumbAwareness(list)) { if (visitor.suitableForFile(file)) visitors.add(visitor); } LOG.assertTrue(!visitors.isEmpty(), list); HighlightVisitor[] visitorArray = visitors.toArray(new HighlightVisitor[visitors.size()]); Arrays.sort(visitorArray, VISITOR_ORDER_COMPARATOR); return visitorArray; } static Void cancelAndRestartDaemonLater( ProgressIndicator progress, final Project project, TextEditorHighlightingPass pass) { PassExecutorService.log(progress, pass, "Cancel and restart"); progress.cancel(); ApplicationManager.getApplication() .invokeLater( new Runnable() { @Override public void run() { try { Thread.sleep(new Random().nextInt(100)); } catch (InterruptedException e) { LOG.error(e); } DaemonCodeAnalyzer.getInstance(project).restart(); } }, project.getDisposed()); throw new ProcessCanceledException(); } private boolean forceHighlightParents() { boolean forceHighlightParents = false; for (HighlightRangeExtension extension : Extensions.getExtensions(HighlightRangeExtension.EP_NAME)) { if (extension.isForceHighlightParents(myFile)) { forceHighlightParents = true; break; } } return forceHighlightParents; } protected HighlightInfoHolder createInfoHolder(final PsiFile file) { final HighlightInfoFilter[] filters = ApplicationManager.getApplication().getExtensions(HighlightInfoFilter.EXTENSION_POINT_NAME); return new HighlightInfoHolder(file, getColorsScheme(), filters); } private static void highlightTodos( @NotNull PsiFile file, @NotNull CharSequence text, int startOffset, int endOffset, @NotNull ProgressIndicator progress, @NotNull ProperTextRange priorityRange, @NotNull Collection<HighlightInfo> result, @NotNull Collection<HighlightInfo> outsideResult) { PsiSearchHelper helper = PsiSearchHelper.SERVICE.getInstance(file.getProject()); TodoItem[] todoItems = helper.findTodoItems(file, startOffset, endOffset); if (todoItems.length == 0) return; for (TodoItem todoItem : todoItems) { progress.checkCanceled(); TextRange range = todoItem.getTextRange(); String description = text.subSequence(range.getStartOffset(), range.getEndOffset()).toString(); TextAttributes attributes = todoItem.getPattern().getAttributes().getTextAttributes(); HighlightInfo info = HighlightInfo.createHighlightInfo( HighlightInfoType.TODO, range, description, description, attributes); assert info != null; if (priorityRange.containsRange(info.getStartOffset(), info.getEndOffset())) { result.add(info); } else { outsideResult.add(info); } } } private void reportErrorsToWolf() { if (!myFile.getViewProvider().isPhysical()) return; // e.g. errors in evaluate expression Project project = myFile.getProject(); if (!PsiManager.getInstance(project).isInProject(myFile)) return; // do not report problems in libraries VirtualFile file = myFile.getVirtualFile(); if (file == null) return; List<Problem> problems = convertToProblems(getInfos(), file, myHasErrorElement); WolfTheProblemSolver wolf = WolfTheProblemSolver.getInstance(project); boolean hasErrors = DaemonCodeAnalyzerImpl.hasErrors(project, getDocument()); if (!hasErrors || isWholeFileHighlighting()) { wolf.reportProblems(file, problems); } else { wolf.weHaveGotProblems(file, problems); } } @Override public double getProgress() { // do not show progress of visible highlighters update return myUpdateAll ? super.getProgress() : -1; } private static List<Problem> convertToProblems( final Collection<HighlightInfo> infos, final VirtualFile file, final boolean hasErrorElement) { List<Problem> problems = new SmartList<Problem>(); for (HighlightInfo info : infos) { if (info.getSeverity() == HighlightSeverity.ERROR) { Problem problem = new ProblemImpl(file, info, hasErrorElement); problems.add(problem); } } return problems; } @Override public String toString() { return super.toString() + " updateAll=" + myUpdateAll + " range=(" + myStartOffset + "," + myEndOffset + ")"; } public void setFailFastOnAcquireReadAction(boolean failFastOnAcquireReadAction) { myFailFastOnAcquireReadAction = failFastOnAcquireReadAction; } }
class DocumentFoldingInfo implements JDOMExternalizable, CodeFoldingState { private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.folding.impl.DocumentFoldingInfo"); private static final Key<FoldingInfo> FOLDING_INFO_KEY = Key.create("FOLDING_INFO"); @NotNull private final Project myProject; private final VirtualFile myFile; private static class SerializedPsiElement { private final String mySerializedElement; private final FoldingInfo myFoldingInfo; public SerializedPsiElement(@NotNull String serialized, @NotNull FoldingInfo foldingInfo) { mySerializedElement = serialized; myFoldingInfo = foldingInfo; } } @NotNull private final List<SmartPsiElementPointer<PsiElement>> myPsiElements = ContainerUtil.createLockFreeCopyOnWriteList(); @NotNull private final List<SerializedPsiElement> mySerializedElements = ContainerUtil.createLockFreeCopyOnWriteList(); @NotNull private final List<RangeMarker> myRangeMarkers = ContainerUtil.createLockFreeCopyOnWriteList(); private static final String DEFAULT_PLACEHOLDER = "..."; @NonNls private static final String ELEMENT_TAG = "element"; @NonNls private static final String SIGNATURE_ATT = "signature"; @NonNls private static final String EXPANDED_ATT = "expanded"; @NonNls private static final String MARKER_TAG = "marker"; @NonNls private static final String DATE_ATT = "date"; @NonNls private static final String PLACEHOLDER_ATT = "placeholder"; DocumentFoldingInfo(@NotNull Project project, @NotNull Document document) { myProject = project; myFile = FileDocumentManager.getInstance().getFile(document); } void loadFromEditor(@NotNull Editor editor) { assertDispatchThread(); LOG.assertTrue(!editor.isDisposed()); clear(); PsiDocumentManager documentManager = PsiDocumentManager.getInstance(myProject); documentManager.commitDocument(editor.getDocument()); PsiFile file = documentManager.getPsiFile(editor.getDocument()); SmartPointerManager smartPointerManager = SmartPointerManager.getInstance(myProject); EditorFoldingInfo info = EditorFoldingInfo.get(editor); FoldRegion[] foldRegions = editor.getFoldingModel().getAllFoldRegions(); for (FoldRegion region : foldRegions) { if (!region.isValid()) continue; PsiElement element = info.getPsiElement(region); boolean expanded = region.isExpanded(); boolean collapseByDefault = element != null && FoldingPolicy.isCollapseByDefault(element) && !FoldingUtil.caretInsideRange(editor, TextRange.create(region)); if (collapseByDefault == expanded || element == null) { FoldingInfo fi = new FoldingInfo(region.getPlaceholderText(), expanded); if (element != null) { myPsiElements.add(smartPointerManager.createSmartPsiElementPointer(element, file)); element.putUserData(FOLDING_INFO_KEY, fi); } else if (region.isValid()) { myRangeMarkers.add(region); region.putUserData(FOLDING_INFO_KEY, fi); } } } } private static void assertDispatchThread() { ApplicationManagerEx.getApplicationEx().assertIsDispatchThread(); } @Override public void setToEditor(@NotNull final Editor editor) { assertDispatchThread(); final PsiManager psiManager = PsiManager.getInstance(myProject); if (psiManager.isDisposed()) return; if (!myFile.isValid()) return; final PsiFile psiFile = psiManager.findFile(myFile); if (psiFile == null) return; if (!mySerializedElements.isEmpty()) { // Restore postponed state assert myPsiElements.isEmpty() : "Sequential deserialization"; for (SerializedPsiElement entry : mySerializedElements) { PsiElement restoredElement = FoldingPolicy.restoreBySignature(psiFile, entry.mySerializedElement); if (restoredElement != null && restoredElement.isValid()) { myPsiElements.add( SmartPointerManager.getInstance(myProject) .createSmartPsiElementPointer(restoredElement)); restoredElement.putUserData(FOLDING_INFO_KEY, entry.myFoldingInfo); } } mySerializedElements.clear(); } Map<PsiElement, FoldingDescriptor> ranges = null; for (SmartPsiElementPointer<PsiElement> ptr : myPsiElements) { PsiElement element = ptr.getElement(); if (element == null || !element.isValid()) { continue; } if (ranges == null) { ranges = buildRanges(editor, psiFile); } FoldingDescriptor descriptor = ranges.get(element); if (descriptor == null) { continue; } TextRange range = descriptor.getRange(); FoldRegion region = FoldingUtil.findFoldRegion(editor, range.getStartOffset(), range.getEndOffset()); if (region != null) { FoldingInfo fi = element.getUserData(FOLDING_INFO_KEY); boolean state = fi != null && fi.expanded; region.setExpanded(state); } } for (RangeMarker marker : myRangeMarkers) { if (!marker.isValid()) { continue; } FoldRegion region = FoldingUtil.findFoldRegion(editor, marker.getStartOffset(), marker.getEndOffset()); FoldingInfo info = marker.getUserData(FOLDING_INFO_KEY); if (region == null) { if (info != null) { region = editor .getFoldingModel() .addFoldRegion(marker.getStartOffset(), marker.getEndOffset(), info.placeHolder); } if (region == null) { return; } } boolean state = info != null && info.expanded; region.setExpanded(state); } } @NotNull private static Map<PsiElement, FoldingDescriptor> buildRanges( @NotNull Editor editor, @NotNull PsiFile psiFile) { final FoldingBuilder foldingBuilder = LanguageFolding.INSTANCE.forLanguage(psiFile.getLanguage()); final ASTNode node = psiFile.getNode(); if (node == null) return Collections.emptyMap(); final FoldingDescriptor[] descriptors = LanguageFolding.buildFoldingDescriptors( foldingBuilder, psiFile, editor.getDocument(), true); Map<PsiElement, FoldingDescriptor> ranges = new HashMap<PsiElement, FoldingDescriptor>(); for (FoldingDescriptor descriptor : descriptors) { final ASTNode ast = descriptor.getElement(); final PsiElement psi = ast.getPsi(); if (psi != null) { ranges.put(psi, descriptor); } } return ranges; } void clear() { myPsiElements.clear(); for (RangeMarker marker : myRangeMarkers) { if (!(marker instanceof FoldRegion)) marker.dispose(); } myRangeMarkers.clear(); mySerializedElements.clear(); } @Override public void writeExternal(Element element) throws WriteExternalException { PsiDocumentManager.getInstance(myProject).commitAllDocuments(); if (myPsiElements.isEmpty() && myRangeMarkers.isEmpty() && mySerializedElements.isEmpty()) { throw new WriteExternalException(); } if (mySerializedElements.isEmpty()) { for (SmartPsiElementPointer<PsiElement> ptr : myPsiElements) { PsiElement psiElement = ptr.getElement(); if (psiElement == null || !psiElement.isValid()) { continue; } FoldingInfo fi = psiElement.getUserData(FOLDING_INFO_KEY); boolean state = fi != null && fi.expanded; String signature = FoldingPolicy.getSignature(psiElement); if (signature == null) { continue; } PsiFile containingFile = psiElement.getContainingFile(); PsiElement restoredElement = FoldingPolicy.restoreBySignature(containingFile, signature); if (!psiElement.equals(restoredElement)) { StringBuilder trace = new StringBuilder(); PsiElement restoredAgain = FoldingPolicy.restoreBySignature(containingFile, signature, trace); LOG.error( "element: " + psiElement + "(" + psiElement.getText() + "); restoredElement: " + restoredElement + "; signature: '" + signature + "'; file: " + containingFile + "; injected: " + InjectedLanguageManager.getInstance(myProject) .isInjectedFragment(containingFile) + "; languages: " + containingFile.getViewProvider().getLanguages() + "; restored again: " + restoredAgain + "; restore produces same results: " + (restoredAgain == restoredElement) + "; trace:\n" + trace); } Element e = new Element(ELEMENT_TAG); e.setAttribute(SIGNATURE_ATT, signature); e.setAttribute(EXPANDED_ATT, Boolean.toString(state)); element.addContent(e); } } else { // get back postponed state (before folding initialization) for (SerializedPsiElement entry : mySerializedElements) { Element e = new Element(ELEMENT_TAG); e.setAttribute(SIGNATURE_ATT, entry.mySerializedElement); e.setAttribute(EXPANDED_ATT, Boolean.toString(entry.myFoldingInfo.getExpanded())); element.addContent(e); } } String date = null; for (RangeMarker marker : myRangeMarkers) { FoldingInfo fi = marker.getUserData(FOLDING_INFO_KEY); boolean state = fi != null && fi.expanded; Element e = new Element(MARKER_TAG); if (date == null) { date = getTimeStamp(); } if (date.isEmpty()) { continue; } e.setAttribute(DATE_ATT, date); e.setAttribute(EXPANDED_ATT, Boolean.toString(state)); String signature = Integer.valueOf(marker.getStartOffset()) + ":" + Integer.valueOf(marker.getEndOffset()); e.setAttribute(SIGNATURE_ATT, signature); String placeHolderText = fi == null ? DEFAULT_PLACEHOLDER : fi.placeHolder; e.setAttribute(PLACEHOLDER_ATT, placeHolderText); element.addContent(e); } } @Override public void readExternal(final Element element) { ApplicationManager.getApplication() .runReadAction( new Runnable() { @Override public void run() { clear(); if (!myFile.isValid()) return; final Document document = FileDocumentManager.getInstance().getDocument(myFile); if (document == null) return; PsiFile psiFile = PsiDocumentManager.getInstance(myProject).getPsiFile(document); if (psiFile == null || !psiFile.getViewProvider().isPhysical()) return; String date = null; boolean canRestoreElement = !DumbService.getInstance(myProject).isDumb() || FoldingUpdate.supportsDumbModeFolding(psiFile); for (final Object o : element.getChildren()) { Element e = (Element) o; Boolean expanded = Boolean.valueOf(e.getAttributeValue(EXPANDED_ATT)); if (ELEMENT_TAG.equals(e.getName())) { String signature = e.getAttributeValue(SIGNATURE_ATT); if (signature == null) { continue; } FoldingInfo fi = new FoldingInfo(DEFAULT_PLACEHOLDER, expanded); if (canRestoreElement) { PsiElement restoredElement = FoldingPolicy.restoreBySignature(psiFile, signature); if (restoredElement != null && restoredElement.isValid()) { myPsiElements.add( SmartPointerManager.getInstance(myProject) .createSmartPsiElementPointer(restoredElement)); restoredElement.putUserData(FOLDING_INFO_KEY, fi); } } else { // Postponed initialization mySerializedElements.add(new SerializedPsiElement(signature, fi)); } } else if (MARKER_TAG.equals(e.getName())) { if (date == null) { date = getTimeStamp(); } if (date.isEmpty()) continue; if (!date.equals(e.getAttributeValue(DATE_ATT)) || FileDocumentManager.getInstance().isDocumentUnsaved(document)) continue; StringTokenizer tokenizer = new StringTokenizer(e.getAttributeValue(SIGNATURE_ATT), ":"); try { int start = Integer.valueOf(tokenizer.nextToken()).intValue(); int end = Integer.valueOf(tokenizer.nextToken()).intValue(); if (start < 0 || end >= document.getTextLength() || start > end) continue; RangeMarker marker = document.createRangeMarker(start, end); myRangeMarkers.add(marker); String placeHolderText = e.getAttributeValue(PLACEHOLDER_ATT); if (placeHolderText == null) placeHolderText = DEFAULT_PLACEHOLDER; FoldingInfo fi = new FoldingInfo(placeHolderText, expanded); marker.putUserData(FOLDING_INFO_KEY, fi); } catch (NoSuchElementException exc) { LOG.error(exc); } } else { throw new IllegalStateException("unknown tag: " + e.getName()); } } } }); } private String getTimeStamp() { if (!myFile.isValid()) return ""; return Long.toString(myFile.getTimeStamp()); } @Override public int hashCode() { int result = myProject.hashCode(); result = 31 * result + (myFile != null ? myFile.hashCode() : 0); result = 31 * result + myPsiElements.hashCode(); result = 31 * result + myRangeMarkers.hashCode(); result = 31 * result + mySerializedElements.hashCode(); return result; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } DocumentFoldingInfo info = (DocumentFoldingInfo) o; if (myFile != null ? !myFile.equals(info.myFile) : info.myFile != null) { return false; } if (!myProject.equals(info.myProject) || !myPsiElements.equals(info.myPsiElements) || !mySerializedElements.equals(info.mySerializedElements)) { return false; } if (myRangeMarkers.size() != info.myRangeMarkers.size()) return false; for (int i = 0; i < myRangeMarkers.size(); i++) { RangeMarker marker = myRangeMarkers.get(i); RangeMarker other = info.myRangeMarkers.get(i); if (marker == other || !marker.isValid() || !other.isValid()) { continue; } if (!TextRange.areSegmentsEqual(marker, other)) return false; FoldingInfo fi = marker.getUserData(FOLDING_INFO_KEY); FoldingInfo ofi = other.getUserData(FOLDING_INFO_KEY); if (!Comparing.equal(fi, ofi)) return false; } return true; } private static class FoldingInfo { private final String placeHolder; private final boolean expanded; private FoldingInfo(@NotNull String placeHolder, boolean expanded) { this.placeHolder = placeHolder; this.expanded = expanded; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } FoldingInfo info = (FoldingInfo) o; return expanded == info.expanded && placeHolder.equals(info.placeHolder); } @Override public int hashCode() { int result = placeHolder.hashCode(); result = 31 * result + (expanded ? 1 : 0); return result; } public boolean getExpanded() { return expanded; } } }
/** @author peter */ @SuppressWarnings("UseOfSystemOutOrSystemErr") public abstract class UsefulTestCase extends TestCase { public static final boolean IS_UNDER_TEAMCITY = System.getenv("TEAMCITY_VERSION") != null; public static final String IDEA_MARKER_CLASS = "com.intellij.openapi.components.impl.stores.IdeaProjectStoreImpl"; public static final String TEMP_DIR_MARKER = "unitTest_"; protected static boolean OVERWRITE_TESTDATA = false; private static final String DEFAULT_SETTINGS_EXTERNALIZED; private static final Random RNG = new SecureRandom(); private static final String ORIGINAL_TEMP_DIR = FileUtil.getTempDirectory(); protected final Disposable myTestRootDisposable = new Disposable() { @Override public void dispose() {} @Override public String toString() { String testName = getTestName(false); return UsefulTestCase.this.getClass() + (StringUtil.isEmpty(testName) ? "" : ".test" + testName); } }; protected static String ourPathToKeep = null; private CodeStyleSettings myOldCodeStyleSettings; private String myTempDir; protected static final Key<String> CREATION_PLACE = Key.create("CREATION_PLACE"); static { // Radar #5755208: Command line Java applications need a way to launch without a Dock icon. System.setProperty("apple.awt.UIElement", "true"); try { CodeInsightSettings defaultSettings = new CodeInsightSettings(); Element oldS = new Element("temp"); defaultSettings.writeExternal(oldS); DEFAULT_SETTINGS_EXTERNALIZED = JDOMUtil.writeElement(oldS, "\n"); } catch (Exception e) { throw new RuntimeException(e); } } protected boolean shouldContainTempFiles() { return true; } @Override protected void setUp() throws Exception { super.setUp(); if (shouldContainTempFiles()) { String testName = getTestName(true); if (StringUtil.isEmptyOrSpaces(testName)) testName = ""; testName = new File(testName).getName(); // in case the test name contains file separators myTempDir = FileUtil.toSystemDependentName( ORIGINAL_TEMP_DIR + "/" + TEMP_DIR_MARKER + testName + "_" + RNG.nextInt(1000)); FileUtil.resetCanonicalTempPathCache(myTempDir); } //noinspection AssignmentToStaticFieldFromInstanceMethod DocumentImpl.CHECK_DOCUMENT_CONSISTENCY = !isPerformanceTest(); } @Override protected void tearDown() throws Exception { try { Disposer.dispose(myTestRootDisposable); cleanupSwingDataStructures(); cleanupDeleteOnExitHookList(); } finally { if (shouldContainTempFiles()) { FileUtil.resetCanonicalTempPathCache(ORIGINAL_TEMP_DIR); if (ourPathToKeep != null && FileUtil.isAncestor(myTempDir, ourPathToKeep, false)) { File[] files = new File(myTempDir).listFiles(); if (files != null) { for (File file : files) { if (!FileUtil.pathsEqual(file.getPath(), ourPathToKeep)) { FileUtil.delete(file); } } } } else { FileUtil.delete(new File(myTempDir)); } } } UIUtil.removeLeakingAppleListeners(); super.tearDown(); } private static final Set<String> DELETE_ON_EXIT_HOOK_DOT_FILES; private static final Class DELETE_ON_EXIT_HOOK_CLASS; static { Class<?> aClass = null; Set<String> files = null; try { aClass = Class.forName("java.io.DeleteOnExitHook"); files = ReflectionUtil.getField(aClass, null, Set.class, "files"); } catch (Exception ignored) { } DELETE_ON_EXIT_HOOK_CLASS = aClass; DELETE_ON_EXIT_HOOK_DOT_FILES = files; } public static void cleanupDeleteOnExitHookList() throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException { // try to reduce file set retained by java.io.DeleteOnExitHook List<String> list; synchronized (DELETE_ON_EXIT_HOOK_CLASS) { if (DELETE_ON_EXIT_HOOK_DOT_FILES.isEmpty()) return; list = new ArrayList<String>(DELETE_ON_EXIT_HOOK_DOT_FILES); } for (int i = list.size() - 1; i >= 0; i--) { String path = list.get(i); if (FileSystemUtil.getAttributes(path) == null || new File(path).delete()) { synchronized (DELETE_ON_EXIT_HOOK_CLASS) { DELETE_ON_EXIT_HOOK_DOT_FILES.remove(path); } } } } private static void cleanupSwingDataStructures() throws Exception { Class<?> aClass = Class.forName("javax.swing.KeyboardManager"); Method get = aClass.getMethod("getCurrentManager"); get.setAccessible(true); Object manager = get.invoke(null); { Field mapF = aClass.getDeclaredField("componentKeyStrokeMap"); mapF.setAccessible(true); Object map = mapF.get(manager); ((Map) map).clear(); } { Field mapF = aClass.getDeclaredField("containerMap"); mapF.setAccessible(true); Object map = mapF.get(manager); ((Map) map).clear(); } } protected CompositeException checkForSettingsDamage() throws Exception { Application app = ApplicationManager.getApplication(); if (isPerformanceTest() || app == null || app instanceof MockApplication) { return new CompositeException(); } CodeStyleSettings oldCodeStyleSettings = myOldCodeStyleSettings; myOldCodeStyleSettings = null; return doCheckForSettingsDamage(oldCodeStyleSettings, getCurrentCodeStyleSettings()); } public static CompositeException doCheckForSettingsDamage( @NotNull CodeStyleSettings oldCodeStyleSettings, @NotNull CodeStyleSettings currentCodeStyleSettings) throws Exception { CompositeException result = new CompositeException(); final CodeInsightSettings settings = CodeInsightSettings.getInstance(); try { Element newS = new Element("temp"); settings.writeExternal(newS); Assert.assertEquals( "Code insight settings damaged", DEFAULT_SETTINGS_EXTERNALIZED, JDOMUtil.writeElement(newS, "\n")); } catch (AssertionError error) { CodeInsightSettings clean = new CodeInsightSettings(); Element temp = new Element("temp"); clean.writeExternal(temp); settings.loadState(temp); result.add(error); } currentCodeStyleSettings.getIndentOptions(StdFileTypes.JAVA); try { checkSettingsEqual( oldCodeStyleSettings, currentCodeStyleSettings, "Code style settings damaged"); } catch (AssertionError e) { result.add(e); } finally { currentCodeStyleSettings.clearCodeStyleSettings(); } try { InplaceRefactoring.checkCleared(); } catch (AssertionError e) { result.add(e); } try { StartMarkAction.checkCleared(); } catch (AssertionError e) { result.add(e); } return result; } protected void storeSettings() { if (!isPerformanceTest() && ApplicationManager.getApplication() != null) { myOldCodeStyleSettings = getCurrentCodeStyleSettings().clone(); myOldCodeStyleSettings.getIndentOptions(StdFileTypes.JAVA); } } protected CodeStyleSettings getCurrentCodeStyleSettings() { if (CodeStyleSchemes.getInstance().getCurrentScheme() == null) return new CodeStyleSettings(); return CodeStyleSettingsManager.getInstance().getCurrentSettings(); } public Disposable getTestRootDisposable() { return myTestRootDisposable; } @Override protected void runTest() throws Throwable { final Throwable[] throwables = new Throwable[1]; Runnable runnable = new Runnable() { @Override public void run() { try { UsefulTestCase.super.runTest(); } catch (InvocationTargetException e) { e.fillInStackTrace(); throwables[0] = e.getTargetException(); } catch (IllegalAccessException e) { e.fillInStackTrace(); throwables[0] = e; } catch (Throwable e) { throwables[0] = e; } } }; invokeTestRunnable(runnable); if (throwables[0] != null) { throw throwables[0]; } } protected boolean shouldRunTest() { return PlatformTestUtil.canRunTest(getClass()); } public static void edt(Runnable r) { UIUtil.invokeAndWaitIfNeeded(r); } protected void invokeTestRunnable(@NotNull Runnable runnable) throws Exception { UIUtil.invokeAndWaitIfNeeded(runnable); // runnable.run(); } protected void defaultRunBare() throws Throwable { super.runBare(); } @Override public void runBare() throws Throwable { if (!shouldRunTest()) return; if (runInDispatchThread()) { final Throwable[] exception = {null}; UIUtil.invokeAndWaitIfNeeded( new Runnable() { @Override public void run() { try { defaultRunBare(); } catch (Throwable tearingDown) { if (exception[0] == null) exception[0] = tearingDown; } } }); if (exception[0] != null) throw exception[0]; } else { defaultRunBare(); } } protected boolean runInDispatchThread() { return true; } @NonNls public static String toString(Iterable<?> collection) { if (!collection.iterator().hasNext()) { return "<empty>"; } final StringBuilder builder = new StringBuilder(); for (final Object o : collection) { if (o instanceof THashSet) { builder.append(new TreeSet<Object>((THashSet) o)); } else { builder.append(o); } builder.append("\n"); } return builder.toString(); } public static <T> void assertOrderedEquals(T[] actual, T... expected) { assertOrderedEquals(Arrays.asList(actual), expected); } public static <T> void assertOrderedEquals(Iterable<T> actual, T... expected) { assertOrderedEquals(null, actual, expected); } public static void assertOrderedEquals(@NotNull byte[] actual, @NotNull byte[] expected) { assertEquals(actual.length, expected.length); for (int i = 0; i < actual.length; i++) { byte a = actual[i]; byte e = expected[i]; assertEquals("not equals at index: " + i, e, a); } } public static <T> void assertOrderedEquals( final String errorMsg, @NotNull Iterable<T> actual, @NotNull T... expected) { Assert.assertNotNull(actual); Assert.assertNotNull(expected); assertOrderedEquals(errorMsg, actual, Arrays.asList(expected)); } public static <T> void assertOrderedEquals( final Iterable<? extends T> actual, final Collection<? extends T> expected) { assertOrderedEquals(null, actual, expected); } public static <T> void assertOrderedEquals( final String erroMsg, final Iterable<? extends T> actual, final Collection<? extends T> expected) { ArrayList<T> list = new ArrayList<T>(); for (T t : actual) { list.add(t); } if (!list.equals(new ArrayList<T>(expected))) { String expectedString = toString(expected); String actualString = toString(actual); Assert.assertEquals(erroMsg, expectedString, actualString); Assert.fail( "Warning! 'toString' does not reflect the difference.\nExpected: " + expectedString + "\nActual: " + actualString); } } public static <T> void assertOrderedCollection(T[] collection, @NotNull Consumer<T>... checkers) { Assert.assertNotNull(collection); assertOrderedCollection(Arrays.asList(collection), checkers); } public static <T> void assertSameElements(T[] collection, T... expected) { assertSameElements(Arrays.asList(collection), expected); } public static <T> void assertSameElements(Collection<? extends T> collection, T... expected) { assertSameElements(collection, Arrays.asList(expected)); } public static <T> void assertSameElements( Collection<? extends T> collection, Collection<T> expected) { assertSameElements(null, collection, expected); } public static <T> void assertSameElements( String message, Collection<? extends T> collection, Collection<T> expected) { assertNotNull(collection); assertNotNull(expected); if (collection.size() != expected.size() || !new HashSet<T>(expected).equals(new HashSet<T>(collection))) { Assert.assertEquals(message, toString(expected, "\n"), toString(collection, "\n")); Assert.assertEquals(message, new HashSet<T>(expected), new HashSet<T>(collection)); } } public <T> void assertContainsOrdered(Collection<? extends T> collection, T... expected) { assertContainsOrdered(collection, Arrays.asList(expected)); } public <T> void assertContainsOrdered( Collection<? extends T> collection, Collection<T> expected) { ArrayList<T> copy = new ArrayList<T>(collection); copy.retainAll(expected); assertOrderedEquals(toString(collection), copy, expected); } public <T> void assertContainsElements(Collection<? extends T> collection, T... expected) { assertContainsElements(collection, Arrays.asList(expected)); } public <T> void assertContainsElements( Collection<? extends T> collection, Collection<T> expected) { ArrayList<T> copy = new ArrayList<T>(collection); copy.retainAll(expected); assertSameElements(toString(collection), copy, expected); } public static String toString(Object[] collection, String separator) { return toString(Arrays.asList(collection), separator); } public <T> void assertDoesntContain(Collection<? extends T> collection, T... notExpected) { assertDoesntContain(collection, Arrays.asList(notExpected)); } public <T> void assertDoesntContain( Collection<? extends T> collection, Collection<T> notExpected) { ArrayList<T> expected = new ArrayList<T>(collection); expected.removeAll(notExpected); assertSameElements(collection, expected); } public static String toString(Collection<?> collection, String separator) { List<String> list = ContainerUtil.map2List( collection, new Function<Object, String>() { @Override public String fun(final Object o) { return String.valueOf(o); } }); Collections.sort(list); StringBuilder builder = new StringBuilder(); boolean flag = false; for (final String o : list) { if (flag) { builder.append(separator); } builder.append(o); flag = true; } return builder.toString(); } public static <T> void assertOrderedCollection( Collection<? extends T> collection, Consumer<T>... checkers) { Assert.assertNotNull(collection); if (collection.size() != checkers.length) { Assert.fail(toString(collection)); } int i = 0; for (final T actual : collection) { try { checkers[i].consume(actual); } catch (AssertionFailedError e) { System.out.println(i + ": " + actual); throw e; } i++; } } public static <T> void assertUnorderedCollection(T[] collection, Consumer<T>... checkers) { assertUnorderedCollection(Arrays.asList(collection), checkers); } public static <T> void assertUnorderedCollection( Collection<? extends T> collection, Consumer<T>... checkers) { Assert.assertNotNull(collection); if (collection.size() != checkers.length) { Assert.fail(toString(collection)); } Set<Consumer<T>> checkerSet = new HashSet<Consumer<T>>(Arrays.asList(checkers)); int i = 0; Throwable lastError = null; for (final T actual : collection) { boolean flag = true; for (final Consumer<T> condition : checkerSet) { Throwable error = accepts(condition, actual); if (error == null) { checkerSet.remove(condition); flag = false; break; } else { lastError = error; } } if (flag) { lastError.printStackTrace(); Assert.fail("Incorrect element(" + i + "): " + actual); } i++; } } private static <T> Throwable accepts(final Consumer<T> condition, final T actual) { try { condition.consume(actual); return null; } catch (Throwable e) { return e; } } public static <T> T assertInstanceOf(Object o, Class<T> aClass) { Assert.assertNotNull("Expected instance of: " + aClass.getName() + " actual: " + null, o); Assert.assertTrue( "Expected instance of: " + aClass.getName() + " actual: " + o.getClass().getName(), aClass.isInstance(o)); @SuppressWarnings("unchecked") T t = (T) o; return t; } public static <T> T assertOneElement(Collection<T> collection) { Assert.assertNotNull(collection); Assert.assertEquals(toString(collection), 1, collection.size()); return collection.iterator().next(); } public static <T> T assertOneElement(T[] ts) { Assert.assertNotNull(ts); Assert.assertEquals(Arrays.asList(ts).toString(), 1, ts.length); return ts[0]; } public static <T> void assertOneOf(T value, T... values) { boolean found = false; for (T v : values) { if (value == v || value != null && value.equals(v)) { found = true; } } Assert.assertTrue(value + " should be equal to one of " + Arrays.toString(values), found); } public static void printThreadDump() { PerformanceWatcher.dumpThreadsToConsole("Thread dump:"); } public static void assertEmpty(final Object[] array) { assertOrderedEquals(array); } public static void assertNotEmpty(final Collection<?> collection) { if (collection == null) return; assertTrue(!collection.isEmpty()); } public static void assertEmpty(final Collection<?> collection) { assertEmpty(collection.toString(), collection); } public static void assertNullOrEmpty(final Collection<?> collection) { if (collection == null) return; assertEmpty(null, collection); } public static void assertEmpty(final String s) { assertTrue(s, StringUtil.isEmpty(s)); } public static <T> void assertEmpty(final String errorMsg, final Collection<T> collection) { assertOrderedEquals(errorMsg, collection); } public static void assertSize(int expectedSize, final Object[] array) { assertEquals(toString(Arrays.asList(array)), expectedSize, array.length); } public static void assertSize(int expectedSize, final Collection<?> c) { assertEquals(toString(c), expectedSize, c.size()); } protected <T extends Disposable> T disposeOnTearDown(final T disposable) { Disposer.register(myTestRootDisposable, disposable); return disposable; } public static void assertSameLines(String expected, String actual) { String expectedText = StringUtil.convertLineSeparators(expected.trim()); String actualText = StringUtil.convertLineSeparators(actual.trim()); Assert.assertEquals(expectedText, actualText); } public static void assertExists(File file) { assertTrue("File should exist " + file, file.exists()); } public static void assertDoesntExist(File file) { assertFalse("File should not exist " + file, file.exists()); } protected String getTestName(boolean lowercaseFirstLetter) { String name = getName(); return getTestName(name, lowercaseFirstLetter); } public static String getTestName(String name, boolean lowercaseFirstLetter) { if (name == null) { return ""; } name = StringUtil.trimStart(name, "test"); if (StringUtil.isEmpty(name)) { return ""; } return lowercaseFirstLetter(name, lowercaseFirstLetter); } public static String lowercaseFirstLetter(String name, boolean lowercaseFirstLetter) { if (lowercaseFirstLetter && !isAllUppercaseName(name)) { name = Character.toLowerCase(name.charAt(0)) + name.substring(1); } return name; } public static boolean isAllUppercaseName(String name) { int uppercaseChars = 0; for (int i = 0; i < name.length(); i++) { if (Character.isLowerCase(name.charAt(i))) { return false; } if (Character.isUpperCase(name.charAt(i))) { uppercaseChars++; } } return uppercaseChars >= 3; } protected String getTestDirectoryName() { final String testName = getTestName(true); return testName.replaceAll("_.*", ""); } protected static void assertSameLinesWithFile(String filePath, String actualText) { String fileText; try { if (OVERWRITE_TESTDATA) { VfsTestUtil.overwriteTestData(filePath, actualText); System.out.println("File " + filePath + " created."); } fileText = FileUtil.loadFile(new File(filePath), CharsetToolkit.UTF8); } catch (FileNotFoundException e) { VfsTestUtil.overwriteTestData(filePath, actualText); throw new AssertionFailedError("No output text found. File " + filePath + " created."); } catch (IOException e) { throw new RuntimeException(e); } String expected = StringUtil.convertLineSeparators(fileText.trim()); String actual = StringUtil.convertLineSeparators(actualText.trim()); if (!Comparing.equal(expected, actual)) { throw new FileComparisonFailure(null, expected, actual, filePath); } } public static void clearFields(final Object test) throws IllegalAccessException { Class aClass = test.getClass(); while (aClass != null) { clearDeclaredFields(test, aClass); aClass = aClass.getSuperclass(); } } public static void clearDeclaredFields(Object test, Class aClass) throws IllegalAccessException { if (aClass == null) return; for (final Field field : aClass.getDeclaredFields()) { @NonNls final String name = field.getDeclaringClass().getName(); if (!name.startsWith("junit.framework.") && !name.startsWith("com.intellij.testFramework.")) { final int modifiers = field.getModifiers(); if ((modifiers & Modifier.FINAL) == 0 && (modifiers & Modifier.STATIC) == 0 && !field.getType().isPrimitive()) { field.setAccessible(true); field.set(test, null); } } } } @SuppressWarnings("deprecation") protected static void checkSettingsEqual( JDOMExternalizable expected, JDOMExternalizable settings, String message) throws Exception { if (expected == null || settings == null) return; Element oldS = new Element("temp"); expected.writeExternal(oldS); Element newS = new Element("temp"); settings.writeExternal(newS); String newString = JDOMUtil.writeElement(newS, "\n"); String oldString = JDOMUtil.writeElement(oldS, "\n"); Assert.assertEquals(message, oldString, newString); } public boolean isPerformanceTest() { String name = getName(); return name != null && name.contains("Performance") || getClass().getName().contains("Performance"); } public static void doPostponedFormatting(final Project project) { DocumentUtil.writeInRunUndoTransparentAction( new Runnable() { @Override public void run() { PsiDocumentManager.getInstance(project).commitAllDocuments(); PostprocessReformattingAspect.getInstance(project).doPostponedFormatting(); } }); } protected static void checkAllTimersAreDisposed() { try { Class<?> aClass = Class.forName("javax.swing.TimerQueue"); Method inst = aClass.getDeclaredMethod("sharedInstance"); inst.setAccessible(true); Object queue = inst.invoke(null); Field field = aClass.getDeclaredField("firstTimer"); field.setAccessible(true); Object firstTimer = field.get(queue); if (firstTimer != null) { try { fail("Not disposed Timer: " + firstTimer.toString() + "; queue:" + queue); } finally { field.set(queue, null); } } } catch (Throwable e) { // Ignore } } /** * Checks that code block throw corresponding exception. * * @param exceptionCase Block annotated with some exception type * @throws Throwable */ protected void assertException(final AbstractExceptionCase exceptionCase) throws Throwable { assertException(exceptionCase, null); } /** * Checks that code block throw corresponding exception with expected error msg. If expected error * message is null it will not be checked. * * @param exceptionCase Block annotated with some exception type * @param expectedErrorMsg expected error messge * @throws Throwable */ protected void assertException( final AbstractExceptionCase exceptionCase, @Nullable final String expectedErrorMsg) throws Throwable { assertExceptionOccurred(true, exceptionCase, expectedErrorMsg); } /** * Checks that code block doesn't throw corresponding exception. * * @param exceptionCase Block annotated with some exception type * @throws Throwable */ protected void assertNoException(final AbstractExceptionCase exceptionCase) throws Throwable { assertExceptionOccurred(false, exceptionCase, null); } protected void assertNoThrowable(final Runnable closure) { String throwableName = null; try { closure.run(); } catch (Throwable thr) { throwableName = thr.getClass().getName(); } assertNull(throwableName); } private static void assertExceptionOccurred( boolean shouldOccur, AbstractExceptionCase exceptionCase, String expectedErrorMsg) throws Throwable { boolean wasThrown = false; try { exceptionCase.tryClosure(); } catch (Throwable e) { if (shouldOccur) { wasThrown = true; final String errorMessage = exceptionCase.getAssertionErrorMessage(); assertEquals(errorMessage, exceptionCase.getExpectedExceptionClass(), e.getClass()); if (expectedErrorMsg != null) { assertEquals("Compare error messages", expectedErrorMsg, e.getMessage()); } } else if (exceptionCase.getExpectedExceptionClass().equals(e.getClass())) { wasThrown = true; System.out.println(""); e.printStackTrace(System.out); fail("Exception isn't expected here. Exception message: " + e.getMessage()); } else { throw e; } } finally { if (shouldOccur && !wasThrown) { fail(exceptionCase.getAssertionErrorMessage()); } } } protected boolean annotatedWith(@NotNull Class annotationClass) { Class<?> aClass = getClass(); String methodName = "test" + getTestName(false); boolean methodChecked = false; while (aClass != null && aClass != Object.class) { if (aClass.getAnnotation(annotationClass) != null) return true; if (!methodChecked) { try { Method method = aClass.getDeclaredMethod(methodName); if (method.getAnnotation(annotationClass) != null) return true; methodChecked = true; } catch (NoSuchMethodException ignored) { } } aClass = aClass.getSuperclass(); } return false; } protected String getHomePath() { return PathManager.getHomePath().replace(File.separatorChar, '/'); } protected static boolean isInHeadlessEnvironment() { return GraphicsEnvironment.isHeadless(); } public static void refreshRecursively(@NotNull VirtualFile file) { VfsUtilCore.visitChildrenRecursively( file, new VirtualFileVisitor() { @Override public boolean visitFile(@NotNull VirtualFile file) { file.getChildren(); return true; } }); file.refresh(false, true); } @NotNull public static Test filteredSuite(@RegExp String regexp, @NotNull Test test) { final Pattern pattern = Pattern.compile(regexp); final TestSuite testSuite = new TestSuite(); new Processor<Test>() { @Override public boolean process(Test test) { if (test instanceof TestSuite) { for (int i = 0, len = ((TestSuite) test).testCount(); i < len; i++) { process(((TestSuite) test).testAt(i)); } } else if (pattern.matcher(test.toString()).find()) { testSuite.addTest(test); } return false; } }.process(test); return testSuite; } }
public class ProjectManagerImpl extends ProjectManagerEx implements NamedJDOMExternalizable, ExportableApplicationComponent { private static final Logger LOG = Logger.getInstance("#com.intellij.project.impl.ProjectManagerImpl"); public static final int CURRENT_FORMAT_VERSION = 4; private static final Key<List<ProjectManagerListener>> LISTENERS_IN_PROJECT_KEY = Key.create("LISTENERS_IN_PROJECT_KEY"); @NonNls private static final String ELEMENT_DEFAULT_PROJECT = "defaultProject"; @SuppressWarnings({"FieldAccessedSynchronizedAndUnsynchronized"}) private ProjectImpl myDefaultProject; // Only used asynchronously in save and dispose, which itself are // synchronized. @SuppressWarnings({"FieldAccessedSynchronizedAndUnsynchronized"}) private Element myDefaultProjectRootElement; // Only used asynchronously in save and dispose, which itself are // synchronized. private final List<Project> myOpenProjects = new ArrayList<Project>(); private Project[] myOpenProjectsArrayCache = {}; private final List<ProjectManagerListener> myListeners = ContainerUtil.createLockFreeCopyOnWriteList(); private final Set<Project> myTestProjects = new THashSet<Project>(); private final Map<VirtualFile, byte[]> mySavedCopies = new HashMap<VirtualFile, byte[]>(); private final TObjectLongHashMap<VirtualFile> mySavedTimestamps = new TObjectLongHashMap<VirtualFile>(); private final Map<Project, List<Pair<VirtualFile, StateStorage>>> myChangedProjectFiles = new HashMap<Project, List<Pair<VirtualFile, StateStorage>>>(); private final Alarm myChangedFilesAlarm = new Alarm(); private final List<Pair<VirtualFile, StateStorage>> myChangedApplicationFiles = new ArrayList<Pair<VirtualFile, StateStorage>>(); private final AtomicInteger myReloadBlockCount = new AtomicInteger(0); @SuppressWarnings("FieldCanBeLocal") private final Map<Project, String> myProjects = new WeakHashMap<Project, String>(); private static final int MAX_LEAKY_PROJECTS = 42; private final ProgressManager myProgressManager; private volatile boolean myDefaultProjectWasDisposed = false; @NotNull private static List<ProjectManagerListener> getListeners(Project project) { List<ProjectManagerListener> array = project.getUserData(LISTENERS_IN_PROJECT_KEY); if (array == null) return Collections.emptyList(); return array; } /** @noinspection UnusedParameters */ public ProjectManagerImpl( VirtualFileManager virtualFileManager, RecentProjectsManagerBase recentProjectsManager, ProgressManager progressManager) { myProgressManager = progressManager; Application app = ApplicationManager.getApplication(); MessageBus messageBus = app.getMessageBus(); MessageBusConnection connection = messageBus.connect(app); connection.subscribe( StateStorage.STORAGE_TOPIC, new StateStorage.Listener() { @Override public void storageFileChanged( @NotNull final VirtualFileEvent event, @NotNull final StateStorage storage) { VirtualFile file = event.getFile(); if (!file.isDirectory() && !(event.getRequestor() instanceof StateStorage.SaveSession)) { saveChangedProjectFile(file, null, storage); } } }); final ProjectManagerListener busPublisher = messageBus.syncPublisher(TOPIC); addProjectManagerListener( new ProjectManagerListener() { @Override public void projectOpened(final Project project) { MessageBus messageBus = project.getMessageBus(); MessageBusConnection connection = messageBus.connect(project); connection.subscribe( StateStorage.STORAGE_TOPIC, new StateStorage.Listener() { @Override public void storageFileChanged( @NotNull final VirtualFileEvent event, @NotNull final StateStorage storage) { VirtualFile file = event.getFile(); if (!file.isDirectory() && !(event.getRequestor() instanceof StateStorage.SaveSession)) { saveChangedProjectFile(file, project, storage); } } }); busPublisher.projectOpened(project); for (ProjectManagerListener listener : getListeners(project)) { listener.projectOpened(project); } } @Override public void projectClosed(Project project) { busPublisher.projectClosed(project); for (ProjectManagerListener listener : getListeners(project)) { listener.projectClosed(project); } } @Override public boolean canCloseProject(Project project) { for (ProjectManagerListener listener : getListeners(project)) { if (!listener.canCloseProject(project)) { return false; } } return true; } @Override public void projectClosing(Project project) { busPublisher.projectClosing(project); for (ProjectManagerListener listener : getListeners(project)) { listener.projectClosing(project); } } }); registerExternalProjectFileListener(virtualFileManager); } @Override public void disposeComponent() { ApplicationManager.getApplication().assertWriteAccessAllowed(); Disposer.dispose(myChangedFilesAlarm); if (myDefaultProject != null) { Disposer.dispose(myDefaultProject); myDefaultProject = null; myDefaultProjectWasDisposed = true; } } @Override public void initComponent() {} private static final boolean LOG_PROJECT_LEAKAGE_IN_TESTS = false; @Override @Nullable public Project newProject( final String projectName, @NotNull String filePath, boolean useDefaultProjectSettings, boolean isDummy) { filePath = toCanonicalName(filePath); //noinspection ConstantConditions if (LOG_PROJECT_LEAKAGE_IN_TESTS && ApplicationManager.getApplication().isUnitTestMode()) { for (int i = 0; i < 42; i++) { if (myProjects.size() < MAX_LEAKY_PROJECTS) break; System.gc(); TimeoutUtil.sleep(100); System.gc(); } if (myProjects.size() >= MAX_LEAKY_PROJECTS) { List<Project> copy = new ArrayList<Project>(myProjects.keySet()); myProjects.clear(); throw new TooManyProjectLeakedException(copy); } } ProjectImpl project = createProject( projectName, filePath, false, ApplicationManager.getApplication().isUnitTestMode()); try { initProject(project, useDefaultProjectSettings ? (ProjectImpl) getDefaultProject() : null); if (LOG_PROJECT_LEAKAGE_IN_TESTS) { myProjects.put(project, null); } return project; } catch (final Exception e) { LOG.info(e); Messages.showErrorDialog(message(e), ProjectBundle.message("project.load.default.error")); } return null; } @NonNls private static String message(Throwable e) { String message = e.getMessage(); if (message != null) return message; message = e.getLocalizedMessage(); if (message != null) return message; message = e.toString(); Throwable cause = e.getCause(); if (cause != null) { String causeMessage = message(cause); return message + " (cause: " + causeMessage + ")"; } return message; } private void initProject(@NotNull ProjectImpl project, @Nullable ProjectImpl template) throws IOException { final ProgressIndicator indicator = myProgressManager.getProgressIndicator(); if (indicator != null && !project.isDefault()) { indicator.setText(ProjectBundle.message("loading.components.for", project.getName())); indicator.setIndeterminate(true); } ApplicationManager.getApplication() .getMessageBus() .syncPublisher(ProjectLifecycleListener.TOPIC) .beforeProjectLoaded(project); try { if (template != null) { project.getStateStore().loadProjectFromTemplate(template); } else { project.getStateStore().load(); } project.loadProjectComponents(); project.init(); } catch (IOException e) { scheduleDispose(project); throw e; } catch (ProcessCanceledException e) { scheduleDispose(project); throw e; } } private ProjectImpl createProject( @Nullable String projectName, @NotNull String filePath, boolean isDefault, boolean isOptimiseTestLoadSpeed) { return isDefault ? new DefaultProject(this, "", isOptimiseTestLoadSpeed) : new ProjectImpl( this, new File(filePath).getAbsolutePath(), isOptimiseTestLoadSpeed, projectName); } private static void scheduleDispose(final ProjectImpl project) { if (project.isDefault()) { return; } ApplicationManager.getApplication() .invokeLater( new Runnable() { @Override public void run() { ApplicationManager.getApplication() .runWriteAction( new Runnable() { @Override public void run() { if (!project.isDisposed()) { Disposer.dispose(project); } } }); } }); } @Override @Nullable public Project loadProject(@NotNull String filePath) throws IOException, JDOMException, InvalidDataException { try { ProjectImpl project = createProject(null, filePath, false, false); initProject(project, null); return project; } catch (StateStorageException e) { throw new IOException(e.getMessage()); } } @NotNull private static String toCanonicalName(@NotNull final String filePath) { try { return FileUtil.resolveShortWindowsName(filePath); } catch (IOException e) { // OK. File does not yet exist so it's canonical path will be equal to its original path. } return filePath; } @TestOnly public synchronized boolean isDefaultProjectInitialized() { return myDefaultProject != null; } @Override @NotNull public synchronized Project getDefaultProject() { LOG.assertTrue(!myDefaultProjectWasDisposed, "Default project has been already disposed!"); if (myDefaultProject == null) { try { myDefaultProject = createProject(null, "", true, ApplicationManager.getApplication().isUnitTestMode()); initProject(myDefaultProject, null); myDefaultProjectRootElement = null; } catch (IOException e) { LOG.error(e); } catch (StateStorageException e) { LOG.error(e); } } return myDefaultProject; } public Element getDefaultProjectRootElement() { return myDefaultProjectRootElement; } @Override @NotNull public Project[] getOpenProjects() { synchronized (myOpenProjects) { if (myOpenProjectsArrayCache.length != myOpenProjects.size()) { LOG.error( "Open projects: " + myOpenProjects + "; cache: " + Arrays.asList(myOpenProjectsArrayCache)); } if (myOpenProjectsArrayCache.length > 0 && myOpenProjectsArrayCache[0] != myOpenProjects.get(0)) { LOG.error( "Open projects cache corrupted. Open projects: " + myOpenProjects + "; cache: " + Arrays.asList(myOpenProjectsArrayCache)); } if (ApplicationManager.getApplication().isUnitTestMode()) { Project[] testProjects = myTestProjects.toArray(new Project[myTestProjects.size()]); for (Project testProject : testProjects) { assert !testProject.isDisposed() : testProject; } return ArrayUtil.mergeArrays(myOpenProjectsArrayCache, testProjects); } return myOpenProjectsArrayCache; } } @Override public boolean isProjectOpened(Project project) { synchronized (myOpenProjects) { return ApplicationManager.getApplication().isUnitTestMode() && myTestProjects.contains(project) || myOpenProjects.contains(project); } } @Override public boolean openProject(final Project project) { if (isLight(project)) { throw new AssertionError("must not open light project"); } final Application application = ApplicationManager.getApplication(); if (!application.isUnitTestMode() && !((ProjectEx) project).getStateStore().checkVersion()) { return false; } synchronized (myOpenProjects) { if (myOpenProjects.contains(project)) { return false; } myOpenProjects.add(project); cacheOpenProjects(); } fireProjectOpened(project); final StartupManagerImpl startupManager = (StartupManagerImpl) StartupManager.getInstance(project); waitForFileWatcher(project); boolean ok = myProgressManager.runProcessWithProgressSynchronously( new Runnable() { @Override public void run() { startupManager.runStartupActivities(); // dumb mode should start before post-startup activities // only when startCacheUpdate is called from UI thread, we can guarantee that // when the method returns, the application has entered dumb mode UIUtil.invokeAndWaitIfNeeded( new Runnable() { @Override public void run() { startupManager.startCacheUpdate(); } }); startupManager.runPostStartupActivitiesFromExtensions(); UIUtil.invokeLaterIfNeeded( new Runnable() { @Override public void run() { startupManager.runPostStartupActivities(); } }); } }, ProjectBundle.message("project.load.progress"), true, project); if (!ok) { closeProject(project, false, false, true); notifyProjectOpenFailed(); return false; } if (!application.isHeadlessEnvironment() && !application.isUnitTestMode()) { // should be invoked last startupManager.runWhenProjectIsInitialized( new Runnable() { @Override public void run() { final TrackingPathMacroSubstitutor macroSubstitutor = ((ProjectEx) project) .getStateStore() .getStateStorageManager() .getMacroSubstitutor(); if (macroSubstitutor != null) { StorageUtil.notifyUnknownMacros(macroSubstitutor, project, null); } } }); } return true; } private void cacheOpenProjects() { myOpenProjectsArrayCache = myOpenProjects.toArray(new Project[myOpenProjects.size()]); } private void waitForFileWatcher(@NotNull Project project) { LocalFileSystem fs = LocalFileSystem.getInstance(); if (!(fs instanceof LocalFileSystemImpl)) return; final FileWatcher watcher = ((LocalFileSystemImpl) fs).getFileWatcher(); if (!watcher.isOperational() || !watcher.isSettingRoots()) return; LOG.info("FW/roots waiting started"); Task.Modal task = new Task.Modal(project, ProjectBundle.message("project.load.progress"), true) { @Override public void run(@NotNull ProgressIndicator indicator) { indicator.setIndeterminate(true); indicator.setText(ProjectBundle.message("project.load.waiting.watcher")); if (indicator instanceof ProgressWindow) { ((ProgressWindow) indicator).setCancelButtonText(CommonBundle.message("button.skip")); } while (watcher.isSettingRoots() && !indicator.isCanceled()) { TimeoutUtil.sleep(10); } LOG.info("FW/roots waiting finished"); } }; myProgressManager.run(task); } @Override public Project loadAndOpenProject(@NotNull final String filePath) throws IOException { final Project project = convertAndLoadProject(filePath); if (project == null) { WelcomeFrame.showIfNoProjectOpened(); return null; } // todo unify this logic with PlatformProjectOpenProcessor if (!openProject(project)) { WelcomeFrame.showIfNoProjectOpened(); ApplicationManager.getApplication() .runWriteAction( new Runnable() { @Override public void run() { Disposer.dispose(project); } }); } return project; } /** * Converts and loads the project at the specified path. * * @param filePath the path to open the project. * @return the project, or null if the user has cancelled opening the project. */ @Override @Nullable public Project convertAndLoadProject(String filePath) throws IOException { final String fp = toCanonicalName(filePath); final ConversionResult conversionResult = ConversionService.getInstance().convert(fp); if (conversionResult.openingIsCanceled()) { return null; } final Project project = loadProjectWithProgress(filePath); if (project == null) return null; if (!conversionResult.conversionNotNeeded()) { StartupManager.getInstance(project) .registerPostStartupActivity( new Runnable() { @Override public void run() { conversionResult.postStartupActivity(project); } }); } return project; } /** * Opens the project at the specified path. * * @param filePath the path to open the project. * @return the project, or null if the user has cancelled opening the project. */ @Nullable private Project loadProjectWithProgress(@NotNull final String filePath) throws IOException { final ProjectImpl project = createProject(null, toCanonicalName(filePath), false, false); try { myProgressManager.runProcessWithProgressSynchronously( new ThrowableComputable<Project, IOException>() { @Override @Nullable public Project compute() throws IOException { initProject(project, null); return project; } }, ProjectBundle.message("project.load.progress"), true, project); } catch (StateStorageException e) { throw new IOException(e); } catch (ProcessCanceledException ignore) { return null; } return project; } private static void notifyProjectOpenFailed() { ApplicationManager.getApplication() .getMessageBus() .syncPublisher(AppLifecycleListener.TOPIC) .projectOpenFailed(); WelcomeFrame.showIfNoProjectOpened(); } private void registerExternalProjectFileListener(VirtualFileManager virtualFileManager) { virtualFileManager.addVirtualFileManagerListener( new VirtualFileManagerListener() { @Override public void beforeRefreshStart(boolean asynchronous) {} @Override public void afterRefreshFinish(boolean asynchronous) { scheduleReloadApplicationAndProject(); } }); } private void askToReloadProjectIfConfigFilesChangedExternally() { LOG.debug("[RELOAD] myReloadBlockCount = " + myReloadBlockCount.get()); if (myReloadBlockCount.get() == 0) { Set<Project> projects; synchronized (myChangedProjectFiles) { if (myChangedProjectFiles.isEmpty()) return; projects = new HashSet<Project>(myChangedProjectFiles.keySet()); } List<Project> projectsToReload = new ArrayList<Project>(); for (Project project : projects) { if (shouldReloadProject(project)) { projectsToReload.add(project); } } for (final Project projectToReload : projectsToReload) { reloadProjectImpl(projectToReload, false); } } } private boolean tryToReloadApplication() { try { final Application app = ApplicationManager.getApplication(); if (app.isDisposed()) return false; final HashSet<Pair<VirtualFile, StateStorage>> causes = new HashSet<Pair<VirtualFile, StateStorage>>(myChangedApplicationFiles); if (causes.isEmpty()) return true; final boolean[] reloadOk = {false}; final LinkedHashSet<String> components = new LinkedHashSet<String>(); ApplicationManager.getApplication() .runWriteAction( new Runnable() { @Override public void run() { try { reloadOk[0] = ((ApplicationImpl) app).getStateStore().reload(causes, components); } catch (StateStorageException e) { Messages.showWarningDialog( ProjectBundle.message("project.reload.failed", e.getMessage()), ProjectBundle.message("project.reload.failed.title")); } catch (IOException e) { Messages.showWarningDialog( ProjectBundle.message("project.reload.failed", e.getMessage()), ProjectBundle.message("project.reload.failed.title")); } } }); if (!reloadOk[0] && !components.isEmpty()) { String message = "Application components were changed externally and cannot be reloaded:\n"; for (String component : components) { message += component + "\n"; } final boolean canRestart = ApplicationManager.getApplication().isRestartCapable(); message += "Would you like to " + (canRestart ? "restart " : "shutdown "); message += ApplicationNamesInfo.getInstance().getProductName() + "?"; if (Messages.showYesNoDialog( message, "Application Configuration Reload", Messages.getQuestionIcon()) == Messages.YES) { for (Pair<VirtualFile, StateStorage> cause : causes) { StateStorage stateStorage = cause.getSecond(); if (stateStorage instanceof XmlElementStorage) { ((XmlElementStorage) stateStorage).disableSaving(); } } ApplicationManagerEx.getApplicationEx().restart(true); } } return reloadOk[0]; } finally { myChangedApplicationFiles.clear(); } } private boolean shouldReloadProject(final Project project) { if (project.isDisposed()) return false; final HashSet<Pair<VirtualFile, StateStorage>> causes = new HashSet<Pair<VirtualFile, StateStorage>>(); synchronized (myChangedProjectFiles) { final List<Pair<VirtualFile, StateStorage>> changes = myChangedProjectFiles.remove(project); if (changes != null) { causes.addAll(changes); } if (causes.isEmpty()) return false; } final boolean[] reloadOk = {false}; ApplicationManager.getApplication() .runWriteAction( new Runnable() { @Override public void run() { try { LOG.debug("[RELOAD] Reloading project/components..."); reloadOk[0] = ((ProjectEx) project).getStateStore().reload(causes); } catch (StateStorageException e) { Messages.showWarningDialog( ProjectBundle.message("project.reload.failed", e.getMessage()), ProjectBundle.message("project.reload.failed.title")); } catch (IOException e) { Messages.showWarningDialog( ProjectBundle.message("project.reload.failed", e.getMessage()), ProjectBundle.message("project.reload.failed.title")); } } }); if (reloadOk[0]) return false; String message; if (causes.size() == 1) { message = ProjectBundle.message( "project.reload.external.change.single", causes.iterator().next().first.getPresentableUrl()); } else { StringBuilder filesBuilder = new StringBuilder(); boolean first = true; Set<String> alreadyShown = new HashSet<String>(); for (Pair<VirtualFile, StateStorage> cause : causes) { String url = cause.first.getPresentableUrl(); if (!alreadyShown.contains(url)) { if (alreadyShown.size() > 10) { filesBuilder .append("\n" + "and ") .append(causes.size() - alreadyShown.size()) .append(" more"); break; } if (!first) filesBuilder.append("\n"); first = false; filesBuilder.append(url); alreadyShown.add(url); } } message = ProjectBundle.message("project.reload.external.change.multiple", filesBuilder.toString()); } return Messages.showTwoStepConfirmationDialog( message, ProjectBundle.message("project.reload.external.change.title"), "Reload project", Messages.getQuestionIcon()) == 0; } @Override public boolean isFileSavedToBeReloaded(VirtualFile candidate) { return mySavedCopies.containsKey(candidate); } @Override public void blockReloadingProjectOnExternalChanges() { myReloadBlockCount.incrementAndGet(); } @Override public void unblockReloadingProjectOnExternalChanges() { if (myReloadBlockCount.decrementAndGet() == 0) scheduleReloadApplicationAndProject(); } private void scheduleReloadApplicationAndProject() { // todo: commented due to "IDEA-61938 Libraries configuration is kept if switching branches" // because of save which may happen _before_ project reload ;( // ApplicationManager.getApplication().invokeLater(new Runnable() { // public void run() { // IdeEventQueue.getInstance().addIdleListener(new Runnable() { // @Override // public void run() { // IdeEventQueue.getInstance().removeIdleListener(this); ApplicationManager.getApplication() .invokeLater( new Runnable() { @Override public void run() { if (!tryToReloadApplication()) return; askToReloadProjectIfConfigFilesChangedExternally(); } }, ModalityState.NON_MODAL); // } // }, 2000); // } // }, ModalityState.NON_MODAL); } @Override public void openTestProject(@NotNull final Project project) { synchronized (myOpenProjects) { assert ApplicationManager.getApplication().isUnitTestMode(); assert !project.isDisposed() : "Must not open already disposed project"; myTestProjects.add(project); } } @Override public Collection<Project> closeTestProject(@NotNull Project project) { synchronized (myOpenProjects) { assert ApplicationManager.getApplication().isUnitTestMode(); myTestProjects.remove(project); return myTestProjects; } } @Override public void saveChangedProjectFile(final VirtualFile file, final Project project) { if (file.exists()) { copyToTemp(file); } registerProjectToReload(project, file, null); } private void saveChangedProjectFile( final VirtualFile file, @Nullable final Project project, final StateStorage storage) { if (file.exists()) { copyToTemp(file); } registerProjectToReload(project, file, storage); } private void registerProjectToReload( @Nullable final Project project, final VirtualFile cause, @Nullable final StateStorage storage) { if (LOG.isDebugEnabled()) { LOG.debug("[RELOAD] Registering project to reload: " + cause, new Exception()); } if (project != null) { synchronized (myChangedProjectFiles) { List<Pair<VirtualFile, StateStorage>> changedProjectFiles = myChangedProjectFiles.get(project); if (changedProjectFiles == null) { changedProjectFiles = new ArrayList<Pair<VirtualFile, StateStorage>>(); myChangedProjectFiles.put(project, changedProjectFiles); } changedProjectFiles.add(new Pair<VirtualFile, StateStorage>(cause, storage)); } } else { myChangedApplicationFiles.add(new Pair<VirtualFile, StateStorage>(cause, storage)); } myChangedFilesAlarm.cancelAllRequests(); myChangedFilesAlarm.addRequest( new Runnable() { @Override public void run() { LOG.debug( "[RELOAD] Scheduling reload application & project, myReloadBlockCount = " + myReloadBlockCount); if (myReloadBlockCount.get() == 0) { scheduleReloadApplicationAndProject(); } } }, 444); } private void copyToTemp(VirtualFile file) { try { final byte[] bytes = file.contentsToByteArray(); mySavedCopies.put(file, bytes); mySavedTimestamps.put(file, file.getTimeStamp()); } catch (IOException e) { LOG.error(e); } } private void restoreCopy(VirtualFile file) { try { if (file == null) return; // Externally deleted actually. if (!file.isWritable()) return; // IDEA was unable to save it as well. So no need to restore. final byte[] bytes = mySavedCopies.get(file); if (bytes != null) { try { file.setBinaryContent(bytes, -1, mySavedTimestamps.get(file)); } catch (IOException e) { Messages.showWarningDialog( ProjectBundle.message("project.reload.write.failed", file.getPresentableUrl()), ProjectBundle.message("project.reload.write.failed.title")); } } } finally { mySavedCopies.remove(file); mySavedTimestamps.remove(file); } } @Override public void reloadProject(@NotNull final Project p) { reloadProjectImpl(p, true); } public void reloadProjectImpl(@NotNull final Project p, final boolean clearCopyToRestore) { if (clearCopyToRestore) { mySavedCopies.clear(); mySavedTimestamps.clear(); } final Project[] project = {p}; ProjectReloadState.getInstance(project[0]).onBeforeAutomaticProjectReload(); final Application application = ApplicationManager.getApplication(); application.invokeLater( new Runnable() { @Override public void run() { LOG.debug("Reloading project."); ProjectImpl projectImpl = (ProjectImpl) project[0]; if (projectImpl.isDisposed()) return; IProjectStore projectStore = projectImpl.getStateStore(); final String location = projectImpl.getPresentableUrl(); final List<IFile> original; try { final IComponentStore.SaveSession saveSession = projectStore.startSave(); original = saveSession.getAllStorageFiles(true); saveSession.finishSave(); } catch (IOException e) { LOG.error(e); return; } if (project[0].isDisposed() || ProjectUtil.closeAndDispose(project[0])) { application.runWriteAction( new Runnable() { @Override public void run() { for (final IFile originalFile : original) { restoreCopy( LocalFileSystem.getInstance().refreshAndFindFileByIoFile(originalFile)); } } }); project[0] = null; // Let it go. ProjectUtil.openProject(location, null, true); } } }, ModalityState.NON_MODAL); } @Override public boolean closeProject(@NotNull final Project project) { return closeProject(project, true, false, true); } public boolean closeProject( @NotNull final Project project, final boolean save, final boolean dispose, boolean checkCanClose) { if (isLight(project)) { throw new AssertionError("must not close light project"); } if (!isProjectOpened(project)) return true; if (checkCanClose && !canClose(project)) return false; final ShutDownTracker shutDownTracker = ShutDownTracker.getInstance(); shutDownTracker.registerStopperThread(Thread.currentThread()); try { if (save) { FileDocumentManager.getInstance().saveAllDocuments(); project.save(); } if (checkCanClose && !ensureCouldCloseIfUnableToSave(project)) { return false; } fireProjectClosing(project); // somebody can start progress here, do not wrap in write action ApplicationManager.getApplication() .runWriteAction( new Runnable() { @Override public void run() { synchronized (myOpenProjects) { myOpenProjects.remove(project); cacheOpenProjects(); myTestProjects.remove(project); } myChangedProjectFiles.remove(project); fireProjectClosed(project); if (dispose) { Disposer.dispose(project); } } }); } finally { shutDownTracker.unregisterStopperThread(Thread.currentThread()); } return true; } public static boolean isLight(@NotNull Project project) { return ApplicationManager.getApplication().isUnitTestMode() && project.toString().contains("light_temp_"); } @Override public boolean closeAndDispose(@NotNull final Project project) { return closeProject(project, true, true, true); } private void fireProjectClosing(Project project) { if (LOG.isDebugEnabled()) { LOG.debug("enter: fireProjectClosing()"); } for (ProjectManagerListener listener : myListeners) { try { listener.projectClosing(project); } catch (Exception e) { LOG.error(e); } } } @Override public void addProjectManagerListener(@NotNull ProjectManagerListener listener) { myListeners.add(listener); } @Override public void addProjectManagerListener( @NotNull final ProjectManagerListener listener, @NotNull Disposable parentDisposable) { addProjectManagerListener(listener); Disposer.register( parentDisposable, new Disposable() { @Override public void dispose() { removeProjectManagerListener(listener); } }); } @Override public void removeProjectManagerListener(@NotNull ProjectManagerListener listener) { boolean removed = myListeners.remove(listener); LOG.assertTrue(removed); } @Override public void addProjectManagerListener( @NotNull Project project, @NotNull ProjectManagerListener listener) { List<ProjectManagerListener> listeners = project.getUserData(LISTENERS_IN_PROJECT_KEY); if (listeners == null) { listeners = ((UserDataHolderEx) project) .putUserDataIfAbsent( LISTENERS_IN_PROJECT_KEY, ContainerUtil.<ProjectManagerListener>createLockFreeCopyOnWriteList()); } listeners.add(listener); } @Override public void removeProjectManagerListener( @NotNull Project project, @NotNull ProjectManagerListener listener) { List<ProjectManagerListener> listeners = project.getUserData(LISTENERS_IN_PROJECT_KEY); LOG.assertTrue(listeners != null); boolean removed = listeners.remove(listener); LOG.assertTrue(removed); } private void fireProjectOpened(Project project) { if (LOG.isDebugEnabled()) { LOG.debug("projectOpened"); } for (ProjectManagerListener listener : myListeners) { try { listener.projectOpened(project); } catch (Exception e) { LOG.error(e); } } } private void fireProjectClosed(Project project) { if (LOG.isDebugEnabled()) { LOG.debug("projectClosed"); } for (ProjectManagerListener listener : myListeners) { try { listener.projectClosed(project); } catch (Exception e) { LOG.error(e); } } } @Override public boolean canClose(Project project) { if (LOG.isDebugEnabled()) { LOG.debug("enter: canClose()"); } for (ProjectManagerListener listener : myListeners) { try { if (!listener.canCloseProject(project)) return false; } catch (Throwable e) { LOG.warn(e); // DO NOT LET ANY PLUGIN to prevent closing due to exception } } return true; } private static boolean ensureCouldCloseIfUnableToSave(@NotNull final Project project) { final ProjectImpl.UnableToSaveProjectNotification[] notifications = NotificationsManager.getNotificationsManager() .getNotificationsOfType(ProjectImpl.UnableToSaveProjectNotification.class, project); if (notifications.length == 0) return true; final String fileNames = StringUtil.join(notifications[0].getFileNames(), "\n"); final String msg = String.format( "%s was unable to save some project files,\nare you sure you want to close this project anyway?", ApplicationNamesInfo.getInstance().getProductName()); return Messages.showDialog( project, msg, "Unsaved Project", "Read-only files:\n\n" + fileNames, new String[] {"Yes", "No"}, 0, 1, Messages.getWarningIcon()) == 0; } @Override public void writeExternal(Element parentNode) throws WriteExternalException { if (myDefaultProject != null) { myDefaultProject.save(); } if (myDefaultProjectRootElement == null) { // read external isn't called if config folder is absent myDefaultProjectRootElement = new Element(ELEMENT_DEFAULT_PROJECT); } myDefaultProjectRootElement.detach(); parentNode.addContent(myDefaultProjectRootElement); } public void setDefaultProjectRootElement(final Element defaultProjectRootElement) { myDefaultProjectRootElement = defaultProjectRootElement; } @Override public void readExternal(Element parentNode) throws InvalidDataException { myDefaultProjectRootElement = parentNode.getChild(ELEMENT_DEFAULT_PROJECT); if (myDefaultProjectRootElement == null) { myDefaultProjectRootElement = new Element(ELEMENT_DEFAULT_PROJECT); } myDefaultProjectRootElement.detach(); } @Override public String getExternalFileName() { return "project.default"; } @Override @NotNull public String getComponentName() { return "ProjectManager"; } @Override @NotNull public File[] getExportFiles() { return new File[] {PathManager.getOptionsFile(this)}; } @Override @NotNull public String getPresentableName() { return ProjectBundle.message("project.default.settings"); } }
/** * Train a Deep Learning neural net model * * @param model Input model (e.g., from initModel(), or from a previous training run) * @return Trained model */ public final DeepLearningModel trainModel(DeepLearningModel model) { Frame validScoreFrame = null; Frame train, trainScoreFrame; try { // if (checkpoint == null && !quiet_mode) logStart(); //if checkpoint is given, some // Job's params might be uninitialized (but the restarted model's parameters are correct) if (model == null) { model = DKV.get(dest()).get(); } Log.info( "Model category: " + (_parms._autoencoder ? "Auto-Encoder" : isClassifier() ? "Classification" : "Regression")); final long model_size = model.model_info().size(); Log.info( "Number of model parameters (weights/biases): " + String.format("%,d", model_size)); model.write_lock(_job); _job.update(0, "Setting up training data..."); final DeepLearningParameters mp = model.model_info().get_params(); // temporary frames of the same "name" as the orig _train/_valid (asking the parameter's // Key, not the actual frame) // Note: don't put into DKV or they would overwrite the _train/_valid frames! Frame tra_fr = new Frame(mp._train, _train.names(), _train.vecs()); Frame val_fr = _valid != null ? new Frame(mp._valid, _valid.names(), _valid.vecs()) : null; train = tra_fr; if (model._output.isClassifier() && mp._balance_classes) { _job.update(0, "Balancing class distribution of training data..."); float[] trainSamplingFactors = new float [train .lastVec() .domain() .length]; // leave initialized to 0 -> will be filled up below if (mp._class_sampling_factors != null) { if (mp._class_sampling_factors.length != train.lastVec().domain().length) throw new IllegalArgumentException( "class_sampling_factors must have " + train.lastVec().domain().length + " elements"); trainSamplingFactors = mp._class_sampling_factors.clone(); // clone: don't modify the original } train = sampleFrameStratified( train, train.lastVec(), train.vec(model._output.weightsName()), trainSamplingFactors, (long) (mp._max_after_balance_size * train.numRows()), mp._seed, true, false); Vec l = train.lastVec(); Vec w = train.vec(model._output.weightsName()); MRUtils.ClassDist cd = new MRUtils.ClassDist(l); model._output._modelClassDist = _weights != null ? cd.doAll(l, w).rel_dist() : cd.doAll(l).rel_dist(); } model.training_rows = train.numRows(); if (_weights != null && _weights.min() == 0 && _weights.max() == 1 && _weights.isInt()) { model.training_rows = Math.round(train.numRows() * _weights.mean()); Log.warn( "Not counting " + (train.numRows() - model.training_rows) + " rows with weight=0 towards an epoch."); } Log.info("One epoch corresponds to " + model.training_rows + " training data rows."); trainScoreFrame = sampleFrame( train, mp._score_training_samples, mp._seed); // training scoring dataset is always sampled uniformly from the training // dataset if (trainScoreFrame != train) Scope.track(trainScoreFrame); if (!_parms._quiet_mode) Log.info("Number of chunks of the training data: " + train.anyVec().nChunks()); if (val_fr != null) { model.validation_rows = val_fr.numRows(); // validation scoring dataset can be sampled in multiple ways from the given validation // dataset if (model._output.isClassifier() && mp._balance_classes && mp._score_validation_sampling == DeepLearningParameters.ClassSamplingMethod.Stratified) { _job.update(0, "Sampling validation data (stratified)..."); validScoreFrame = sampleFrameStratified( val_fr, val_fr.lastVec(), val_fr.vec(model._output.weightsName()), null, mp._score_validation_samples > 0 ? mp._score_validation_samples : val_fr.numRows(), mp._seed + 1, false /* no oversampling */, false); } else { _job.update(0, "Sampling validation data..."); validScoreFrame = sampleFrame(val_fr, mp._score_validation_samples, mp._seed + 1); if (validScoreFrame != val_fr) Scope.track(validScoreFrame); } if (!_parms._quiet_mode) Log.info( "Number of chunks of the validation data: " + validScoreFrame.anyVec().nChunks()); } // Set train_samples_per_iteration size (cannot be done earlier since this depends on // whether stratified sampling is done) model.actual_train_samples_per_iteration = computeTrainSamplesPerIteration(mp, model.training_rows, model); // Determine whether shuffling is enforced if (mp._replicate_training_data && (model.actual_train_samples_per_iteration == model.training_rows * (mp._single_node_mode ? 1 : H2O.CLOUD.size())) && !mp._shuffle_training_data && H2O.CLOUD.size() > 1 && !mp._reproducible) { if (!mp._quiet_mode) Log.info( "Enabling training data shuffling, because all nodes train on the full dataset (replicated training data)."); mp._shuffle_training_data = true; } if (!mp._shuffle_training_data && model.actual_train_samples_per_iteration == model.training_rows && train.anyVec().nChunks() == 1) { if (!mp._quiet_mode) Log.info( "Enabling training data shuffling to avoid training rows in the same order over and over (no Hogwild since there's only 1 chunk)."); mp._shuffle_training_data = true; } // if (!mp._quiet_mode) Log.info("Initial model:\n" + model.model_info()); long now = System.currentTimeMillis(); model._timeLastIterationEnter = now; if (_parms._autoencoder) { _job.update(0, "Scoring null model of autoencoder..."); if (!mp._quiet_mode) Log.info("Scoring the null model of the autoencoder."); model.doScoring( trainScoreFrame, validScoreFrame, _job._key, 0, false); // get the null model reconstruction error } // put the initial version of the model into DKV model.update(_job); model.total_setup_time_ms += now - _job.start_time(); Log.info("Total setup time: " + PrettyPrint.msecs(model.total_setup_time_ms, true)); Log.info("Starting to train the Deep Learning model."); _job.update(0, "Training..."); // main loop for (; ; ) { model.iterations++; model.set_model_info( mp._epochs == 0 ? model.model_info() : H2O.CLOUD.size() > 1 && mp._replicate_training_data ? (mp._single_node_mode ? new DeepLearningTask2( _job._key, train, model.model_info(), rowFraction(train, mp, model), model.iterations) .doAll(Key.make(H2O.SELF)) .model_info() : // replicated data + single node mode new DeepLearningTask2( _job._key, train, model.model_info(), rowFraction(train, mp, model), model.iterations) .doAllNodes() .model_info()) : // replicated data + multi-node mode new DeepLearningTask( _job._key, model.model_info(), rowFraction(train, mp, model), model.iterations) .doAll(train) .model_info()); // distributed data (always in multi-node mode) if (stop_requested() && !timeout()) break; // cancellation if (!model.doScoring( trainScoreFrame, validScoreFrame, _job._key, model.iterations, false)) break; // finished training (or early stopping or convergence) if (timeout()) break; // stop after scoring } // replace the model with the best model so far (if it's better) if (!stop_requested() && _parms._overwrite_with_best_model && model.actual_best_model_key != null && _parms._nfolds == 0) { DeepLearningModel best_model = DKV.getGet(model.actual_best_model_key); if (best_model != null && best_model.loss() < model.loss() && Arrays.equals(best_model.model_info().units, model.model_info().units)) { if (!_parms._quiet_mode) Log.info("Setting the model to be the best model so far (based on scoring history)."); DeepLearningModelInfo mi = best_model.model_info().deep_clone(); // Don't cheat - count full amount of training samples, since that's the amount of // training it took to train (without finding anything better) mi.set_processed_global(model.model_info().get_processed_global()); mi.set_processed_local(model.model_info().get_processed_local()); model.set_model_info(mi); model.update(_job); model.doScoring(trainScoreFrame, validScoreFrame, _job._key, model.iterations, true); assert (best_model.loss() == model.loss()); } } // store coefficient names for future use // possibly change model.model_info().data_info().coefNames(); if (!_parms._quiet_mode) { Log.info( "=============================================================================================================================================================================="); if (stop_requested()) { Log.info("Deep Learning model training was interrupted."); } else { Log.info("Finished training the Deep Learning model."); Log.info(model); } Log.info( "=============================================================================================================================================================================="); } } finally { if (model != null) { model.deleteElasticAverageModels(); model.unlock(_job); if (model.actual_best_model_key != null) { assert (model.actual_best_model_key != model._key); DKV.remove(model.actual_best_model_key); } } } return model; }
public class JavaCompletionUtil { private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.completion.JavaCompletionUtil"); public static final Key<PairFunction<PsiExpression, CompletionParameters, PsiType>> DYNAMIC_TYPE_EVALUATOR = Key.create("DYNAMIC_TYPE_EVALUATOR"); private static final Key<PsiType> QUALIFIER_TYPE_ATTR = Key.create("qualifierType"); // SmartPsiElementPointer to PsiType of "qualifier" public static final OffsetKey LPAREN_OFFSET = OffsetKey.create("lparen"); public static final OffsetKey RPAREN_OFFSET = OffsetKey.create("rparen"); public static final OffsetKey ARG_LIST_END_OFFSET = OffsetKey.create("argListEnd"); static final NullableLazyKey<ExpectedTypeInfo[], CompletionLocation> EXPECTED_TYPES = NullableLazyKey.create( "expectedTypes", new NullableFunction<CompletionLocation, ExpectedTypeInfo[]>() { @Override @Nullable public ExpectedTypeInfo[] fun(final CompletionLocation location) { if (PsiJavaPatterns.psiElement() .beforeLeaf(PsiJavaPatterns.psiElement().withText(".")) .accepts(location.getCompletionParameters().getPosition())) { return ExpectedTypeInfo.EMPTY_ARRAY; } return JavaSmartCompletionContributor.getExpectedTypes( location.getCompletionParameters()); } }); private static final ElementPattern<PsiElement> LEFT_PAREN = psiElement(JavaTokenType.LPARENTH) .andOr( psiElement().withParent(PsiExpressionList.class), psiElement().afterLeaf(".", PsiKeyword.NEW)); public static final Key<Boolean> SUPER_METHOD_PARAMETERS = Key.create("SUPER_METHOD_PARAMETERS"); @Nullable public static Set<PsiType> getExpectedTypes(final CompletionParameters parameters) { final PsiExpression expr = PsiTreeUtil.getContextOfType(parameters.getPosition(), PsiExpression.class, true); if (expr != null) { final Set<PsiType> set = new THashSet<PsiType>(); for (final ExpectedTypeInfo expectedInfo : JavaSmartCompletionContributor.getExpectedTypes(parameters)) { set.add(expectedInfo.getType()); } return set; } return null; } private static final Key<List<SmartPsiElementPointer<PsiMethod>>> ALL_METHODS_ATTRIBUTE = Key.create("allMethods"); public static PsiType getQualifierType(LookupItem item) { return item.getUserData(QUALIFIER_TYPE_ATTR); } public static void completeVariableNameForRefactoring( Project project, Set<LookupElement> set, String prefix, PsiType varType, VariableKind varKind) { final CamelHumpMatcher camelHumpMatcher = new CamelHumpMatcher(prefix); JavaMemberNameCompletionContributor.completeVariableNameForRefactoring( project, set, camelHumpMatcher, varType, varKind, true, false); } public static void putAllMethods(LookupElement item, List<PsiMethod> methods) { item.putUserData( ALL_METHODS_ATTRIBUTE, ContainerUtil.map( methods, new Function<PsiMethod, SmartPsiElementPointer<PsiMethod>>() { @Override public SmartPsiElementPointer<PsiMethod> fun(PsiMethod method) { return SmartPointerManager.getInstance(method.getProject()) .createSmartPsiElementPointer(method); } })); } public static List<PsiMethod> getAllMethods(LookupElement item) { List<SmartPsiElementPointer<PsiMethod>> pointers = item.getUserData(ALL_METHODS_ATTRIBUTE); if (pointers == null) return null; return ContainerUtil.mapNotNull( pointers, new Function<SmartPsiElementPointer<PsiMethod>, PsiMethod>() { @Override public PsiMethod fun(SmartPsiElementPointer<PsiMethod> pointer) { return pointer.getElement(); } }); } public static String[] completeVariableNameForRefactoring( JavaCodeStyleManager codeStyleManager, @Nullable final PsiType varType, final VariableKind varKind, SuggestedNameInfo suggestedNameInfo) { return JavaMemberNameCompletionContributor.completeVariableNameForRefactoring( codeStyleManager, new CamelHumpMatcher(""), varType, varKind, suggestedNameInfo, true, false); } public static boolean isInExcludedPackage( @NotNull final PsiMember member, boolean allowInstanceInnerClasses) { final String name = PsiUtil.getMemberQualifiedName(member); if (name == null) return false; if (!member.hasModifierProperty(PsiModifier.STATIC)) { if (member instanceof PsiMethod || member instanceof PsiField) { return false; } if (allowInstanceInnerClasses && member instanceof PsiClass && member.getContainingClass() != null) { return false; } } return ProjectCodeInsightSettings.getSettings(member.getProject()).isExcluded(name); } @SuppressWarnings({"unchecked"}) @NotNull public static <T extends PsiType> T originalize(@NotNull T type) { if (!type.isValid()) { return type; } T result = new PsiTypeMapper() { private final Set<PsiClassType> myVisited = ContainerUtil.newIdentityTroveSet(); @Override public PsiType visitClassType(final PsiClassType classType) { if (!myVisited.add(classType)) return classType; final PsiClassType.ClassResolveResult classResolveResult = classType.resolveGenerics(); final PsiClass psiClass = classResolveResult.getElement(); final PsiSubstitutor substitutor = classResolveResult.getSubstitutor(); if (psiClass == null) return classType; return new PsiImmediateClassType( CompletionUtil.getOriginalOrSelf(psiClass), originalizeSubstitutor(substitutor)); } private PsiSubstitutor originalizeSubstitutor(final PsiSubstitutor substitutor) { PsiSubstitutor originalSubstitutor = PsiSubstitutor.EMPTY; for (final Map.Entry<PsiTypeParameter, PsiType> entry : substitutor.getSubstitutionMap().entrySet()) { final PsiType value = entry.getValue(); originalSubstitutor = originalSubstitutor.put( CompletionUtil.getOriginalOrSelf(entry.getKey()), value == null ? null : mapType(value)); } return originalSubstitutor; } @Override public PsiType visitType(PsiType type) { return type; } }.mapType(type); if (result == null) { throw new AssertionError("Null result for type " + type + " of class " + type.getClass()); } return result; } public static void initOffsets(final PsiFile file, final OffsetMap offsetMap) { int offset = Math.max( offsetMap.getOffset(CompletionInitializationContext.SELECTION_END_OFFSET), offsetMap.getOffset(CompletionInitializationContext.IDENTIFIER_END_OFFSET)); PsiElement element = file.findElementAt(offset); if (element instanceof PsiWhiteSpace && (!element.textContains('\n') || CodeStyleSettingsManager.getSettings(file.getProject()) .getCommonSettings(JavaLanguage.INSTANCE) .METHOD_PARAMETERS_LPAREN_ON_NEXT_LINE)) { element = file.findElementAt(element.getTextRange().getEndOffset()); } if (element == null) return; if (LEFT_PAREN.accepts(element)) { offsetMap.addOffset(LPAREN_OFFSET, element.getTextRange().getStartOffset()); PsiElement list = element.getParent(); PsiElement last = list.getLastChild(); if (last instanceof PsiJavaToken && ((PsiJavaToken) last).getTokenType() == JavaTokenType.RPARENTH) { offsetMap.addOffset(RPAREN_OFFSET, last.getTextRange().getStartOffset()); } offsetMap.addOffset(ARG_LIST_END_OFFSET, list.getTextRange().getEndOffset()); } } public static void resetParensInfo(final OffsetMap offsetMap) { offsetMap.removeOffset(LPAREN_OFFSET); offsetMap.removeOffset(RPAREN_OFFSET); offsetMap.removeOffset(ARG_LIST_END_OFFSET); offsetMap.removeOffset(CompletionInitializationContext.IDENTIFIER_END_OFFSET); } @Nullable public static List<? extends PsiElement> getAllPsiElements(final LookupElement item) { List<PsiMethod> allMethods = getAllMethods(item); if (allMethods != null) return allMethods; if (item.getObject() instanceof PsiElement) return Collections.singletonList((PsiElement) item.getObject()); return null; } @Nullable public static PsiType getLookupElementType(final LookupElement element) { TypedLookupItem typed = element.as(TypedLookupItem.CLASS_CONDITION_KEY); return typed != null ? typed.getType() : null; } @Nullable public static PsiType getQualifiedMemberReferenceType( @Nullable PsiType qualifierType, @NotNull final PsiMember member) { final Ref<PsiSubstitutor> subst = Ref.create(PsiSubstitutor.EMPTY); class MyProcessor extends BaseScopeProcessor implements NameHint, ElementClassHint { @Override public boolean execute(@NotNull PsiElement element, @NotNull ResolveState state) { if (element == member) { subst.set(state.get(PsiSubstitutor.KEY)); } return true; } @Override public String getName(@NotNull ResolveState state) { return member.getName(); } @Override public boolean shouldProcess(DeclarationKind kind) { return member instanceof PsiEnumConstant ? kind == DeclarationKind.ENUM_CONST : member instanceof PsiField ? kind == DeclarationKind.FIELD : kind == DeclarationKind.METHOD; } @Override public <T> T getHint(@NotNull Key<T> hintKey) { return hintKey == NameHint.KEY || hintKey == ElementClassHint.KEY ? (T) this : null; } } PsiScopesUtil.processTypeDeclarations(qualifierType, member, new MyProcessor()); PsiType rawType = member instanceof PsiField ? ((PsiField) member).getType() : member instanceof PsiMethod ? ((PsiMethod) member).getReturnType() : JavaPsiFacade.getElementFactory(member.getProject()) .createType((PsiClass) member); return subst.get().substitute(rawType); } public static Set<LookupElement> processJavaReference( PsiElement element, PsiJavaReference javaReference, ElementFilter elementFilter, JavaCompletionProcessor.Options options, final PrefixMatcher matcher, CompletionParameters parameters) { final Set<LookupElement> set = new LinkedHashSet<LookupElement>(); final Condition<String> nameCondition = new Condition<String>() { @Override public boolean value(String s) { return matcher.prefixMatches(s); } }; PsiMethodCallExpression call = PsiTreeUtil.getParentOfType(element, PsiMethodCallExpression.class); boolean checkInitialized = parameters.getInvocationCount() <= 1 && call != null && PsiKeyword.SUPER.equals(call.getMethodExpression().getText()); final JavaCompletionProcessor processor = new JavaCompletionProcessor( element, elementFilter, options.withInitialized(checkInitialized), nameCondition); final PsiType plainQualifier = processor.getQualifierType(); PsiType qualifierType = plainQualifier; PsiType runtimeQualifier = getQualifierCastType(javaReference, parameters); if (runtimeQualifier != null) { PsiType composite = qualifierType == null ? runtimeQualifier : PsiIntersectionType.createIntersection(qualifierType, runtimeQualifier); PsiElement ctx = createContextWithXxxVariable(element, composite); javaReference = (PsiReferenceExpression) JavaPsiFacade.getElementFactory(element.getProject()) .createExpressionFromText("xxx.xxx", ctx); qualifierType = runtimeQualifier; processor.setQualifierType(qualifierType); } javaReference.processVariants(processor); final PsiTypeLookupItem castItem = runtimeQualifier == null ? null : PsiTypeLookupItem.createLookupItem( runtimeQualifier, (PsiReferenceExpression) javaReference); final boolean pkgContext = inSomePackage(element); final Set<PsiMember> mentioned = new THashSet<PsiMember>(); for (CompletionElement completionElement : processor.getResults()) { for (LookupElement item : createLookupElements(completionElement, javaReference)) { item.putUserData(QUALIFIER_TYPE_ATTR, qualifierType); final Object o = item.getObject(); if (o instanceof PsiClass && !isSourceLevelAccessible(element, (PsiClass) o, pkgContext)) { continue; } if (o instanceof PsiMember) { if (isInExcludedPackage((PsiMember) o, true)) { continue; } mentioned.add(CompletionUtil.getOriginalOrSelf((PsiMember) o)); } set.add( highlightIfNeeded( qualifierType, castQualifier(item, castItem, plainQualifier, processor), o, element)); } } if (javaReference instanceof PsiJavaCodeReferenceElement && !((PsiJavaCodeReferenceElement) javaReference).isQualified()) { final StaticMemberProcessor memberProcessor = new JavaStaticMemberProcessor(parameters); memberProcessor.processMembersOfRegisteredClasses( matcher, new PairConsumer<PsiMember, PsiClass>() { @Override public void consume(PsiMember member, PsiClass psiClass) { if (!mentioned.contains(member) && processor.satisfies(member, ResolveState.initial())) { set.add(memberProcessor.createLookupElement(member, psiClass, true)); } } }); } return set; } @Nullable private static PsiType getQualifierCastType( PsiJavaReference javaReference, CompletionParameters parameters) { if (javaReference instanceof PsiReferenceExpression) { final PsiReferenceExpression refExpr = (PsiReferenceExpression) javaReference; final PsiExpression qualifier = refExpr.getQualifierExpression(); if (qualifier != null) { final Project project = qualifier.getProject(); PsiType type = null; final PairFunction<PsiExpression, CompletionParameters, PsiType> evaluator = refExpr.getContainingFile().getCopyableUserData(DYNAMIC_TYPE_EVALUATOR); if (evaluator != null) { type = evaluator.fun(qualifier, parameters); } if (type == null) { type = GuessManager.getInstance(project).getControlFlowExpressionType(qualifier); } return type; } } return null; } @NotNull private static LookupElement castQualifier( @NotNull LookupElement item, @Nullable final PsiTypeLookupItem castTypeItem, @Nullable PsiType plainQualifier, JavaCompletionProcessor processor) { if (castTypeItem == null) { return item; } if (plainQualifier != null) { Object o = item.getObject(); if (o instanceof PsiMethod) { PsiType castType = castTypeItem.getType(); if (plainQualifier instanceof PsiClassType && castType instanceof PsiClassType) { PsiMethod method = (PsiMethod) o; PsiClassType.ClassResolveResult plainResult = ((PsiClassType) plainQualifier).resolveGenerics(); PsiClass plainClass = plainResult.getElement(); if (plainClass != null && plainClass.findMethodBySignature(method, true) != null) { PsiClass castClass = ((PsiClassType) castType).resolveGenerics().getElement(); if (castClass == null || !castClass.isInheritor(plainClass, true)) { return item; } PsiSubstitutor plainSub = plainResult.getSubstitutor(); PsiSubstitutor castSub = TypeConversionUtil.getSuperClassSubstitutor(plainClass, (PsiClassType) castType); PsiType returnType = method.getReturnType(); if (method.getSignature(plainSub).equals(method.getSignature(castSub))) { PsiType typeAfterCast = toRaw(castSub.substitute(returnType)); PsiType typeDeclared = toRaw(plainSub.substitute(returnType)); if (typeAfterCast != null && typeDeclared != null && typeAfterCast.isAssignableFrom(typeDeclared) && processor.isAccessible(plainClass.findMethodBySignature(method, true))) { return item; } } } } } else if (containsMember(plainQualifier, o)) { return item; } } return LookupElementDecorator.withInsertHandler( item, new InsertHandlerDecorator<LookupElement>() { @Override public void handleInsert( InsertionContext context, LookupElementDecorator<LookupElement> item) { final Document document = context.getEditor().getDocument(); context.commitDocument(); final PsiFile file = context.getFile(); final PsiJavaCodeReferenceElement ref = PsiTreeUtil.findElementOfClassAtOffset( file, context.getStartOffset(), PsiJavaCodeReferenceElement.class, false); if (ref != null) { final PsiElement qualifier = ref.getQualifier(); if (qualifier != null) { final CommonCodeStyleSettings settings = context.getCodeStyleSettings(); final String parenSpace = settings.SPACE_WITHIN_PARENTHESES ? " " : ""; document.insertString(qualifier.getTextRange().getEndOffset(), parenSpace + ")"); final String spaceWithin = settings.SPACE_WITHIN_CAST_PARENTHESES ? " " : ""; final String prefix = "(" + parenSpace + "(" + spaceWithin; final String spaceAfter = settings.SPACE_AFTER_TYPE_CAST ? " " : ""; final int exprStart = qualifier.getTextRange().getStartOffset(); document.insertString(exprStart, prefix + spaceWithin + ")" + spaceAfter); CompletionUtil.emulateInsertion(context, exprStart + prefix.length(), castTypeItem); PsiDocumentManager.getInstance(file.getProject()) .doPostponedOperationsAndUnblockDocument(document); context.getEditor().getCaretModel().moveToOffset(context.getTailOffset()); } } item.getDelegate().handleInsert(context); } }); } @Nullable private static PsiType toRaw(@Nullable PsiType type) { return type instanceof PsiClassType ? ((PsiClassType) type).rawType() : type; } @NotNull public static LookupElement highlightIfNeeded( @Nullable PsiType qualifierType, @NotNull LookupElement item, @NotNull Object object, @NotNull PsiElement place) { if (shouldMarkRed(object, place)) { return PrioritizedLookupElement.withExplicitProximity( LookupElementDecorator.withRenderer( item, new LookupElementRenderer<LookupElementDecorator<LookupElement>>() { @Override public void renderElement( LookupElementDecorator<LookupElement> element, LookupElementPresentation presentation) { element.getDelegate().renderElement(presentation); presentation.setItemTextForeground(JBColor.RED); } }), -1); } if (containsMember(qualifierType, object)) { LookupElementRenderer<LookupElementDecorator<LookupElement>> boldRenderer = new LookupElementRenderer<LookupElementDecorator<LookupElement>>() { @Override public void renderElement( LookupElementDecorator<LookupElement> element, LookupElementPresentation presentation) { element.getDelegate().renderElement(presentation); presentation.setItemTextBold(true); } }; return PrioritizedLookupElement.withExplicitProximity( LookupElementDecorator.withRenderer(item, boldRenderer), 1); } return item; } private static boolean shouldMarkRed(@NotNull Object object, @NotNull PsiElement place) { if (!(object instanceof PsiMember)) return false; if (Java15APIUsageInspectionBase.isForbiddenApiUsage( (PsiMember) object, PsiUtil.getLanguageLevel(place))) return true; if (object instanceof PsiEnumConstant) { return findConstantsUsedInSwitch(place) .contains(CompletionUtil.getOriginalOrSelf((PsiEnumConstant) object)); } return false; } public static boolean containsMember(@Nullable PsiType qualifierType, @NotNull Object object) { if (qualifierType instanceof PsiArrayType && object instanceof PsiMember) { // length and clone() PsiFile file = ((PsiMember) object).getContainingFile(); if (file == null || file.getVirtualFile() == null) { // yes, they're a bit dummy return true; } } else if (qualifierType instanceof PsiClassType) { PsiClass qualifierClass = ((PsiClassType) qualifierType).resolve(); if (qualifierClass == null) return false; if (object instanceof PsiMethod && qualifierClass.findMethodBySignature((PsiMethod) object, false) != null) { return true; } if (object instanceof PsiMember) { return qualifierClass.equals(((PsiMember) object).getContainingClass()); } } return false; } private static List<? extends LookupElement> createLookupElements( CompletionElement completionElement, PsiJavaReference reference) { Object completion = completionElement.getElement(); assert !(completion instanceof LookupElement); if (reference instanceof PsiJavaCodeReferenceElement) { if (completion instanceof PsiMethod && ((PsiJavaCodeReferenceElement) reference).getParent() instanceof PsiImportStaticStatement) { return Collections.singletonList( JavaLookupElementBuilder.forMethod((PsiMethod) completion, PsiSubstitutor.EMPTY)); } if (completion instanceof PsiClass) { return JavaClassNameCompletionContributor.createClassLookupItems( (PsiClass) completion, JavaClassNameCompletionContributor.AFTER_NEW.accepts(reference), JavaClassNameInsertHandler.JAVA_CLASS_INSERT_HANDLER, Conditions.<PsiClass>alwaysTrue()); } } if (reference instanceof PsiMethodReferenceExpression && completion instanceof PsiMethod && ((PsiMethod) completion).isConstructor()) { return Collections.singletonList( JavaLookupElementBuilder.forMethod( (PsiMethod) completion, "new", PsiSubstitutor.EMPTY, null)); } LookupElement _ret = LookupItemUtil.objectToLookupItem(completion); if (_ret instanceof LookupItem) { final PsiSubstitutor substitutor = completionElement.getSubstitutor(); if (substitutor != null) { ((LookupItem<?>) _ret).setAttribute(LookupItem.SUBSTITUTOR, substitutor); } } return Collections.singletonList(_ret); } public static boolean hasAccessibleConstructor(PsiType type) { if (type instanceof PsiArrayType) return true; final PsiClass psiClass = PsiUtil.resolveClassInType(type); if (psiClass == null || psiClass.isEnum() || psiClass.isAnnotationType()) return false; if (!(psiClass instanceof PsiCompiledElement)) return true; final PsiMethod[] methods = psiClass.getConstructors(); if (methods.length == 0) return true; for (final PsiMethod method : methods) { if (!method.hasModifierProperty(PsiModifier.PRIVATE)) return true; } return false; } public static LookupItem qualify(final LookupItem ret) { return ret.forceQualify(); } public static Set<String> getAllLookupStrings(@NotNull PsiMember member) { Set<String> allLookupStrings = ContainerUtil.newLinkedHashSet(); String name = member.getName(); allLookupStrings.add(name); PsiClass containingClass = member.getContainingClass(); while (containingClass != null) { final String className = containingClass.getName(); if (className == null) { break; } name = className + "." + name; allLookupStrings.add(name); final PsiElement parent = containingClass.getParent(); if (!(parent instanceof PsiClass)) { break; } containingClass = (PsiClass) parent; } return allLookupStrings; } public static LookupItem setShowFQN(final LookupItem ret) { ret.setAttribute( JavaPsiClassReferenceElement.PACKAGE_NAME, PsiFormatUtil.getPackageDisplayName((PsiClass) ret.getObject())); return ret; } public static boolean mayHaveSideEffects(@Nullable final PsiElement element) { return element instanceof PsiExpression && SideEffectChecker.mayHaveSideEffects((PsiExpression) element); } public static void insertClassReference( @NotNull PsiClass psiClass, @NotNull PsiFile file, int offset) { insertClassReference(psiClass, file, offset, offset); } public static int insertClassReference( PsiClass psiClass, PsiFile file, int startOffset, int endOffset) { final Project project = file.getProject(); PsiDocumentManager documentManager = PsiDocumentManager.getInstance(project); documentManager.commitAllDocuments(); final PsiManager manager = file.getManager(); final Document document = FileDocumentManager.getInstance().getDocument(file.getViewProvider().getVirtualFile()); final PsiReference reference = file.findReferenceAt(startOffset); if (reference != null) { final PsiElement resolved = reference.resolve(); if (resolved instanceof PsiClass) { if (((PsiClass) resolved).getQualifiedName() == null || manager.areElementsEquivalent(psiClass, resolved)) { return endOffset; } } } String name = psiClass.getName(); if (name == null) { return endOffset; } assert document != null; document.replaceString(startOffset, endOffset, name); int newEndOffset = startOffset + name.length(); final RangeMarker toDelete = insertTemporary(newEndOffset, document, " "); documentManager.commitAllDocuments(); PsiElement element = file.findElementAt(startOffset); if (element instanceof PsiIdentifier) { PsiElement parent = element.getParent(); if (parent instanceof PsiJavaCodeReferenceElement && !((PsiJavaCodeReferenceElement) parent).isQualified() && !(parent.getParent() instanceof PsiPackageStatement)) { PsiJavaCodeReferenceElement ref = (PsiJavaCodeReferenceElement) parent; if (psiClass.isValid() && !psiClass.getManager().areElementsEquivalent(psiClass, resolveReference(ref))) { final boolean staticImport = ref instanceof PsiImportStaticReferenceElement; PsiElement newElement; try { newElement = staticImport ? ((PsiImportStaticReferenceElement) ref).bindToTargetClass(psiClass) : ref.bindToElement(psiClass); } catch (IncorrectOperationException e) { return endOffset; // can happen if fqn contains reserved words, for example } final RangeMarker rangeMarker = document.createRangeMarker(newElement.getTextRange()); documentManager.doPostponedOperationsAndUnblockDocument(document); documentManager.commitDocument(document); newElement = CodeInsightUtilCore.findElementInRange( file, rangeMarker.getStartOffset(), rangeMarker.getEndOffset(), PsiJavaCodeReferenceElement.class, JavaLanguage.INSTANCE); rangeMarker.dispose(); if (newElement != null) { newEndOffset = newElement.getTextRange().getEndOffset(); if (!(newElement instanceof PsiReferenceExpression)) { PsiReferenceParameterList parameterList = ((PsiJavaCodeReferenceElement) newElement).getParameterList(); if (parameterList != null) { newEndOffset = parameterList.getTextRange().getStartOffset(); } } if (!staticImport && !psiClass .getManager() .areElementsEquivalent(psiClass, resolveReference((PsiReference) newElement)) && !PsiUtil.isInnerClass(psiClass)) { final String qName = psiClass.getQualifiedName(); if (qName != null) { document.replaceString( newElement.getTextRange().getStartOffset(), newEndOffset, qName); newEndOffset = newElement.getTextRange().getStartOffset() + qName.length(); } } } } } } if (toDelete.isValid()) { document.deleteString(toDelete.getStartOffset(), toDelete.getEndOffset()); } return newEndOffset; } @Nullable static PsiElement resolveReference(final PsiReference psiReference) { if (psiReference instanceof PsiPolyVariantReference) { final ResolveResult[] results = ((PsiPolyVariantReference) psiReference).multiResolve(true); if (results.length == 1) return results[0].getElement(); } return psiReference.resolve(); } public static RangeMarker insertTemporary( final int endOffset, final Document document, final String temporary) { final CharSequence chars = document.getCharsSequence(); final int length = chars.length(); final RangeMarker toDelete; if (endOffset < length && Character.isJavaIdentifierPart(chars.charAt(endOffset))) { document.insertString(endOffset, temporary); toDelete = document.createRangeMarker(endOffset, endOffset + 1); } else if (endOffset >= length) { toDelete = document.createRangeMarker(length, length); } else { toDelete = document.createRangeMarker(endOffset, endOffset); } toDelete.setGreedyToLeft(true); toDelete.setGreedyToRight(true); return toDelete; } public static void insertParentheses( final InsertionContext context, final LookupElement item, boolean overloadsMatter, boolean hasParams) { insertParentheses(context, item, overloadsMatter, hasParams, false); } public static void insertParentheses( final InsertionContext context, final LookupElement item, boolean overloadsMatter, boolean hasParams, final boolean forceClosingParenthesis) { final Editor editor = context.getEditor(); final char completionChar = context.getCompletionChar(); final PsiFile file = context.getFile(); final TailType tailType = completionChar == '(' ? TailType.NONE : completionChar == ':' ? TailType.COND_EXPR_COLON : LookupItem.handleCompletionChar(context.getEditor(), item, completionChar); final boolean hasTail = tailType != TailType.NONE && tailType != TailType.UNKNOWN; final boolean smart = completionChar == Lookup.COMPLETE_STATEMENT_SELECT_CHAR; if (completionChar == '(' || completionChar == '.' || completionChar == ',' || completionChar == ';' || completionChar == ':' || completionChar == ' ') { context.setAddCompletionChar(false); } if (hasTail) { hasParams = false; } final boolean needRightParenth = forceClosingParenthesis || !smart && (CodeInsightSettings.getInstance().AUTOINSERT_PAIR_BRACKET || !hasParams && completionChar != '('); context.commitDocument(); final CommonCodeStyleSettings styleSettings = context.getCodeStyleSettings(); final PsiElement elementAt = file.findElementAt(context.getStartOffset()); if (elementAt == null || !(elementAt.getParent() instanceof PsiMethodReferenceExpression)) { ParenthesesInsertHandler.getInstance( hasParams, styleSettings.SPACE_BEFORE_METHOD_CALL_PARENTHESES, styleSettings.SPACE_WITHIN_METHOD_CALL_PARENTHESES && hasParams, needRightParenth, styleSettings.METHOD_PARAMETERS_LPAREN_ON_NEXT_LINE) .handleInsert(context, item); } if (hasParams) { // Invoke parameters popup AutoPopupController.getInstance(file.getProject()) .autoPopupParameterInfo(editor, overloadsMatter ? null : (PsiElement) item.getObject()); } if (smart || !needRightParenth || !insertTail(context, item, tailType, hasTail)) { return; } if (completionChar == '.') { AutoPopupController.getInstance(file.getProject()) .autoPopupMemberLookup(context.getEditor(), null); } else if (completionChar == ',') { AutoPopupController.getInstance(file.getProject()) .autoPopupParameterInfo(context.getEditor(), null); } } public static boolean insertTail( InsertionContext context, LookupElement item, TailType tailType, boolean hasTail) { TailType toInsert = tailType; LookupItem<?> lookupItem = item.as(LookupItem.CLASS_CONDITION_KEY); if (lookupItem == null || lookupItem.getAttribute(LookupItem.TAIL_TYPE_ATTR) != TailType.UNKNOWN) { if (!hasTail && item.getObject() instanceof PsiMethod && ((PsiMethod) item.getObject()).getReturnType() == PsiType.VOID) { PsiDocumentManager.getInstance(context.getProject()).commitAllDocuments(); if (psiElement() .beforeLeaf(psiElement().withText(".")) .accepts(context.getFile().findElementAt(context.getTailOffset() - 1))) { return false; } boolean insertAdditionalSemicolon = true; final PsiReferenceExpression referenceExpression = PsiTreeUtil.getTopmostParentOfType( context.getFile().findElementAt(context.getStartOffset()), PsiReferenceExpression.class); if (referenceExpression instanceof PsiMethodReferenceExpression && LambdaHighlightingUtil.insertSemicolon(referenceExpression.getParent())) { insertAdditionalSemicolon = false; } else if (referenceExpression != null) { PsiElement parent = referenceExpression.getParent(); if (parent instanceof PsiMethodCallExpression) { parent = parent.getParent(); } if (parent instanceof PsiLambdaExpression && !LambdaHighlightingUtil.insertSemicolonAfter((PsiLambdaExpression) parent)) { insertAdditionalSemicolon = false; } } if (insertAdditionalSemicolon) { toInsert = TailType.SEMICOLON; } } } toInsert.processTail(context.getEditor(), context.getTailOffset()); return true; } // need to shorten references in type argument list public static void shortenReference(final PsiFile file, final int offset) throws IncorrectOperationException { Project project = file.getProject(); final PsiDocumentManager manager = PsiDocumentManager.getInstance(project); Document document = manager.getDocument(file); if (document == null) { PsiUtilCore.ensureValid(file); LOG.error("No document for " + file); return; } manager.commitDocument(document); final PsiReference ref = file.findReferenceAt(offset); if (ref != null) { PsiElement element = ref.getElement(); if (element != null) { JavaCodeStyleManager.getInstance(project).shortenClassReferences(element); PsiDocumentManager.getInstance(project).doPostponedOperationsAndUnblockDocument(document); } } } public static boolean inSomePackage(PsiElement context) { PsiFile contextFile = context.getContainingFile(); return contextFile instanceof PsiClassOwner && StringUtil.isNotEmpty(((PsiClassOwner) contextFile).getPackageName()); } public static boolean isSourceLevelAccessible( PsiElement context, PsiClass psiClass, final boolean pkgContext) { if (!JavaPsiFacade.getInstance(psiClass.getProject()) .getResolveHelper() .isAccessible(psiClass, context, null)) { return false; } if (pkgContext) { PsiClass topLevel = PsiUtil.getTopLevelClass(psiClass); if (topLevel != null) { String fqName = topLevel.getQualifiedName(); if (fqName != null && StringUtil.isEmpty(StringUtil.getPackageName(fqName))) { return false; } } } return true; } public static boolean promptTypeArgs(InsertionContext context, int offset) { if (offset < 0) { return false; } OffsetKey key = context.trackOffset(offset, false); PostprocessReformattingAspect.getInstance(context.getProject()).doPostponedFormatting(); offset = context.getOffset(key); if (offset < 0) { return false; } String open = escapeXmlIfNeeded(context, "<"); context.getDocument().insertString(offset, open); context.getEditor().getCaretModel().moveToOffset(offset + open.length()); context.getDocument().insertString(offset + open.length(), escapeXmlIfNeeded(context, ">")); context.setAddCompletionChar(false); return true; } public static FakePsiElement createContextWithXxxVariable( final PsiElement place, final PsiType varType) { return new FakePsiElement() { @Override public boolean processDeclarations( @NotNull PsiScopeProcessor processor, @NotNull ResolveState state, PsiElement lastParent, @NotNull PsiElement place) { return processor.execute( new LightVariableBuilder("xxx", varType, place), ResolveState.initial()); } @Override public PsiElement getParent() { return place; } }; } public static String escapeXmlIfNeeded(InsertionContext context, String generics) { if (context.getFile().getViewProvider().getBaseLanguage() == StdLanguages.JSPX) { return StringUtil.escapeXml(generics); } return generics; } }
public static ValueArray loadAndParseKey(String path) { return loadAndParseKey(Key.make(), path); }
/** Author: msk */ public class EditorsSplitters extends IdePanePanel implements UISettingsListener, Disposable { private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.fileEditor.impl.EditorsSplitters"); private static final String PINNED = "pinned"; private static final String CURRENT_IN_TAB = "current-in-tab"; private static final Key<Object> DUMMY_KEY = Key.create("EditorsSplitters.dummy.key"); private EditorWindow myCurrentWindow; private final Set<EditorWindow> myWindows = new CopyOnWriteArraySet<EditorWindow>(); private final FileEditorManagerImpl myManager; private Element mySplittersElement; // temporarily used during initialization int myInsideChange; private final MyFocusWatcher myFocusWatcher; private final Alarm myIconUpdaterAlarm = new Alarm(); private final UIBuilder myUIBuilder = new UIBuilder(); EditorsSplitters( final FileEditorManagerImpl manager, DockManager dockManager, boolean createOwnDockableContainer) { super(new BorderLayout()); myManager = manager; myFocusWatcher = new MyFocusWatcher(); setFocusTraversalPolicy(new MyFocusTraversalPolicy()); clear(); if (createOwnDockableContainer) { DockableEditorTabbedContainer dockable = new DockableEditorTabbedContainer(myManager.getProject(), this, false); Disposer.register(manager.getProject(), dockable); dockManager.register(dockable); } KeymapManagerListener keymapListener = new KeymapManagerListener() { @Override public void activeKeymapChanged(Keymap keymap) { invalidate(); repaint(); } }; KeymapManager.getInstance().addKeymapManagerListener(keymapListener, this); } public FileEditorManagerImpl getManager() { return myManager; } public void clear() { for (EditorWindow window : myWindows) { window.dispose(); } removeAll(); myWindows.clear(); setCurrentWindow(null); repaint(); // revalidate doesn't repaint correctly after "Close All" } void startListeningFocus() { myFocusWatcher.install(this); } private void stopListeningFocus() { myFocusWatcher.deinstall(this); } @Override public void dispose() { myIconUpdaterAlarm.cancelAllRequests(); stopListeningFocus(); } @Nullable public VirtualFile getCurrentFile() { if (myCurrentWindow != null) { return myCurrentWindow.getSelectedFile(); } return null; } private boolean showEmptyText() { return myCurrentWindow == null || myCurrentWindow.getFiles().length == 0; } @Override protected void paintComponent(Graphics g) { if (showEmptyText()) { Graphics2D gg = IdeBackgroundUtil.withFrameBackground(g, this); super.paintComponent(gg); g.setColor(UIUtil.isUnderDarcula() ? UIUtil.getBorderColor() : new Color(0, 0, 0, 50)); g.drawLine(0, 0, getWidth(), 0); } } public void writeExternal(final Element element) { if (getComponentCount() != 0) { final Component comp = getComponent(0); LOG.assertTrue(comp instanceof JPanel); final JPanel panel = (JPanel) comp; if (panel.getComponentCount() != 0) { element.addContent(writePanel(panel)); } } } @SuppressWarnings("HardCodedStringLiteral") private Element writePanel(final JPanel panel) { final Component comp = panel.getComponent(0); if (comp instanceof Splitter) { final Splitter splitter = (Splitter) comp; final Element res = new Element("splitter"); res.setAttribute("split-orientation", splitter.getOrientation() ? "vertical" : "horizontal"); res.setAttribute("split-proportion", Float.toString(splitter.getProportion())); final Element first = new Element("split-first"); first.addContent(writePanel((JPanel) splitter.getFirstComponent())); final Element second = new Element("split-second"); second.addContent(writePanel((JPanel) splitter.getSecondComponent())); res.addContent(first); res.addContent(second); return res; } else if (comp instanceof JBTabs) { final Element res = new Element("leaf"); Integer limit = UIUtil.getClientProperty( ((JBTabs) comp).getComponent(), JBTabsImpl.SIDE_TABS_SIZE_LIMIT_KEY); if (limit != null) { res.setAttribute(JBTabsImpl.SIDE_TABS_SIZE_LIMIT_KEY.toString(), String.valueOf(limit)); } writeWindow(res, findWindowWith(comp)); return res; } else if (comp instanceof EditorWindow.TCompForTablessMode) { EditorWithProviderComposite composite = ((EditorWindow.TCompForTablessMode) comp).myEditor; Element res = new Element("leaf"); res.addContent(writeComposite(composite.getFile(), composite, false, composite)); return res; } else { LOG.error(comp != null ? comp.getClass().getName() : null); return null; } } private void writeWindow(@NotNull Element res, @Nullable EditorWindow window) { if (window != null) { EditorWithProviderComposite[] composites = window.getEditors(); for (int i = 0; i < composites.length; i++) { VirtualFile file = window.getFileAt(i); res.addContent( writeComposite( file, composites[i], window.isFilePinned(file), window.getSelectedEditor())); } } } @NotNull private Element writeComposite( VirtualFile file, EditorWithProviderComposite composite, boolean pinned, EditorWithProviderComposite selectedEditor) { Element fileElement = new Element("file"); fileElement.setAttribute("leaf-file-name", file.getName()); // TODO: all files composite.currentStateAsHistoryEntry().writeExternal(fileElement, getManager().getProject()); fileElement.setAttribute(PINNED, Boolean.toString(pinned)); fileElement.setAttribute(CURRENT_IN_TAB, Boolean.toString(composite.equals(selectedEditor))); return fileElement; } public void openFiles() { if (mySplittersElement != null) { initializeProgress(); final JPanel comp = myUIBuilder.process(mySplittersElement, getTopPanel()); UIUtil.invokeAndWaitIfNeeded( new Runnable() { @Override public void run() { if (comp != null) { removeAll(); add(comp, BorderLayout.CENTER); mySplittersElement = null; } // clear empty splitters for (EditorWindow window : getWindows()) { if (window.getEditors().length == 0) { for (EditorWindow sibling : window.findSiblings()) { sibling.unsplit(false); } } } } }); } } private static void initializeProgress() { ProgressIndicator indicator = ProgressManager.getInstance().getProgressIndicator(); if (indicator != null) { indicator.setText(IdeBundle.message("loading.editors")); } } public int getEditorsCount() { return mySplittersElement == null ? 0 : countFiles(mySplittersElement); } private double myProgressStep; public void setProgressStep(double step) { myProgressStep = step; } private void updateProgress() { ProgressIndicator indicator = ProgressManager.getInstance().getProgressIndicator(); if (indicator != null) { indicator.setFraction(indicator.getFraction() + myProgressStep); } } private static int countFiles(Element element) { Integer value = new ConfigTreeReader<Integer>() { @Override protected Integer processFiles( @NotNull List<Element> fileElements, @Nullable Integer context) { return fileElements.size(); } @Override protected Integer processSplitter( @NotNull Element element, @Nullable Element firstChild, @Nullable Element secondChild, @Nullable Integer context) { Integer first = process(firstChild, null); Integer second = process(secondChild, null); return (first == null ? 0 : first) + (second == null ? 0 : second); } }.process(element, null); return value == null ? 0 : value; } public void readExternal(final Element element) { mySplittersElement = element; } @NotNull public VirtualFile[] getOpenFiles() { final Set<VirtualFile> files = new ArrayListSet<VirtualFile>(); for (final EditorWindow myWindow : myWindows) { final EditorWithProviderComposite[] editors = myWindow.getEditors(); for (final EditorWithProviderComposite editor : editors) { VirtualFile file = editor.getFile(); // background thread may call this method when invalid file is being removed // do not return it here as it will quietly drop out soon if (file.isValid()) { files.add(file); } } } return VfsUtilCore.toVirtualFileArray(files); } @NotNull public VirtualFile[] getSelectedFiles() { final Set<VirtualFile> files = new ArrayListSet<VirtualFile>(); for (final EditorWindow window : myWindows) { final VirtualFile file = window.getSelectedFile(); if (file != null) { files.add(file); } } final VirtualFile[] virtualFiles = VfsUtilCore.toVirtualFileArray(files); final VirtualFile currentFile = getCurrentFile(); if (currentFile != null) { for (int i = 0; i != virtualFiles.length; ++i) { if (Comparing.equal(virtualFiles[i], currentFile)) { virtualFiles[i] = virtualFiles[0]; virtualFiles[0] = currentFile; break; } } } return virtualFiles; } @NotNull public FileEditor[] getSelectedEditors() { List<FileEditor> editors = new ArrayList<FileEditor>(); Set<EditorWindow> windows = new THashSet<EditorWindow>(myWindows); final EditorWindow currentWindow = getCurrentWindow(); if (currentWindow != null) { windows.add(currentWindow); } for (final EditorWindow window : windows) { final EditorWithProviderComposite composite = window.getSelectedEditor(); if (composite != null) { editors.add(composite.getSelectedEditor()); } } return editors.toArray(new FileEditor[editors.size()]); } public void updateFileIcon(@NotNull final VirtualFile file) { updateFileIconLater(file); } private void updateFileIconImmediately(final VirtualFile file) { final Collection<EditorWindow> windows = findWindows(file); for (EditorWindow window : windows) { window.updateFileIcon(file); } } private final Set<VirtualFile> myFilesToUpdateIconsFor = new HashSet<VirtualFile>(); private void updateFileIconLater(VirtualFile file) { myFilesToUpdateIconsFor.add(file); myIconUpdaterAlarm.cancelAllRequests(); myIconUpdaterAlarm.addRequest( () -> { if (myManager.getProject().isDisposed()) return; for (VirtualFile file1 : myFilesToUpdateIconsFor) { updateFileIconImmediately(file1); } myFilesToUpdateIconsFor.clear(); }, 200, ModalityState.stateForComponent(this)); } void updateFileColor(@NotNull final VirtualFile file) { final Collection<EditorWindow> windows = findWindows(file); for (final EditorWindow window : windows) { final int index = window.findEditorIndex(window.findFileComposite(file)); LOG.assertTrue(index != -1); window.setForegroundAt(index, getManager().getFileColor(file)); window.setWaveColor(index, getManager().isProblem(file) ? JBColor.red : null); } } public void trimToSize(final int editor_tab_limit) { for (final EditorWindow window : myWindows) { window.trimToSize(editor_tab_limit, null, true); } } public void setTabsPlacement(final int tabPlacement) { final EditorWindow[] windows = getWindows(); for (int i = 0; i != windows.length; ++i) { windows[i].setTabsPlacement(tabPlacement); } } void setTabLayoutPolicy(int scrollTabLayout) { final EditorWindow[] windows = getWindows(); for (int i = 0; i != windows.length; ++i) { windows[i].setTabLayoutPolicy(scrollTabLayout); } } void updateFileName(@Nullable final VirtualFile updatedFile) { final EditorWindow[] windows = getWindows(); for (int i = 0; i != windows.length; ++i) { for (VirtualFile file : windows[i].getFiles()) { if (updatedFile == null || file.getName().equals(updatedFile.getName())) { windows[i].updateFileName(file); } } } Project project = myManager.getProject(); final IdeFrame frame = getFrame(project); if (frame != null) { VirtualFile file = getCurrentFile(); File ioFile = file == null ? null : new File(file.getPresentableUrl()); String fileTitle = null; if (file != null) { fileTitle = DumbService.isDumb(project) ? file.getName() : FrameTitleBuilder.getInstance().getFileTitle(project, file); } frame.setFileTitle(fileTitle, ioFile); } } protected IdeFrame getFrame(Project project) { final WindowManagerEx windowManagerEx = WindowManagerEx.getInstanceEx(); final IdeFrame frame = windowManagerEx.getFrame(project); LOG.assertTrue(ApplicationManager.getApplication().isUnitTestMode() || frame != null); return frame; } boolean isInsideChange() { return myInsideChange > 0; } private void setCurrentWindow(@Nullable final EditorWindow currentWindow) { if (currentWindow != null && !myWindows.contains(currentWindow)) { throw new IllegalArgumentException(currentWindow + " is not a member of this container"); } myCurrentWindow = currentWindow; } void updateFileBackgroundColor(@NotNull VirtualFile file) { final EditorWindow[] windows = getWindows(); for (int i = 0; i != windows.length; ++i) { windows[i].updateFileBackgroundColor(file); } } int getSplitCount() { if (getComponentCount() > 0) { JPanel panel = (JPanel) getComponent(0); return getSplitCount(panel); } return 0; } private static int getSplitCount(JComponent component) { if (component.getComponentCount() > 0) { final JComponent firstChild = (JComponent) component.getComponent(0); if (firstChild instanceof Splitter) { final Splitter splitter = (Splitter) firstChild; return getSplitCount(splitter.getFirstComponent()) + getSplitCount(splitter.getSecondComponent()); } return 1; } return 0; } protected void afterFileClosed(VirtualFile file) {} protected void afterFileOpen(VirtualFile file) {} @Nullable JBTabs getTabsAt(RelativePoint point) { Point thisPoint = point.getPoint(this); Component c = SwingUtilities.getDeepestComponentAt(this, thisPoint.x, thisPoint.y); while (c != null) { if (c instanceof JBTabs) { return (JBTabs) c; } c = c.getParent(); } return null; } boolean isEmptyVisible() { EditorWindow[] windows = getWindows(); for (EditorWindow each : windows) { if (!each.isEmptyVisible()) { return false; } } return true; } @Nullable private VirtualFile findNextFile(final VirtualFile file) { final EditorWindow[] windows = getWindows(); // TODO: use current file as base for (int i = 0; i != windows.length; ++i) { final VirtualFile[] files = windows[i].getFiles(); for (final VirtualFile fileAt : files) { if (!Comparing.equal(fileAt, file)) { return fileAt; } } } return null; } void closeFile(VirtualFile file, boolean moveFocus) { final List<EditorWindow> windows = findWindows(file); if (!windows.isEmpty()) { final VirtualFile nextFile = findNextFile(file); for (final EditorWindow window : windows) { LOG.assertTrue(window.getSelectedEditor() != null); window.closeFile(file, false, moveFocus); if (window.getTabCount() == 0 && nextFile != null && myManager.getProject().isOpen()) { EditorWithProviderComposite newComposite = myManager.newEditorComposite(nextFile); window.setEditor(newComposite, moveFocus); // newComposite can be null } } // cleanup windows with no tabs for (final EditorWindow window : windows) { if (window.isDisposed()) { // call to window.unsplit() which might make its sibling disposed continue; } if (window.getTabCount() == 0) { window.unsplit(false); } } } } @Override public void uiSettingsChanged(UISettings source) { if (!myManager.getProject().isOpen()) return; for (VirtualFile file : getOpenFiles()) { updateFileBackgroundColor(file); updateFileIcon(file); updateFileColor(file); } } private final class MyFocusTraversalPolicy extends IdeFocusTraversalPolicy { @Override public final Component getDefaultComponentImpl(final Container focusCycleRoot) { if (myCurrentWindow != null) { final EditorWithProviderComposite selectedEditor = myCurrentWindow.getSelectedEditor(); if (selectedEditor != null) { return IdeFocusTraversalPolicy.getPreferredFocusedComponent( selectedEditor.getComponent(), this); } } return IdeFocusTraversalPolicy.getPreferredFocusedComponent(EditorsSplitters.this, this); } } @Nullable public JPanel getTopPanel() { return getComponentCount() > 0 ? (JPanel) getComponent(0) : null; } public EditorWindow getCurrentWindow() { return myCurrentWindow; } public EditorWindow getOrCreateCurrentWindow(final VirtualFile file) { final List<EditorWindow> windows = findWindows(file); if (getCurrentWindow() == null) { final Iterator<EditorWindow> iterator = myWindows.iterator(); if (!windows.isEmpty()) { setCurrentWindow(windows.get(0), false); } else if (iterator.hasNext()) { setCurrentWindow(iterator.next(), false); } else { createCurrentWindow(); } } else if (!windows.isEmpty()) { if (!windows.contains(getCurrentWindow())) { setCurrentWindow(windows.get(0), false); } } return getCurrentWindow(); } void createCurrentWindow() { LOG.assertTrue(myCurrentWindow == null); setCurrentWindow(createEditorWindow()); add(myCurrentWindow.myPanel, BorderLayout.CENTER); } protected EditorWindow createEditorWindow() { return new EditorWindow(this); } /** * sets the window passed as a current ('focused') window among all splitters. All file openings * will be done inside this current window * * @param window a window to be set as current * @param requestFocus whether to request focus to the editor currently selected in this window */ void setCurrentWindow(@Nullable final EditorWindow window, final boolean requestFocus) { final EditorWithProviderComposite newEditor = window == null ? null : window.getSelectedEditor(); Runnable fireRunnable = () -> getManager().fireSelectionChanged(newEditor); setCurrentWindow(window); getManager().updateFileName(window == null ? null : window.getSelectedFile()); if (window != null) { final EditorWithProviderComposite selectedEditor = window.getSelectedEditor(); if (selectedEditor != null) { fireRunnable.run(); } if (requestFocus) { window.requestFocus(true); } } else { fireRunnable.run(); } } void addWindow(EditorWindow window) { myWindows.add(window); } void removeWindow(EditorWindow window) { myWindows.remove(window); if (myCurrentWindow == window) { myCurrentWindow = null; } } boolean containsWindow(EditorWindow window) { return myWindows.contains(window); } // --------------------------------------------------------- public EditorWithProviderComposite[] getEditorsComposites() { List<EditorWithProviderComposite> res = new ArrayList<EditorWithProviderComposite>(); for (final EditorWindow myWindow : myWindows) { final EditorWithProviderComposite[] editors = myWindow.getEditors(); ContainerUtil.addAll(res, editors); } return res.toArray(new EditorWithProviderComposite[res.size()]); } // --------------------------------------------------------- @NotNull public List<EditorWithProviderComposite> findEditorComposites(@NotNull VirtualFile file) { List<EditorWithProviderComposite> res = new ArrayList<EditorWithProviderComposite>(); for (final EditorWindow window : myWindows) { final EditorWithProviderComposite fileComposite = window.findFileComposite(file); if (fileComposite != null) { res.add(fileComposite); } } return res; } @NotNull private List<EditorWindow> findWindows(final VirtualFile file) { List<EditorWindow> res = new ArrayList<EditorWindow>(); for (final EditorWindow window : myWindows) { if (window.findFileComposite(file) != null) { res.add(window); } } return res; } @NotNull public EditorWindow[] getWindows() { return myWindows.toArray(new EditorWindow[myWindows.size()]); } @NotNull EditorWindow[] getOrderedWindows() { final List<EditorWindow> res = new ArrayList<EditorWindow>(); // Collector for windows in tree ordering: class Inner { private void collect(final JPanel panel) { final Component comp = panel.getComponent(0); if (comp instanceof Splitter) { final Splitter splitter = (Splitter) comp; collect((JPanel) splitter.getFirstComponent()); collect((JPanel) splitter.getSecondComponent()); } else if (comp instanceof JPanel || comp instanceof JBTabs) { final EditorWindow window = findWindowWith(comp); if (window != null) { res.add(window); } } } } // get root component and traverse splitters tree: if (getComponentCount() != 0) { final Component comp = getComponent(0); LOG.assertTrue(comp instanceof JPanel); final JPanel panel = (JPanel) comp; if (panel.getComponentCount() != 0) { new Inner().collect(panel); } } LOG.assertTrue(res.size() == myWindows.size()); return res.toArray(new EditorWindow[res.size()]); } @Nullable private EditorWindow findWindowWith(final Component component) { if (component != null) { for (final EditorWindow window : myWindows) { if (SwingUtilities.isDescendingFrom(component, window.myPanel)) { return window; } } } return null; } public boolean isFloating() { return false; } public boolean isPreview() { return false; } private final class MyFocusWatcher extends FocusWatcher { @Override protected void focusedComponentChanged(final Component component, final AWTEvent cause) { EditorWindow newWindow = null; if (component != null) { newWindow = findWindowWith(component); } else if (cause instanceof ContainerEvent && cause.getID() == ContainerEvent.COMPONENT_REMOVED) { // do not change current window in case of child removal as in JTable.removeEditor // otherwise Escape in a toolwindow will not focus editor with JTable content return; } setCurrentWindow(newWindow); setCurrentWindow(newWindow, false); } } private abstract static class ConfigTreeReader<T> { @Nullable public T process(@Nullable Element element, @Nullable T context) { if (element == null) { return null; } final Element splitterElement = element.getChild("splitter"); if (splitterElement != null) { final Element first = splitterElement.getChild("split-first"); final Element second = splitterElement.getChild("split-second"); return processSplitter(splitterElement, first, second, context); } final Element leaf = element.getChild("leaf"); if (leaf == null) { return null; } List<Element> fileElements = leaf.getChildren("file"); final List<Element> children = new ArrayList<Element>(fileElements.size()); // trim to EDITOR_TAB_LIMIT, ignoring CLOSE_NON_MODIFIED_FILES_FIRST policy int toRemove = fileElements.size() - UISettings.getInstance().EDITOR_TAB_LIMIT; for (Element fileElement : fileElements) { if (toRemove <= 0 || Boolean.valueOf(fileElement.getAttributeValue(PINNED)).booleanValue()) { children.add(fileElement); } else { toRemove--; } } return processFiles(children, context); } @Nullable protected abstract T processFiles(@NotNull List<Element> fileElements, @Nullable T context); @Nullable protected abstract T processSplitter( @NotNull Element element, @Nullable Element firstChild, @Nullable Element secondChild, @Nullable T context); } private class UIBuilder extends ConfigTreeReader<JPanel> { @Override protected JPanel processFiles(@NotNull List<Element> fileElements, final JPanel context) { final Ref<EditorWindow> windowRef = new Ref<EditorWindow>(); UIUtil.invokeAndWaitIfNeeded( new Runnable() { @Override public void run() { windowRef.set(context == null ? createEditorWindow() : findWindowWith(context)); } }); final EditorWindow window = windowRef.get(); LOG.assertTrue(window != null); VirtualFile focusedFile = null; for (int i = 0; i < fileElements.size(); i++) { final Element file = fileElements.get(i); if (i == 0) { EditorTabbedContainer tabbedPane = window.getTabbedPane(); if (tabbedPane != null) { try { int limit = Integer.parseInt( file.getParentElement() .getAttributeValue( JBTabsImpl.SIDE_TABS_SIZE_LIMIT_KEY.toString(), String.valueOf(JBTabsImpl.DEFAULT_MAX_TAB_WIDTH))); UIUtil.putClientProperty( tabbedPane.getComponent(), JBTabsImpl.SIDE_TABS_SIZE_LIMIT_KEY, limit); } catch (NumberFormatException e) { // ignore } } } try { final FileEditorManagerImpl fileEditorManager = getManager(); Element historyElement = file.getChild(HistoryEntry.TAG); final HistoryEntry entry = HistoryEntry.createLight(fileEditorManager.getProject(), historyElement); final VirtualFile virtualFile = entry.getFile(); if (virtualFile == null) throw new InvalidDataException("No file exists: " + entry.getFilePointer().getUrl()); Document document = ApplicationManager.getApplication() .runReadAction( new Computable<Document>() { @Override public Document compute() { return virtualFile.isValid() ? FileDocumentManager.getInstance().getDocument(virtualFile) : null; } }); final boolean isCurrentInTab = Boolean.valueOf(file.getAttributeValue(CURRENT_IN_TAB)).booleanValue(); Boolean pin = Boolean.valueOf(file.getAttributeValue(PINNED)); fileEditorManager.openFileImpl4( window, virtualFile, entry, isCurrentInTab, isCurrentInTab, pin, i); if (isCurrentInTab) { focusedFile = virtualFile; } if (document != null) { // This is just to make sure document reference is kept on stack till this point // so that document is available for folding state deserialization in HistoryEntry // constructor // and that document will be created only once during file opening document.putUserData(DUMMY_KEY, null); } updateProgress(); } catch (InvalidDataException e) { if (ApplicationManager.getApplication().isUnitTestMode()) { LOG.error(e); } } } if (focusedFile != null) { getManager().addSelectionRecord(focusedFile, window); } return window.myPanel; } @Override protected JPanel processSplitter( @NotNull Element splitterElement, Element firstChild, Element secondChild, final JPanel context) { if (context == null) { final boolean orientation = "vertical".equals(splitterElement.getAttributeValue("split-orientation")); final float proportion = Float.valueOf(splitterElement.getAttributeValue("split-proportion")).floatValue(); final JPanel firstComponent = process(firstChild, null); final JPanel secondComponent = process(secondChild, null); final Ref<JPanel> panelRef = new Ref<JPanel>(); UIUtil.invokeAndWaitIfNeeded( new Runnable() { @Override public void run() { JPanel panel = new JPanel(new BorderLayout()); panel.setOpaque(false); Splitter splitter = new OnePixelSplitter(orientation, proportion, 0.1f, 0.9f); panel.add(splitter, BorderLayout.CENTER); splitter.setFirstComponent(firstComponent); splitter.setSecondComponent(secondComponent); panelRef.set(panel); } }); return panelRef.get(); } final Ref<JPanel> firstComponent = new Ref<JPanel>(); final Ref<JPanel> secondComponent = new Ref<JPanel>(); UIUtil.invokeAndWaitIfNeeded( new Runnable() { @Override public void run() { if (context.getComponent(0) instanceof Splitter) { Splitter splitter = (Splitter) context.getComponent(0); firstComponent.set((JPanel) splitter.getFirstComponent()); secondComponent.set((JPanel) splitter.getSecondComponent()); } else { firstComponent.set(context); secondComponent.set(context); } } }); process(firstChild, firstComponent.get()); process(secondChild, secondComponent.get()); return context; } } }
public abstract class PsiDocumentManagerBase extends PsiDocumentManager implements DocumentListener { static final Logger LOG = Logger.getInstance("#com.intellij.psi.impl.PsiDocumentManagerImpl"); private static final Key<Document> HARD_REF_TO_DOCUMENT = Key.create("HARD_REFERENCE_TO_DOCUMENT"); private static final Key<PsiFile> HARD_REF_TO_PSI = Key.create("HARD_REFERENCE_TO_PSI"); private static final Key<List<Runnable>> ACTION_AFTER_COMMIT = Key.create("ACTION_AFTER_COMMIT"); protected final Project myProject; private final PsiManager myPsiManager; private final DocumentCommitProcessor myDocumentCommitProcessor; protected final Set<Document> myUncommittedDocuments = ContainerUtil.newConcurrentSet(); private final Map<Document, Pair<CharSequence, Long>> myLastCommittedTexts = ContainerUtil.newConcurrentMap(); protected boolean myStopTrackingDocuments; protected boolean myPerformBackgroundCommit = true; private volatile boolean myIsCommitInProgress; private final PsiToDocumentSynchronizer mySynchronizer; private final List<Listener> myListeners = ContainerUtil.createLockFreeCopyOnWriteList(); protected PsiDocumentManagerBase( @NotNull final Project project, @NotNull PsiManager psiManager, @NotNull MessageBus bus, @NonNls @NotNull final DocumentCommitProcessor documentCommitProcessor) { myProject = project; myPsiManager = psiManager; myDocumentCommitProcessor = documentCommitProcessor; mySynchronizer = new PsiToDocumentSynchronizer(this, bus); myPsiManager.addPsiTreeChangeListener(mySynchronizer); bus.connect() .subscribe( PsiDocumentTransactionListener.TOPIC, new PsiDocumentTransactionListener() { @Override public void transactionStarted(@NotNull Document document, @NotNull PsiFile file) { myUncommittedDocuments.remove(document); } @Override public void transactionCompleted(@NotNull Document document, @NotNull PsiFile file) {} }); } @Override @Nullable public PsiFile getPsiFile(@NotNull Document document) { final PsiFile userData = document.getUserData(HARD_REF_TO_PSI); if (userData != null) return userData; PsiFile psiFile = getCachedPsiFile(document); if (psiFile != null) return psiFile; final VirtualFile virtualFile = FileDocumentManager.getInstance().getFile(document); if (virtualFile == null || !virtualFile.isValid()) return null; psiFile = getPsiFile(virtualFile); if (psiFile == null) return null; fireFileCreated(document, psiFile); return psiFile; } public static void cachePsi(@NotNull Document document, @Nullable PsiFile file) { document.putUserData(HARD_REF_TO_PSI, file); } @Override public PsiFile getCachedPsiFile(@NotNull Document document) { final PsiFile userData = document.getUserData(HARD_REF_TO_PSI); if (userData != null) return userData; final VirtualFile virtualFile = FileDocumentManager.getInstance().getFile(document); if (virtualFile == null || !virtualFile.isValid()) return null; return getCachedPsiFile(virtualFile); } @Nullable FileViewProvider getCachedViewProvider(@NotNull Document document) { final VirtualFile virtualFile = getVirtualFile(document); if (virtualFile == null) return null; return getCachedViewProvider(virtualFile); } private FileViewProvider getCachedViewProvider(@NotNull VirtualFile virtualFile) { return ((PsiManagerEx) myPsiManager).getFileManager().findCachedViewProvider(virtualFile); } private static VirtualFile getVirtualFile(@NotNull Document document) { final VirtualFile virtualFile = FileDocumentManager.getInstance().getFile(document); if (virtualFile == null || !virtualFile.isValid()) return null; return virtualFile; } @Nullable PsiFile getCachedPsiFile(@NotNull VirtualFile virtualFile) { return ((PsiManagerEx) myPsiManager).getFileManager().getCachedPsiFile(virtualFile); } @Nullable private PsiFile getPsiFile(@NotNull VirtualFile virtualFile) { return ((PsiManagerEx) myPsiManager).getFileManager().findFile(virtualFile); } @Nullable @Override public Document getDocument(@NotNull PsiFile file) { if (file instanceof PsiBinaryFile) return null; Document document = getCachedDocument(file); if (document != null) { if (!file.getViewProvider().isPhysical() && document.getUserData(HARD_REF_TO_PSI) == null) { PsiUtilCore.ensureValid(file); cachePsi(document, file); } return document; } FileViewProvider viewProvider = file.getViewProvider(); if (!viewProvider.isEventSystemEnabled()) return null; document = FileDocumentManager.getInstance().getDocument(viewProvider.getVirtualFile()); if (document != null) { if (document.getTextLength() != file.getTextLength()) { String message = "Document/PSI mismatch: " + file + " (" + file.getClass() + "); physical=" + viewProvider.isPhysical(); if (document.getTextLength() + file.getTextLength() < 8096) { message += "\n=== document ===\n" + document.getText() + "\n=== PSI ===\n" + file.getText(); } throw new AssertionError(message); } if (!viewProvider.isPhysical()) { PsiUtilCore.ensureValid(file); cachePsi(document, file); file.putUserData(HARD_REF_TO_DOCUMENT, document); } } return document; } @Override public Document getCachedDocument(@NotNull PsiFile file) { if (!file.isPhysical()) return null; VirtualFile vFile = file.getViewProvider().getVirtualFile(); return FileDocumentManager.getInstance().getCachedDocument(vFile); } @Override public void commitAllDocuments() { ApplicationManager.getApplication().assertIsDispatchThread(); if (myUncommittedDocuments.isEmpty()) return; final Document[] documents = getUncommittedDocuments(); for (Document document : documents) { commitDocument(document); } LOG.assertTrue(!hasUncommitedDocuments(), myUncommittedDocuments); } @Override public void performForCommittedDocument( @NotNull final Document doc, @NotNull final Runnable action) { final Document document = doc instanceof DocumentWindow ? ((DocumentWindow) doc).getDelegate() : doc; if (isCommitted(document)) { action.run(); } else { addRunOnCommit(document, action); } } private final Map<Object, Runnable> actionsWhenAllDocumentsAreCommitted = new LinkedHashMap<Object, Runnable>(); // accessed from EDT only private static final Object PERFORM_ALWAYS_KEY = new Object() { @Override @NonNls public String toString() { return "PERFORM_ALWAYS"; } }; /** * Cancel previously registered action and schedules (new) action to be executed when all * documents are committed. * * @param key the (unique) id of the action. * @param action The action to be executed after automatic commit. This action will overwrite any * action which was registered under this key earlier. The action will be executed in EDT. * @return true if action has been run immediately, or false if action was scheduled for execution * later. */ public boolean cancelAndRunWhenAllCommitted( @NonNls @NotNull Object key, @NotNull final Runnable action) { ApplicationManager.getApplication().assertIsDispatchThread(); if (myProject.isDisposed()) { action.run(); return true; } if (myUncommittedDocuments.isEmpty()) { action.run(); if (!hasUncommitedDocuments()) { assert actionsWhenAllDocumentsAreCommitted.isEmpty() : actionsWhenAllDocumentsAreCommitted; } return true; } actionsWhenAllDocumentsAreCommitted.put(key, action); return false; } public static void addRunOnCommit(@NotNull Document document, @NotNull Runnable action) { synchronized (ACTION_AFTER_COMMIT) { List<Runnable> list = document.getUserData(ACTION_AFTER_COMMIT); if (list == null) { document.putUserData(ACTION_AFTER_COMMIT, list = new SmartList<Runnable>()); } list.add(action); } } @Override public void commitDocument(@NotNull final Document doc) { final Document document = doc instanceof DocumentWindow ? ((DocumentWindow) doc).getDelegate() : doc; if (!isCommitted(document)) { doCommit(document); } } // public for Upsource public boolean finishCommit( @NotNull final Document document, @NotNull final List<Processor<Document>> finishProcessors, final boolean synchronously, @NotNull final Object reason) { assert !myProject.isDisposed() : "Already disposed"; final boolean[] ok = {true}; ApplicationManager.getApplication() .runWriteAction( new CommitToPsiFileAction(document, myProject) { @Override public void run() { ok[0] = finishCommitInWriteAction(document, finishProcessors, synchronously); } }); if (ok[0]) { // otherwise changes maybe not synced to the document yet, and injectors will crash if (!mySynchronizer.isDocumentAffectedByTransactions(document)) { InjectedLanguageManager.getInstance(myProject).startRunInjectors(document, synchronously); } // run after commit actions outside write action runAfterCommitActions(document); if (DebugUtil.DO_EXPENSIVE_CHECKS && !ApplicationInfoImpl.isInPerformanceTest()) { checkAllElementsValid(document, reason); } } return ok[0]; } protected boolean finishCommitInWriteAction( @NotNull final Document document, @NotNull final List<Processor<Document>> finishProcessors, final boolean synchronously) { if (myProject.isDisposed()) return false; assert !(document instanceof DocumentWindow); myIsCommitInProgress = true; boolean success = true; try { final FileViewProvider viewProvider = getCachedViewProvider(document); if (viewProvider != null) { for (Processor<Document> finishRunnable : finishProcessors) { success = finishRunnable.process(document); if (synchronously) { assert success : finishRunnable + " in " + finishProcessors; } if (!success) { break; } } if (success) { myLastCommittedTexts.remove(document); viewProvider.contentsSynchronized(); } } else { handleCommitWithoutPsi(document); } } finally { myDocumentCommitProcessor.log( "in PDI.finishDoc: ", null, synchronously, success, myUncommittedDocuments); if (success) { myUncommittedDocuments.remove(document); myDocumentCommitProcessor.log( "in PDI.finishDoc: removed doc", null, synchronously, success, myUncommittedDocuments); } myIsCommitInProgress = false; myDocumentCommitProcessor.log( "in PDI.finishDoc: exit", null, synchronously, success, myUncommittedDocuments); } return success; } private void checkAllElementsValid(@NotNull Document document, @NotNull final Object reason) { final PsiFile psiFile = getCachedPsiFile(document); if (psiFile != null) { psiFile.accept( new PsiRecursiveElementWalkingVisitor() { @Override public void visitElement(PsiElement element) { if (!element.isValid()) { throw new AssertionError( "Commit to '" + psiFile.getVirtualFile() + "' has led to invalid element: " + element + "; Reason: '" + reason + "'"); } } }); } } private void doCommit(@NotNull final Document document) { assert !myIsCommitInProgress : "Do not call commitDocument() from inside PSI change listener"; ApplicationManager.getApplication() .runWriteAction( new Runnable() { @Override public void run() { // otherwise there are many clients calling commitAllDocs() on PSI childrenChanged() if (getSynchronizer().isDocumentAffectedByTransactions(document)) return; myIsCommitInProgress = true; try { myDocumentCommitProcessor.commitSynchronously(document, myProject); } finally { myIsCommitInProgress = false; } assert !isInUncommittedSet(document) : "Document :" + document; } }); } @Override public <T> T commitAndRunReadAction(@NotNull final Computable<T> computation) { final Ref<T> ref = Ref.create(null); commitAndRunReadAction( new Runnable() { @Override public void run() { ref.set(computation.compute()); } }); return ref.get(); } @Override public void reparseFiles(@NotNull Collection<VirtualFile> files, boolean includeOpenFiles) { FileContentUtilCore.reparseFiles(files); } @Override public void commitAndRunReadAction(@NotNull final Runnable runnable) { final Application application = ApplicationManager.getApplication(); if (SwingUtilities.isEventDispatchThread()) { commitAllDocuments(); runnable.run(); } else { if (ApplicationManager.getApplication().isReadAccessAllowed()) { LOG.error( "Don't call commitAndRunReadAction inside ReadAction, it will cause a deadlock otherwise. " + Thread.currentThread()); } final Semaphore s1 = new Semaphore(); final Semaphore s2 = new Semaphore(); final boolean[] committed = {false}; application.runReadAction( new Runnable() { @Override public void run() { if (myUncommittedDocuments.isEmpty()) { runnable.run(); committed[0] = true; } else { s1.down(); s2.down(); final Runnable commitRunnable = new Runnable() { @Override public void run() { commitAllDocuments(); s1.up(); s2.waitFor(); } }; final ProgressIndicator progressIndicator = ProgressManager.getInstance().getProgressIndicator(); if (progressIndicator == null) { ApplicationManager.getApplication().invokeLater(commitRunnable); } else { ApplicationManager.getApplication() .invokeLater(commitRunnable, progressIndicator.getModalityState()); } } } }); if (!committed[0]) { s1.waitFor(); application.runReadAction( new Runnable() { @Override public void run() { s2.up(); runnable.run(); } }); } } } /** * Schedules action to be executed when all documents are committed. * * @return true if action has been run immediately, or false if action was scheduled for execution * later. */ @Override public boolean performWhenAllCommitted(@NotNull final Runnable action) { ApplicationManager.getApplication().assertIsDispatchThread(); assert !myProject.isDisposed() : "Already disposed: " + myProject; if (myUncommittedDocuments.isEmpty()) { action.run(); return true; } CompositeRunnable actions = (CompositeRunnable) actionsWhenAllDocumentsAreCommitted.get(PERFORM_ALWAYS_KEY); if (actions == null) { actions = new CompositeRunnable(); actionsWhenAllDocumentsAreCommitted.put(PERFORM_ALWAYS_KEY, actions); } actions.add(action); myDocumentCommitProcessor.log( "PDI: added performWhenAllCommitted", null, false, action, myUncommittedDocuments); return false; } private static class CompositeRunnable extends ArrayList<Runnable> implements Runnable { @Override public void run() { for (Runnable runnable : this) { runnable.run(); } } } private void runAfterCommitActions(@NotNull Document document) { ApplicationManager.getApplication().assertIsDispatchThread(); List<Runnable> list; synchronized (ACTION_AFTER_COMMIT) { list = document.getUserData(ACTION_AFTER_COMMIT); if (list != null) { list = new ArrayList<Runnable>(list); document.putUserData(ACTION_AFTER_COMMIT, null); } } if (list != null) { for (final Runnable runnable : list) { runnable.run(); } } if (!hasUncommitedDocuments() && !actionsWhenAllDocumentsAreCommitted.isEmpty()) { List<Object> keys = new ArrayList<Object>(actionsWhenAllDocumentsAreCommitted.keySet()); for (Object key : keys) { try { Runnable action = actionsWhenAllDocumentsAreCommitted.remove(key); myDocumentCommitProcessor.log( "Running after commit runnable: ", null, false, key, action); action.run(); } catch (Throwable e) { LOG.error(e); } } } } @Override public void addListener(@NotNull Listener listener) { myListeners.add(listener); } @Override public void removeListener(@NotNull Listener listener) { myListeners.remove(listener); } @Override public boolean isDocumentBlockedByPsi(@NotNull Document doc) { return false; } @Override public void doPostponedOperationsAndUnblockDocument(@NotNull Document doc) {} void fireDocumentCreated(@NotNull Document document, PsiFile file) { for (Listener listener : myListeners) { listener.documentCreated(document, file); } } private void fireFileCreated(@NotNull Document document, @NotNull PsiFile file) { for (Listener listener : myListeners) { listener.fileCreated(file, document); } } @Override @NotNull public CharSequence getLastCommittedText(@NotNull Document document) { Pair<CharSequence, Long> pair = myLastCommittedTexts.get(document); return pair != null ? pair.first : document.getImmutableCharSequence(); } @Override public long getLastCommittedStamp(@NotNull Document document) { Pair<CharSequence, Long> pair = myLastCommittedTexts.get(document); return pair != null ? pair.second : document.getModificationStamp(); } @Override @NotNull public Document[] getUncommittedDocuments() { ApplicationManager.getApplication().assertIsDispatchThread(); Document[] documents = myUncommittedDocuments.toArray(new Document[myUncommittedDocuments.size()]); return ArrayUtil.stripTrailingNulls(documents); } boolean isInUncommittedSet(@NotNull Document document) { if (document instanceof DocumentWindow) return isInUncommittedSet(((DocumentWindow) document).getDelegate()); return myUncommittedDocuments.contains(document); } @Override public boolean isUncommited(@NotNull Document document) { return !isCommitted(document); } @Override public boolean isCommitted(@NotNull Document document) { if (document instanceof DocumentWindow) return isCommitted(((DocumentWindow) document).getDelegate()); if (getSynchronizer().isInSynchronization(document)) return true; return !((DocumentEx) document).isInEventsHandling() && !isInUncommittedSet(document); } @Override public boolean hasUncommitedDocuments() { return !myIsCommitInProgress && !myUncommittedDocuments.isEmpty(); } @Override public void beforeDocumentChange(@NotNull DocumentEvent event) { if (myStopTrackingDocuments) return; final Document document = event.getDocument(); if (!(document instanceof DocumentWindow) && !myLastCommittedTexts.containsKey(document)) { myLastCommittedTexts.put( document, Pair.create(document.getImmutableCharSequence(), document.getModificationStamp())); } VirtualFile virtualFile = FileDocumentManager.getInstance().getFile(document); boolean isRelevant = virtualFile != null && isRelevant(virtualFile); final FileViewProvider viewProvider = getCachedViewProvider(document); boolean inMyProject = viewProvider != null && viewProvider.getManager() == myPsiManager; if (!isRelevant || !inMyProject) { return; } final List<PsiFile> files = viewProvider.getAllFiles(); PsiFile psiCause = null; for (PsiFile file : files) { if (file == null) { throw new AssertionError( "View provider " + viewProvider + " (" + viewProvider.getClass() + ") returned null in its files array: " + files + " for file " + viewProvider.getVirtualFile()); } if (mySynchronizer.isInsideAtomicChange(file)) { psiCause = file; } } if (psiCause == null) { beforeDocumentChangeOnUnlockedDocument(viewProvider); } ((SingleRootFileViewProvider) viewProvider).beforeDocumentChanged(psiCause); } protected void beforeDocumentChangeOnUnlockedDocument( @NotNull final FileViewProvider viewProvider) {} @Override public void documentChanged(DocumentEvent event) { if (myStopTrackingDocuments) return; final Document document = event.getDocument(); VirtualFile virtualFile = FileDocumentManager.getInstance().getFile(document); boolean isRelevant = virtualFile != null && isRelevant(virtualFile); final FileViewProvider viewProvider = getCachedViewProvider(document); if (viewProvider == null) { handleCommitWithoutPsi(document); return; } boolean inMyProject = viewProvider.getManager() == myPsiManager; if (!isRelevant || !inMyProject) { myLastCommittedTexts.remove(document); return; } ApplicationManager.getApplication().assertWriteAccessAllowed(); final List<PsiFile> files = viewProvider.getAllFiles(); boolean commitNecessary = true; for (PsiFile file : files) { if (mySynchronizer.isInsideAtomicChange(file)) { commitNecessary = false; continue; } assert file instanceof PsiFileImpl || "mock.file".equals(file.getName()) && ApplicationManager.getApplication().isUnitTestMode() : event + "; file=" + file + "; allFiles=" + files + "; viewProvider=" + viewProvider; } boolean forceCommit = ApplicationManager.getApplication().hasWriteAction(ExternalChangeAction.class) && (SystemProperties.getBooleanProperty("idea.force.commit.on.external.change", false) || ApplicationManager.getApplication().isHeadlessEnvironment() && !ApplicationManager.getApplication().isUnitTestMode()); // Consider that it's worth to perform complete re-parse instead of merge if the whole document // text is replaced and // current document lines number is roughly above 5000. This makes sense in situations when // external change is performed // for the huge file (that causes the whole document to be reloaded and 'merge' way takes a // while to complete). if (event.isWholeTextReplaced() && document.getTextLength() > 100000) { document.putUserData(BlockSupport.DO_NOT_REPARSE_INCREMENTALLY, Boolean.TRUE); } if (commitNecessary) { assert !(document instanceof DocumentWindow); myUncommittedDocuments.add(document); myDocumentCommitProcessor.log( "added uncommitted doc", null, false, myProject, document, ((DocumentEx) document).isInBulkUpdate()); if (forceCommit) { commitDocument(document); } else if (!((DocumentEx) document).isInBulkUpdate() && myPerformBackgroundCommit) { myDocumentCommitProcessor.commitAsynchronously(myProject, document, event); } } else { myLastCommittedTexts.remove(document); } } void handleCommitWithoutPsi(@NotNull Document document) { final Pair<CharSequence, Long> prevPair = myLastCommittedTexts.remove(document); if (prevPair == null) { return; } if (!myProject.isInitialized() || myProject.isDisposed()) { return; } myUncommittedDocuments.remove(document); VirtualFile virtualFile = FileDocumentManager.getInstance().getFile(document); if (virtualFile == null || !FileIndexFacade.getInstance(myProject).isInContent(virtualFile)) { return; } final PsiFile psiFile = getPsiFile(document); if (psiFile == null) { return; } // we can end up outside write action here if the document has forUseInNonAWTThread=true ApplicationManager.getApplication() .runWriteAction( new ExternalChangeAction() { @Override public void run() { psiFile.getViewProvider().beforeContentsSynchronized(); synchronized (PsiLock.LOCK) { final int oldLength = prevPair.first.length(); PsiManagerImpl manager = (PsiManagerImpl) psiFile.getManager(); BlockSupportImpl.sendBeforeChildrenChangeEvent(manager, psiFile, true); BlockSupportImpl.sendBeforeChildrenChangeEvent(manager, psiFile, false); if (psiFile instanceof PsiFileImpl) { ((PsiFileImpl) psiFile).onContentReload(); } BlockSupportImpl.sendAfterChildrenChangedEvent( manager, psiFile, oldLength, false); BlockSupportImpl.sendAfterChildrenChangedEvent(manager, psiFile, oldLength, true); } psiFile.getViewProvider().contentsSynchronized(); } }); } private boolean isRelevant(@NotNull VirtualFile virtualFile) { return !virtualFile.getFileType().isBinary() && !myProject.isDisposed(); } public static boolean checkConsistency(@NotNull PsiFile psiFile, @NotNull Document document) { // todo hack if (psiFile.getVirtualFile() == null) return true; CharSequence editorText = document.getCharsSequence(); int documentLength = document.getTextLength(); if (psiFile.textMatches(editorText)) { LOG.assertTrue(psiFile.getTextLength() == documentLength); return true; } char[] fileText = psiFile.textToCharArray(); @SuppressWarnings("NonConstantStringShouldBeStringBuffer") @NonNls String error = "File '" + psiFile.getName() + "' text mismatch after reparse. " + "File length=" + fileText.length + "; Doc length=" + documentLength + "\n"; int i = 0; for (; i < documentLength; i++) { if (i >= fileText.length) { error += "editorText.length > psiText.length i=" + i + "\n"; break; } if (i >= editorText.length()) { error += "editorText.length > psiText.length i=" + i + "\n"; break; } if (editorText.charAt(i) != fileText[i]) { error += "first unequal char i=" + i + "\n"; break; } } // error += "*********************************************" + "\n"; // if (i <= 500){ // error += "Equal part:" + editorText.subSequence(0, i) + "\n"; // } // else{ // error += "Equal part start:\n" + editorText.subSequence(0, 200) + "\n"; // error += "................................................" + "\n"; // error += "................................................" + "\n"; // error += "................................................" + "\n"; // error += "Equal part end:\n" + editorText.subSequence(i - 200, i) + "\n"; // } error += "*********************************************" + "\n"; error += "Editor Text tail:(" + (documentLength - i) + ")\n"; // + editorText.subSequence(i, Math.min(i + 300, documentLength)) + "\n"; error += "*********************************************" + "\n"; error += "Psi Text tail:(" + (fileText.length - i) + ")\n"; error += "*********************************************" + "\n"; if (document instanceof DocumentWindow) { error += "doc: '" + document.getText() + "'\n"; error += "psi: '" + psiFile.getText() + "'\n"; error += "ast: '" + psiFile.getNode().getText() + "'\n"; error += psiFile.getLanguage() + "\n"; PsiElement context = InjectedLanguageManager.getInstance(psiFile.getProject()).getInjectionHost(psiFile); if (context != null) { error += "context: " + context + "; text: '" + context.getText() + "'\n"; error += "context file: " + context.getContainingFile() + "\n"; } error += "document window ranges: " + Arrays.asList(((DocumentWindow) document).getHostRanges()) + "\n"; } LOG.error(error); // document.replaceString(0, documentLength, psiFile.getText()); return false; } @TestOnly public void clearUncommittedDocuments() { myLastCommittedTexts.clear(); myUncommittedDocuments.clear(); mySynchronizer.cleanupForNextTest(); } @TestOnly public void disableBackgroundCommit(@NotNull Disposable parentDisposable) { assert myPerformBackgroundCommit; myPerformBackgroundCommit = false; Disposer.register( parentDisposable, new Disposable() { @Override public void dispose() { myPerformBackgroundCommit = true; } }); } @NotNull public PsiToDocumentSynchronizer getSynchronizer() { return mySynchronizer; } }
/** Constructs a ChainableOrdering. */ public ChainableOrdering(Key<?> key, SortOrder sortOrder) { this(key.key(), sortOrder); }
public static void setAlternativeSource(VirtualFile source, VirtualFile dest) { ALTERNATIVE_SOURCE_KEY.set(source, dest); ALTERNATIVE_SOURCE_KEY.set(dest, null); }