All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.netbeans.modules.csl.navigation.ElementScanningTask Maven / Gradle / Ivy

The newest version!
/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
package org.netbeans.modules.csl.navigation;

import java.lang.ref.Reference;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.WeakHashMap;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.ImageIcon;
import org.netbeans.api.annotations.common.NonNull;
import org.netbeans.api.editor.mimelookup.MimePath;
import org.netbeans.modules.csl.api.StructureScanner;
import org.netbeans.modules.csl.api.ElementHandle;
import org.netbeans.modules.csl.api.ElementKind;
import org.netbeans.modules.csl.api.Modifier;
import org.netbeans.modules.csl.api.StructureItem;
import org.netbeans.modules.csl.core.Language;
import org.netbeans.modules.csl.core.LanguageRegistry;
import org.netbeans.modules.csl.api.HtmlFormatter;
import org.netbeans.modules.csl.core.CancelSupportImplementation;
import org.netbeans.modules.csl.core.SchedulerTaskCancelSupportImpl;
import org.netbeans.modules.csl.core.SpiSupportAccessor;
import org.netbeans.modules.csl.navigation.ElementNode.Description;
import org.netbeans.modules.csl.spi.ParserResult;
import org.netbeans.modules.parsing.api.Embedding;
import org.netbeans.modules.parsing.api.ParserManager;
import org.netbeans.modules.parsing.api.ResultIterator;
import org.netbeans.modules.parsing.api.Snapshot;
import org.netbeans.modules.parsing.api.Source;
import org.netbeans.modules.parsing.api.UserTask;
import org.netbeans.modules.parsing.spi.*;
import org.netbeans.modules.parsing.spi.Parser.Result;
import org.openide.filesystems.FileObject;
import org.openide.util.ImageUtilities;

