I am developing a plugin for intellij and I want to add custom suggestions to xml editor based on a xsd. Up to now I can get required suggestions from xsd file.
I have implemented a completion contributor for xml as follows
import com.intellij.codeInsight.completion.*;
import com.intellij.codeInsight.lookup.LookupElementBuilder;
import com.intellij.patterns.PlatformPatterns;
import com.intellij.psi.xml.XmlElementType;
import com.intellij.util.ProcessingContext;
import com.intellij.lang.xml.*;
import org.jetbrains.annotations.NotNull;
public class SimpleCompletionContributor extends CompletionContributor {
public SimpleCompletionContributor() {
extend(CompletionType.BASIC,PlatformPatterns.psiElement(XmlElementType.XML_ATTRIBUTE_VALUE).withLanguage(XMLLanguage.INSTANCE),
new CompletionProvider<CompletionParameters>() {
public void addCompletions(#NotNull CompletionParameters parameters,
ProcessingContext context,
#NotNull CompletionResultSet resultSet) {
resultSet.addElement(LookupElementBuilder.create("Hello"));
}
}
);
}
}
but this did not provide any suggestion. but when I implement custom language it works. My objective is to view the context of the cursor position and provide suggestion based on it. as an example when user starts a tag on xml file plugin should provide attributes as code completion. I'm new to this Custom language.
So can anyone help me with this completion contributor?
finally i found a way to solve this problem
here is my code
import com.intellij.codeInsight.completion.*;
import com.intellij.codeInsight.lookup.LookupElementBuilder;
import com.intellij.patterns.PlatformPatterns;
import com.intellij.util.ProcessingContext;
import org.jetbrains.annotations.NotNull;
public class ScalaXMLCompletionContributor extends CompletionContributor {
public ScalaXMLCompletionContributor() {
final RelativeNodes rlt = new RelativeNodes();//this is a class to get siblings and children from a sample xml file generated by a given xsd
/*if the parameter position is an xml attribute provide attributes using given xsd*/
extend(CompletionType.BASIC,
PlatformPatterns.psiElement(), new CompletionProvider<CompletionParameters>() {
public void addCompletions(#NotNull CompletionParameters parameters,//completion parameters contain details of the curser position
ProcessingContext context,
#NotNull CompletionResultSet resultSet) {//result set contains completion details to suggest
if (parameters.getPosition().getContext().toString() == "XmlAttribute") {//check whether scala text editors position is an xml attribute position eg: <name |
try {
String[] suggestions = rlt.getAttribute(parameters.getPosition().getParent().getParent().getFirstChild().getNextSibling().getText().replaceFirst("IntellijIdeaRulezzz", ""));//extract text from completion parameter and get required suggestions from RelativeNodes
int i = 0;
do {
resultSet.addElement(LookupElementBuilder.create(suggestions[i]));//add suggestions to resultset to suggest in editor
i++;
} while (suggestions[i] != null);
} catch (NullPointerException e) {
}
}
}
}
);
}
}
in this case we can obtain cursor position and tokens related to curser position by completion parameters and we can inject suggestions using cpmpletion resultset. this can be implemented in scala language too.
to register completion contributor in plugin xml
<extensions defaultExtensionNs="com.intellij">
<completion.contributor language="Scala" implementationClass="com.hsr.ScalaXMLCompletionContributor"/>
</extensions>
JavaDoc for com.intellij.codeInsight.completion.CompletionContributor contains FAQ.
The last question addresses debugging not working completion.
In my case issue was language="Java", whereas all caps expected.
Related
I'm relativly new to Lucene and want to implement my own CustomScoreQuery since I need it for my University.
I used the Lucene demo as my starting point to index all documents in a Folder and want to score them using my own algorithm.
Here are the links to the source code of the demo.
https://lucene.apache.org/core/7_1_0/demo/src-html/org/apache/lucene/demo/IndexFiles.html
https://lucene.apache.org/core/7_1_0/demo/src-html/org/apache/lucene/demo/SearchFiles.html
I'm checking with Luke: Lucene Toolbox Project to see my Index which is as expected. My problem occurs accessing it.
package CustomModul;
import java.io.IOException;
import org.apache.lucene.index.LeafReaderContext;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.Terms;
import org.apache.lucene.queries.CustomScoreProvider;
import org.apache.lucene.queries.CustomScoreQuery;
import org.apache.lucene.search.Query;
public class CountingQuery extends CustomScoreQuery {
public CountingQuery(Query subQuery) {
super(subQuery);
}
public class CountingQueryScoreProvider extends CustomScoreProvider {
String _field;
public CountingQueryScoreProvider(String field, LeafReaderContext context) {
super(context);
_field = field;
}
public float customScore(int doc, float subQueryScore, float valSrcScores[]) throws IOException {
IndexReader r = context.reader();
//getTermVector returns Null
Terms vec = r.getTermVector(doc, _field);
//*TO-DO* Algorithm
return (float)(1.0f);
}
}
protected CustomScoreProvider getCustomScoreProvider(
LeafReaderContext context) throws IOException {
return new CountingQueryScoreProvider("contents", context);
}
}
In my customScore function I access the Index like described in most Tutorials. I should get access to the Index using getTermVector but it returns NULL.
In other posts I read that this could be caused by contents being a TextField which is declared in the Lucene Demo IndexFiles.
After trying a lot of different approaches I came to the conclusion that I need help and here I am.
My Question now is if I need to adjust the Index Process (how?) or is there another way to access the Index in the ScoreProvider other then getTermVector?
I was able to solve the Problem myself and wanted to share my solution if someone finds this Question looking for answers.
The Problem was indeed caused by the contents being a TextField in
https://lucene.apache.org/core/7_1_0/demo/src-html/org/apache/lucene/demo/IndexFiles.html
To solve this Problem one has to construct his own Field which I did replacing line 193 in said IndexFile with
FieldType myFieldType = new FieldType(TextField.TYPE_STORED);
myFieldType.setOmitNorms(true);
myFieldType.setIndexOptions(IndexOptions.DOCS_AND_FREQS);
myFieldType.setStored(false);
myFieldType.setStoreTermVectors(true);
myFieldType.setTokenized(true);
myFieldType.freeze();
Field myField = new Field("contents",
new BufferedReader(new InputStreamReader(stream,
StandardCharsets.UTF_8)),
myFieldType);
doc.add(myField);
this allows the use of getTermVector in the customScore Function. Hope this will help someone in the future.
I'm trying to use bndtools to create my OSGI program. Here is my previous code, and it can work well with the felix console.
package com.buaa.ate.service.data.command;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;
import org.apache.felix.service.command.CommandProcessor;
import com.buaa.ate.service.api.data.Publisher;
#Component(
service=PublishCommand.class,
property={
CommandProcessor.COMMAND_SCOPE + ":String=example",
CommandProcessor.COMMAND_FUNCTION + ":String=publish",
}
)
public class PublishCommand {
private Publisher publishSvc;
#Reference
public void setPublisher(Publisher publishSvc) {
this.publishSvc = publishSvc;
}
public void publish(String content) {
publishSvc.start();
long result = publishSvc.publish(content);
System.out.println(result);
publishSvc.stop();
}
}
Now, I want to change the annotation like this:
package com.buaa.ate.service.data.command;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;
import org.apache.felix.service.command.CommandProcessor;
import com.buaa.ate.service.api.data.Publisher;
#Component(
service=PublishCommand.class,
properties="com/buaa/ate/service/data/command/config.properties"
)
public class PublishCommand {
private Publisher publishSvc;
#Reference
public void setPublisher(Publisher publishSvc) {
this.publishSvc = publishSvc;
}
public void publish(String content) {
publishSvc.start();
long result = publishSvc.publish(content);
System.out.println(result);
publishSvc.stop();
}
}
And this is my properties file:
config.properties
It's content like this:
osgi.command.scope\:String:example
osgi.command.function\:String:publish
When I run the program, input the command 'publish something', and then the problem happens:
'gogo: CommandNotFoundException: Command not found: publish'
So, what should I do to fix the problem?
Well, I just realize that it's so easy to fix the problem. This is a part of the osgi javadoc:
property
public abstract java.lang.String[] property
Properties for this Component.
Each property string is specified as "key=value". The type of the property value can be specified in the key as key:type=value. The type must be one of the property types supported by the type attribute of the property element of a Component Description.
To specify a property with multiple values, use multiple key, value pairs. For example, "foo=bar", "foo=baz".
See Also:
"The property element of a Component Description."
Default:{}
So I add the 'type' property to config.properties, and then the code can work well. Here is the current properties file:
current properties file
And it's content like this:
osgi.command.scope=example
osgi.command.scope\:type:String
osgi.command.function=publish
osgi.command.function\:type:String
The program can work well now.
I have a bunch of source files for java classes. I want to find those classes which are annotated by a given annotation class. The names of those classes should be written to a service provider list file.
Is there any machinery I could use to help me with this task? Or do I have to implement this myself from scratch?
If I had to do this myself, there are several approaches I can think of.
Write an Ant Task in Java. Have it create a ClassLoader using a suitable (probably configurable) class path. Use that loader to (attempt to) load the classes matching the input files, in order to inspect their annotations. Requires annotation retention at runtime, and full initialization of all involved classes and their dependencies.
Use javap to inspect the classes. Since I don't know of a programmatic interface to javap (do you?), this probably means iterating over the files and running a new process for each of them, then massaging the created output in a suitable way. Perhaps a <scriptdef>-ed task could be used for this. This would work with class-file annotation retention, and require no initialization.
Use an annotation processor to collect the information at compile-time. This should be able to work with sourcecode-only retention. But I have no experience writing or using annotation compilers, so I'm not sure this will work, and will need a lot of research to figure out some of the details. In particular how to activate the task for use by ant (Java 6 annotation processing configuration with Ant gives some pointers on this, as does What is the default annotation processors discovery process?) and when to create the output file (in each round, or only in the last round).
Which of these do you think has the greatest chances of success? Can you suggest code samples for one of these, which might be close to what I want and which I could adapt appropriately?
Encouraged by Thomas' comment, I gave approach 3 a try and got the following annotation processor working reasonably well:
import java.io.IOException;
import java.io.Writer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedSourceVersion;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.QualifiedNameable;
import javax.lang.model.element.TypeElement;
import javax.tools.StandardLocation;
#SupportedSourceVersion(SourceVersion.RELEASE_7)
public class AnnotationServiceProcessor extends AbstractProcessor {
// Map name of the annotation to name of the corresponding service interface
private static Map<String, String> annotationToServiceMap = new HashMap<>();
static {
// Adapt this to your use, or make it configurable somehow
annotationToServiceMap.put("Annotation1", "Service1");
annotationToServiceMap.put("Annotation2", "Service2");
}
#Override public Set<String> getSupportedAnnotationTypes() {
return annotationToServiceMap.keySet();
}
// Map name of the annotation to list of names
// of the classes which carry that annotation
private Map<String, List<String>> classLists;
#Override public void init(ProcessingEnvironment env) {
super.init(env);
classLists = new HashMap<>();
for (String ann: getSupportedAnnotationTypes())
classLists.put(ann, new ArrayList<String>());
}
public boolean process(Set<? extends TypeElement> annotations,
RoundEnvironment env) {
for (TypeElement ann: annotations) {
List<String> classes =
classLists.get(ann.getQualifiedName().toString());
for (Element elt: env.getElementsAnnotatedWith(ann)) {
QualifiedNameable qn = (QualifiedNameable)elt;
classes.add(qn.getQualifiedName().toString());
}
}
if (env.processingOver()) { // Only write results at the end
for (String ann: getSupportedAnnotationTypes()) {
try {
write(ann, classLists.get(ann));
} catch (IOException e) {
throw new RuntimeException(e); // UGLY!
}
}
}
return true;
}
// Write the service file for each annotation we found
private void write(String ann, List<String> classes) throws IOException {
if (classes.isEmpty())
return;
String service = annotationToServiceMap.get(ann);
Writer w = processingEnv.getFiler()
.createResource(StandardLocation.CLASS_OUTPUT,
"", "META-INF/services/" + service)
.openWriter();
classes.sort(null); // Make the processing order irrelevant
for (String cls: classes) {
w.write(cls);
w.write('\n');
}
w.close();
}
}
So far I've hooked this up to ant using <compilerarg>s from https://stackoverflow.com/a/3644624/1468366. I'll try something better and if I succeed will edit this post to include some ant snippet.
I've created a compound document type called SampleCaps in a HippoCMS 7.9 site and set out to build a template for it. In the process, I added hst:sitemap nodes, a pair of nested hst:pages nodes, and an hst:templates node. I also added the appropriate type property to hippo:namespaces/barcom/SampleCaps.
Finally, I created a Component and a Bean to expose the document data to the template, adapting the steps presented in part 2 of the Hippo Video Trails.
To my frustration, while the Component loads properly the Bean is never loaded (or at least, its getter is never called.) My component and bean are as follows:
site/src/main/java/com/footech/barcom/components/SampleCapsComponent.java:
package com.footech.barcom.components;
import com.footech.barcom.beans.SampleCapsDocument;
import org.hippoecm.hst.content.beans.standard.HippoBean;
import org.hippoecm.hst.component.support.bean.BaseHstComponent;
import org.hippoecm.hst.core.component.HstComponentException;
import org.hippoecm.hst.core.component.HstRequest;
import org.hippoecm.hst.core.component.HstResponse;
public class SampleCapsComponent extends BaseHstComponent {
#Override
public void doBeforeRender(final HstRequest request, final HstResponse response) throws HstComponentException {
SampleCapsDocument document = request.getRequestContext().getContentBean();
request.setAttribute("document", document);
System.out.println("Ping"); /* prints "Ping" to console */
}
}
site/src/main/java/com/footech/barcom/beans/SampleCapsDocument.java:
package com.footech.barcom.beans;
import java.util.Calendar;
import org.hippoecm.hst.content.beans.Node;
import org.hippoecm.hst.content.beans.standard.HippoHtml;
import org.onehippo.cms7.essentials.dashboard.annotations.HippoEssentialsGenerated;
#HippoEssentialsGenerated(internalName = "barcom:SampleCapsdocument")
#Node(jcrType = "barcom:SampleCapsdocument")
public class SampleCapsDocument extends BaseDocument {
#HippoEssentialsGenerated(internalName = "barcom:title")
public String getTitle() {
System.out.println("Pong"); /* This never triggers */
return getProperty("barcom:title");
}
}
To my understanding, the annotation #Node(jcrType = "barcom:SampleCapsdocument") in SampleCapsComponent.java should hint to the compiler that the content node should be wrapped with the SampleCapsDocument bean - this does not appear to be the case, as the debug console prints Ping but not Pong. What am I doing wrong?
you'll need to call document.getTitle() because values are lazy loaded.
I apologize for a lengthy question, but I really need your help. As a part of our project, I'm currently working on a search engine, that updates results list on the fly: the user types in the first 4 characters and up, and as he types, the results list changes. The search value is typed in a text box, while the results are displayed in a Richfaces component rich:extendedDataTable below. If the search value is removed, the result list is empty. I was able to get that working, however, after a few tries I get ConcurrentModificationException, thrown by the component itself. The initial list I am searching comes from the properties file (because I don't want the search to hit database every time the user types something). I've been banging my head over it for months. What am I missing? Let me show you what I've done:
This is the input text that should trigger the search logic (I make sure that the table does not get updated when the value is less than 4 characters or if the user presses keys, like arrows, shift, and ctrl - this function is "returnunicode(event)"):
<h:inputText id="firmname" value="#{ExtendedTableBean.searchValue}">
<a4j:support reRender="resultsTable" onsubmit="
if ((this.value.length<4 && this.value.length>0) || !returnunicode(event)) {
return false;
}" actionListener="#{ExtendedTableBean.searchForResults}" event="onkeyup" />
</h:inputText>
Action listener is what should update the list. Here is the extendedDataTable, right below the inputText:
<rich:extendedDataTable tableState="#{ExtendedTableBean.tableState}" var="item"
id="resultsTable" value="#{ExtendedTableBean.dataModel}">
... <%-- I'm listing columns here --%>
</rich:extendedDataTable>
Now, if it's ok, I would like to show you the back-end code. I only left the logic that's important to my issue.
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package com.beans;
import java.io.FileInputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.ConcurrentModificationException;
import java.util.List;
import java.util.Properties;
import java.util.concurrent.CopyOnWriteArrayList;
import javax.faces.context.FacesContext;
import javax.faces.event.ActionEvent;
import org.richfaces.model.DataProvider;
import org.richfaces.model.ExtendedTableDataModel;
public class ExtendedTableBean {
private String sortMode="single";
private ExtendedTableDataModel<ResultObject> dataModel;
//ResultObject is a simple pojo and getResultsPerValue is a method that
//read the data from the properties file, assigns it to this pojo, and
//adds a pojo to the list
private Object tableState;
private List<ResultObject> results = new CopyOnWriteArrayList<ResultObject>();
private List<ResultObject> selectedResults =
new CopyOnWriteArrayList<ResultObject>();
private String searchValue;
/**
* This is the action listener that the user triggers, by typing the search value
*/
public void searchForResults(ActionEvent e) {
synchronized(results) {
results.clear();
}
//I don't think it's necessary to clear results list all the time, but here
//I also make sure that we start searching if the value is at least 4
//characters long
if (this.searchValue.length() > 3) {
results.clear();
updateTableList();
} else {
results.clear();
}
dataModel = null; // to force the dataModel to be updated.
}
public List<ResultObject> getResultsPerValue(String searchValue) {
List<ResultObject> resultsList = new CopyOnWriteArrayList<ResultObject>();
//Logic for reading data from the properties file, populating ResultObject
//and adding it to the list
return resultsList;
}
/**
* This method updates a firm list, based on a search value
*/
public void updateTableList() {
try {
List<ResultObject> searchedResults = getResultsPerValue(searchValue);
//Once the results have been retrieved from the properties, empty
//current firm list and replace it with what was found.
synchronized(firms) {
firms.clear();
firms.addAll(searchedFirms);
}
} catch(Throwable xcpt) {
//Exception handling
}
}
/**
* This is a recursive method, that's used to constantly keep updating the
* table list.
*/
public synchronized ExtendedTableDataModel<ResultObject> getDataModel() {
try {
if (dataModel == null) {
dataModel = new ExtendedTableDataModel<ResultObject>(
new DataProvider<ResultObject>() {
public ResultObject getItemByKey(Object key) {
try {
for(ResultObject c : results) {
if (key.equals(getKey(c))){
return c;
}
}
} catch (Exception ex) {
//Exception handling
}
return null;
}
public List<ResultObject> getItemsByRange(
int firstRow, int endRow) {
return Collections.unmodifiableList(results.subList(firstRow, endRow));
}
public Object getKey(ResultObject item) {
return item.getResultName();
}
public int getRowCount() {
return results.size();
}
});
}
} catch (Exception ex) {
//Exception handling
}
return dataModel;
}
//Getters and setters
}
And like I said, it works fine, but when the user types fast or deletes fast (it's hard to catch exactly when it happens), ConcurrentModificationException is thrown. Here's what it looks like exactly:
WARNING: executePhase(RENDER_RESPONSE 6,com.sun.faces.context.FacesContextImpl#4406b8) threw exception
java.util.ConcurrentModificationException
at java.util.AbstractList$Itr.checkForComodification(AbstractList.java:372)
at java.util.AbstractList$Itr.next(AbstractList.java:343)
at org.richfaces.model.ExtendedTableDataModel.walk(ExtendedTableDataModel.java:108)
at org.ajax4jsf.component.UIDataAdaptorBase.walk(UIDataAdaptorBase.java:1156)
at org.richfaces.renderkit.AbstractExtendedRowsRenderer.encodeRows(AbstractExtendedRowsRenderer.java:159)
at org.richfaces.renderkit.AbstractExtendedRowsRenderer.encodeRows(AbstractExtendedRowsRenderer.java:142)
at org.richfaces.renderkit.AbstractExtendedRowsRenderer.encodeChildren(AbstractExtendedRowsRenderer.java:191)
at javax.faces.component.UIComponentBase.encodeChildren(UIComponentBase.java:812)
at org.ajax4jsf.renderkit.RendererBase.renderChild(RendererBase.java:277)
at org.ajax4jsf.renderkit.AjaxChildrenRenderer.encodeAjaxComponent(AjaxChildrenRenderer.java:166)
at org.ajax4jsf.renderkit.AjaxChildrenRenderer.encodeAjaxChildren(AjaxChildrenRenderer.java:83)
at org.ajax4jsf.renderkit.AjaxChildrenRenderer.encodeAjaxComponent(AjaxChildrenRenderer.java:157)
...
Synchronization of the Java code has never been my strongest side, I don't know what would be causing the error and, most importantly, how can I get rid of it. I'm aware, that Richfaces 4.0 has made a lot of changes to rich:extendedDataTable component, I've heard that this was an issue before and now it's resolved. However, I don't have time to upgrade the whole application to Richfaces 4.0 (it's going to be done in phase 2), this is just the small part of the whole project. If there is no way to resolve the issue, described above, then maybe there's a workaround? Or, perhaps, there are other ways to implement similar kind of search, using plain JSF (provided that it's are quick enough to implement). I will appreciate any kind of help or advice on that matter. I hope the code is understandable enough, but if not, let me know, I'll explain further. Thank you in advance, I really appreciate your help.
The problem is concurrent ajax calls to the server. Use the attribute "eventsQueue" in a4j:support. Usually, you should always use "eventsQueue" in any ajax component, with all "eventsQueue" in the same page referencing the same queue, unless you have a very good reason to not doing it.
Also, you'll probably want to look into another ajax attribute: "ajaxSingle".