Skip to content

Commit 9dc9860

Browse files
committed
Add Service Index
1 parent 7b0bb26 commit 9dc9860

File tree

6 files changed

+442
-0
lines changed

6 files changed

+442
-0
lines changed

META-INF/plugin.xml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,11 @@ It is a great inspiration for possible solutions and parts of the code.</p>
5050
<extensions defaultExtensionNs="com.intellij">
5151
<!-- Add your extensions here -->
5252
<php.typeProvider2 implementation="com.cedricziel.idea.typo3.provider.GeneralUtilityTypeProvider" />
53+
<php.typeProvider2 implementation="com.cedricziel.idea.typo3.provider.GeneralUtilityServiceTypeProvider" />
5354
<php.typeProvider2 implementation="com.cedricziel.idea.typo3.provider.ObjectManagerTypeProvider" />
55+
56+
<!-- indexes -->
57+
<fileBasedIndex implementation="com.cedricziel.idea.typo3.index.ServiceIndex"/>
5458
</extensions>
5559

5660
<extensions defaultExtensionNs="com.jetbrains.php">
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
package com.cedricziel.idea.typo3.domain;
2+
3+
import org.jetbrains.annotations.NotNull;
4+
5+
import java.io.Serializable;
6+
7+
public class TYPO3ServiceDefinition implements Serializable {
8+
9+
@NotNull
10+
private final String id;
11+
private String extensionName;
12+
private String className;
13+
private String title;
14+
private String description;
15+
private String subType;
16+
private Boolean available;
17+
private Integer priority;
18+
private Integer quality;
19+
private String os;
20+
private String exec;
21+
private String signature;
22+
23+
public TYPO3ServiceDefinition(@NotNull String id) {
24+
this.id = id;
25+
}
26+
27+
public String getSubType() {
28+
return subType;
29+
}
30+
31+
public void setSubType(String subType) {
32+
this.subType = subType;
33+
}
34+
35+
public Boolean getAvailable() {
36+
return available;
37+
}
38+
39+
public void setAvailable(Boolean available) {
40+
this.available = available;
41+
}
42+
43+
public Integer getPriority() {
44+
return priority;
45+
}
46+
47+
public void setPriority(Integer priority) {
48+
this.priority = priority;
49+
}
50+
51+
public Integer getQuality() {
52+
return quality;
53+
}
54+
55+
public void setQuality(Integer quality) {
56+
this.quality = quality;
57+
}
58+
59+
public String getOs() {
60+
return os;
61+
}
62+
63+
public void setOs(String os) {
64+
this.os = os;
65+
}
66+
67+
public String getExec() {
68+
return exec;
69+
}
70+
71+
public void setExec(String exec) {
72+
this.exec = exec;
73+
}
74+
75+
@NotNull
76+
public String getId() {
77+
return id;
78+
}
79+
80+
public String getExtensionName() {
81+
return extensionName;
82+
}
83+
84+
public void setExtensionName(String extensionName) {
85+
this.extensionName = extensionName;
86+
}
87+
88+
public void setClass(String aClass) {
89+
this.className = aClass;
90+
}
91+
92+
public String getClassName() {
93+
return className;
94+
}
95+
96+
public void setClassName(String className) {
97+
this.className = className;
98+
}
99+
100+
public String getTitle() {
101+
return title;
102+
}
103+
104+
public void setTitle(String title) {
105+
this.title = title;
106+
}
107+
108+
public String getDescription() {
109+
return description;
110+
}
111+
112+
public void setDescription(String description) {
113+
this.description = description;
114+
}
115+
116+
public String getSignature() {
117+
return signature;
118+
}
119+
120+
public void setSignature(String signature) {
121+
this.signature = signature;
122+
}
123+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
package com.cedricziel.idea.typo3.index;
2+
3+
public class ExtensionIndex {
4+
}
Lines changed: 189 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,189 @@
1+
package com.cedricziel.idea.typo3.index;
2+
3+
import com.cedricziel.idea.typo3.domain.TYPO3ServiceDefinition;
4+
import com.cedricziel.idea.typo3.index.externalizer.ObjectStreamDataExternalizer;
5+
import com.cedricziel.idea.typo3.psi.PhpElementsUtil;
6+
import com.intellij.psi.PsiElement;
7+
import com.intellij.psi.PsiFile;
8+
import com.intellij.psi.PsiRecursiveElementVisitor;
9+
import com.intellij.util.indexing.*;
10+
import com.intellij.util.io.DataExternalizer;
11+
import com.intellij.util.io.EnumeratorStringDescriptor;
12+
import com.intellij.util.io.KeyDescriptor;
13+
import com.jetbrains.php.lang.PhpFileType;
14+
import com.jetbrains.php.lang.parser.PhpElementTypes;
15+
import com.jetbrains.php.lang.patterns.PhpPatterns;
16+
import com.jetbrains.php.lang.psi.elements.*;
17+
import gnu.trove.THashMap;
18+
import org.jetbrains.annotations.NotNull;
19+
20+
import java.util.ArrayList;
21+
import java.util.List;
22+
import java.util.Map;
23+
24+
/**
25+
* An index for service definitions made in `ext_localconf.php` files.
26+
*/
27+
public class ServiceIndex extends FileBasedIndexExtension<String, List<TYPO3ServiceDefinition>> {
28+
29+
public static final ID<String, List<TYPO3ServiceDefinition>> KEY = ID.create("com.cedricziel.idea.typo3.domain.typo3_service_definition");
30+
31+
private final static int VERSION = 1;
32+
33+
private final KeyDescriptor<String> myKeyDescriptor = new EnumeratorStringDescriptor();
34+
35+
private final ObjectStreamDataExternalizer<List<TYPO3ServiceDefinition>> externalizer = new ObjectStreamDataExternalizer<>();
36+
37+
@NotNull
38+
@Override
39+
public FileBasedIndex.InputFilter getInputFilter() {
40+
return file -> file.getFileType() == PhpFileType.INSTANCE && file.getName().equals("ext_localconf.php");
41+
}
42+
43+
@Override
44+
public boolean dependsOnFileContent() {
45+
return true;
46+
}
47+
48+
@NotNull
49+
@Override
50+
public ID<String, List<TYPO3ServiceDefinition>> getName() {
51+
return KEY;
52+
}
53+
54+
/**
55+
* Indexes the file content.
56+
*/
57+
@NotNull
58+
@Override
59+
public DataIndexer<String, List<TYPO3ServiceDefinition>, FileContent> getIndexer() {
60+
61+
return inputData -> {
62+
Map<String, List<TYPO3ServiceDefinition>> map = new THashMap<>();
63+
PsiFile psiFile = inputData.getPsiFile();
64+
psiFile.accept(new MyPsiRecursiveElementWalkingVisitor(map));
65+
66+
return map;
67+
};
68+
}
69+
70+
@NotNull
71+
@Override
72+
public KeyDescriptor<String> getKeyDescriptor() {
73+
return this.myKeyDescriptor;
74+
}
75+
76+
@NotNull
77+
@Override
78+
public DataExternalizer<List<TYPO3ServiceDefinition>> getValueExternalizer() {
79+
return externalizer;
80+
}
81+
82+
@Override
83+
public int getVersion() {
84+
return VERSION;
85+
}
86+
87+
private class MyPsiRecursiveElementWalkingVisitor extends PsiRecursiveElementVisitor {
88+
89+
private Map<String, List<TYPO3ServiceDefinition>> map;
90+
91+
MyPsiRecursiveElementWalkingVisitor(Map<String, List<TYPO3ServiceDefinition>> map) {
92+
this.map = map;
93+
}
94+
95+
@Override
96+
public void visitElement(PsiElement element) {
97+
if ((element instanceof MethodReference) && PhpElementsUtil.isMethodWithFirstStringOrFieldReference(element, "addService")) {
98+
visitServiceCreation(element);
99+
}
100+
super.visitElement(element);
101+
}
102+
103+
private void visitServiceCreation(PsiElement element) {
104+
MethodReference methodReference = (MethodReference) element;
105+
// A service definition should contain at least 4 arguments
106+
if (methodReference.getParameters().length < 4) {
107+
return;
108+
}
109+
110+
PsiElement extensionNameParam = methodReference.getParameters()[0]; // Extension name
111+
PsiElement serviceNameParam = methodReference.getParameters()[1]; // Service name
112+
PsiElement classNameParam = methodReference.getParameters()[2]; // Implementing Class (may be short name)
113+
PsiElement optionsArrayParam = methodReference.getParameters()[3]; // Array parameters
114+
115+
String serviceId = serviceNameParam.getText();
116+
List<TYPO3ServiceDefinition> serviceMap;
117+
if (!map.containsKey(serviceId)) {
118+
serviceMap = new ArrayList<>();
119+
map.put(serviceId, serviceMap);
120+
} else {
121+
serviceMap = map.get(serviceId);
122+
}
123+
124+
TYPO3ServiceDefinition serviceDefinition = new TYPO3ServiceDefinition(serviceId);
125+
serviceDefinition.setExtensionName(extensionNameParam.getText());
126+
if (classNameParam instanceof ClassConstantReference) {
127+
PhpExpression classReference = ((ClassConstantReference) classNameParam).getClassReference();
128+
if (classReference instanceof PhpReference) {
129+
serviceDefinition.setClass(((PhpReference) classReference).getFQN());
130+
131+
PhpReference ref = (PhpReference) classReference;
132+
serviceDefinition.setSignature(ref.getSignature());
133+
}
134+
135+
if (optionsArrayParam instanceof ArrayCreationExpression) {
136+
ArrayCreationExpression arrayExpression = (ArrayCreationExpression) optionsArrayParam;
137+
mapOptionsArrayParam(serviceDefinition, arrayExpression.getHashElements());
138+
}
139+
140+
serviceMap.add(serviceDefinition);
141+
}
142+
143+
map.put(serviceId, serviceMap);
144+
}
145+
146+
private void mapOptionsArrayParam(TYPO3ServiceDefinition serviceDefinition, Iterable<ArrayHashElement> optionsArray) {
147+
for (ArrayHashElement element : optionsArray) {
148+
if (null == element.getKey() || !(element.getKey() instanceof StringLiteralExpression)) {
149+
continue;
150+
}
151+
152+
String key = ((StringLiteralExpression) element.getKey()).getContents();
153+
// Assign string properties of the service definition options
154+
if (null != element.getValue() && element.getValue() instanceof StringLiteralExpression) {
155+
String value = ((StringLiteralExpression) element.getValue()).getContents();
156+
switch (key) {
157+
case "os":
158+
serviceDefinition.setOs(value);
159+
break;
160+
case "title":
161+
serviceDefinition.setTitle(value);
162+
break;
163+
case "description":
164+
serviceDefinition.setDescription(value);
165+
break;
166+
case "subtype":
167+
serviceDefinition.setSubType(value);
168+
break;
169+
case "exec":
170+
serviceDefinition.setExec(value);
171+
break;
172+
}
173+
}
174+
175+
// Assign numbers
176+
if (null != element.getValue() && PhpPatterns.psiElement(PhpElementTypes.NUMBER).accepts(element.getValue())) {
177+
switch (key) {
178+
case "priority":
179+
serviceDefinition.setPriority(new Integer(element.getValue().getText()));
180+
break;
181+
case "quality":
182+
serviceDefinition.setQuality(new Integer(element.getValue().getText()));
183+
break;
184+
}
185+
}
186+
}
187+
}
188+
}
189+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package com.cedricziel.idea.typo3.index.externalizer;
2+
3+
import com.intellij.util.io.DataExternalizer;
4+
import org.jetbrains.annotations.NotNull;
5+
6+
import java.io.*;
7+
8+
/**
9+
* @author Daniel Espendiller <daniel@espendiller.net>
10+
*/
11+
public class ObjectStreamDataExternalizer<T> implements DataExternalizer<T> {
12+
13+
@Override
14+
public void save(@NotNull DataOutput out, T value) throws IOException {
15+
ByteArrayOutputStream stream = new ByteArrayOutputStream();
16+
ObjectOutput output = new ObjectOutputStream(stream);
17+
18+
output.writeObject(value);
19+
20+
out.writeInt(stream.size());
21+
out.write(stream.toByteArray());
22+
}
23+
24+
@Override
25+
public T read(@NotNull DataInput in) throws IOException {
26+
int bufferSize = in.readInt();
27+
byte[] buffer = new byte[bufferSize];
28+
in.readFully(buffer, 0, bufferSize);
29+
30+
ByteArrayInputStream stream = new ByteArrayInputStream(buffer);
31+
ObjectInput input = new ObjectInputStream(stream);
32+
33+
T object = null;
34+
try {
35+
object = (T) input.readObject();
36+
} catch (ClassNotFoundException | ClassCastException ignored) {
37+
}
38+
39+
return object;
40+
}
41+
}

0 commit comments

Comments
 (0)