/**
 * This file is originally from Retouche, the Java Support 
 * infrastructure in NetBeans. I have modified the file as little
 * as possible to make merging Retouche fixes back as simple as
 * possible. 
 * 

* * XXX Remove the ElementScanner class from here it should be wenough to * consult the Elements class. It should also permit for showing inherited members. * * @author phrebejk */ public abstract class ElementScanningTask extends IndexingAwareParserResultTask { private static final Logger LOG = Logger.getLogger(ElementScanningTask.class.getName()); private volatile boolean canceled; /** * Reference to the last result of parsing processed into structure. */ private static final Map> lastResults = new WeakHashMap<>(); /** * Cache the parser result and the resulting structure. */ private static class ResultStructure { private Parser.Result result; private List structure; public ResultStructure(Result result, List structure) { this.result = result; this.structure = structure; } } public ElementScanningTask() { super(TaskIndexingMode.ALLOWED_DURING_SCAN); } // not synchronized as the Task invocation itself is synced by parsing API public static List findCachedStructure(Snapshot s, Parser.Result r) { if (!(r instanceof ParserResult)) { return null; } Reference previousRef; previousRef = lastResults.get(s); if (previousRef == null) { return null; } ResultStructure cached = previousRef.get(); if (cached == null || cached.result != r) { // remove from cache: lastResults.remove(s); return null; } return cached.structure; } // not synchronized as the Task invocation itself is synced by parsing API public static void markProcessed(Parser.Result r, List structure) { lastResults.put(r.getSnapshot(), new WeakReference(new ResultStructure(r, structure))); } protected final StructureItem computeStructureRoot(Source source) { //System.out.println("The task is running" + info.getFileObject().getNameExt() + "=====================================" ) ; final FileObject fileObject = source.getFileObject(); if (fileObject == null) { return null; } final int [] mimetypesWithElements = new int [] { 0 }; final List roots = new ArrayList(); try { ParserManager.parse(Collections.singleton(source), new UserTask() { public @Override void run(ResultIterator resultIterator) throws Exception { Language language = LanguageRegistry.getInstance().getLanguageByMimeType(resultIterator.getSnapshot().getMimeType()); if(language != null) { //check for non csl results StructureScanner scanner = language.getStructure(); if (scanner != null) { Parser.Result r = resultIterator.getParserResult(); if (r instanceof ParserResult) { List children = findCachedStructure(resultIterator.getSnapshot(), r); if (children == null) { long startTime = System.currentTimeMillis(); // children = convert(scanner.scan((ParserResult) r), language); children = scanner.scan((ParserResult) r); long endTime = System.currentTimeMillis(); Logger.getLogger("TIMER").log(Level.FINE, "Structure (" + language.getMimeType() + ")", new Object[]{fileObject, endTime - startTime}); } if (children.size() > 0) { mimetypesWithElements[0]++; } if (isCancelled()) { //Don't cache if cancelled return; } markProcessed(r, children); roots.add(new MimetypeRootNode(language, children, resultIterator.getSnapshot().getMimePath())); } } } for(Embedding e : resultIterator.getEmbeddings()) { if (isCancelled()) { return; } run(resultIterator.getResultIterator(e)); } } }); } catch (ParseException e) { LOG.log(Level.WARNING, null, e); } if (isCancelled()) { return null; } //ensure we do display a language structure items only once per mimetype. //there is a problem that a language may be embedded in various mimepaths. //For example javascript virtual source is provided for both text/html //and text/x-jsp mimetypes. //So we have following javascript embedding: // //MimePath[text/x-jsp/text/html/text/javascript] //MimePath[text/x-jsp/text/javascript] // //...which results in multiple javascript nodes in the navigator // //So the solution is to use the shortest mimepath per mimetype only HashMap map = new HashMap(); for(MimetypeRootNode mtRootNode : roots) { MimePath path = mtRootNode.getMimePath(); String mimeType = path.getMimeType(path.size() - 1); //get leaf language MimetypeRootNode node = map.get(mimeType); if(node != null) { MimePath nodeMimePath = node.getMimePath(); if(path.size() < nodeMimePath.size()) { //the actual path is shorter, use this one map.put(mimeType, mtRootNode); } } else { map.put(mimeType, mtRootNode); } } roots.clear(); roots.addAll(map.values()); if (roots.size() > 1) { roots.sort(new Comparator() { public int compare(MimetypeRootNode o1, MimetypeRootNode o2) { return o1.getSortText().compareTo(o2.getSortText()); } }); } final List items = new ArrayList(); if (mimetypesWithElements[0] > 1) { //at least two languages provided some elements - use the root mimetype nodes for (MimetypeRootNode root : roots) { items.add(root); } } else { //just one or none language provided elements - put them to the root for (MimetypeRootNode root : roots) { items.addAll(root.getNestedItems()); } } return new RootStructureItem(items); } @Override public void cancel() { canceled = true; } protected final void resume() { canceled = false; } protected final boolean isCancelled() { return canceled; } protected final void runWithCancelService(@NonNull final Runnable r) { final CancelSupportImplementation cs = SchedulerTaskCancelSupportImpl.create(this); SpiSupportAccessor.getInstance().setCancelSupport(cs); try { r.run(); } finally { SpiSupportAccessor.getInstance().removeCancelSupport(cs); } } private static final class RootStructureItem implements StructureItem { private final List items; public RootStructureItem(List items) { this.items = items; } public String getName() { return null; } public String getHtml(HtmlFormatter formatter) { return null; } public ElementHandle getElementHandle() { return null; } public ElementKind getKind() { return ElementKind.OTHER; } public Set getModifiers() { return Collections.emptySet(); } public boolean isLeaf() { return false; } public List getNestedItems() { return items; } public long getPosition() { return 0; } public long getEndPosition() { return Long.MAX_VALUE; } public ImageIcon getCustomIcon() { return null; } public String getSortText() { return null; } } // End of RootStructureItem class static final class MimetypeRootNode implements StructureItem { //hack - see the getSortText() comment private static final String CSS_MIMETYPE = "text/css"; //NOI18N private static final String CSS_SORT_TEXT = "2";//NOI18N private static final String JAVASCRIPT_MIMETYPE = "text/javascript";//NOI18N private static final String RUBY_MIMETYPE = "text/x-ruby";//NOI18N private static final String YAML_MIMETYPE = "text/x-yaml";//NOI18N private static final String PHP_MIME_TYPE = "text/x-php5"; // NOI18N private static final String PHP_SORT_TEXT = "0";//NOI18N private static final String JAVASCRIPT_SORT_TEXT = "1";//NOI18N private static final String HTML_MIMETYPE = "text/html";//NOI18N private static final String HTML_SORT_TEXT = "3";//NOI18N // Ensure YAML is sorted before Ruby to make navigator look primarily in the YAML offsets first private static final String YAML_SORT_TEXT = "4";//NOI18N private static final String RUBY_SORT_TEXT = "5";//NOI18N private static final String OTHER_SORT_TEXT = "9";//NOI18N Language language; private List items; long from, to; private MimePath mimePath; private MimetypeRootNode(Language lang, List items, MimePath mimePath) { this.language = lang; this.items = new ArrayList(items); items.sort(Description.POSITION_COMPARATOR); this.from = items.size() > 0 ? items.get(0).getPosition() : 0; this.to = items.size() > 0 ? items.get(items.size() - 1).getEndPosition() : 0; this.mimePath = mimePath; } public MimePath getMimePath() { return mimePath; } @Override public boolean equals(Object o) { if (!(o instanceof MimetypeRootNode)) { return false; } MimetypeRootNode compared = (MimetypeRootNode) o; return language.equals(compared.language); } @Override public int hashCode() { int hash = 3; hash = 97 * hash + (this.language != null ? this.language.hashCode() : 0); return hash; } public String getName() { return language.getDisplayName(); } public String getSortText() { //hack -> I need to ensure that the navigator selects the top most //node when user moves caret in the source. //Since the navigator tree is a generic graph if there are more //embedded languages we need to use some tricks... if (language.getMimeType().equals(CSS_MIMETYPE)) { return CSS_SORT_TEXT; } else if (language.getMimeType().equals(JAVASCRIPT_MIMETYPE)) { return JAVASCRIPT_SORT_TEXT; } else if (language.getMimeType().equals(HTML_MIMETYPE)) { return HTML_SORT_TEXT; } else if (language.getMimeType().equals(YAML_MIMETYPE)) { return YAML_SORT_TEXT; } else if (language.getMimeType().equals(RUBY_MIMETYPE)) { return RUBY_SORT_TEXT; } else if (language.getMimeType().equals(PHP_MIME_TYPE)) { return PHP_SORT_TEXT; } else { return OTHER_SORT_TEXT + getName(); } } public String getHtml(HtmlFormatter formatter) { return getName(); } public ElementHandle getElementHandle() { return null; } public ElementKind getKind() { return ElementKind.OTHER; } public Set getModifiers() { return Collections.emptySet(); } public boolean isLeaf() { return false; } public List getNestedItems() { return items; } public long getPosition() { return from; } public long getEndPosition() { return to; } public ImageIcon getCustomIcon() { String iconBase = language.getIconBase(); return iconBase == null ? null : new ImageIcon(ImageUtilities.loadImage(iconBase)); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy