package bluej.stride.framedjava.elements;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.stream.Stream;
import bluej.debugger.gentype.ConstructorReflective;
import bluej.stride.framedjava.ast.FrameFragment;
import bluej.stride.framedjava.errors.SyntaxCodeError;
import bluej.stride.generic.AssistContentThreadSafe;
import bluej.stride.generic.InteractionManager;
import javafx.application.Platform;
import nu.xom.Element;
import threadchecker.OnThread;
import threadchecker.Tag;
import bluej.editor.moe.MoeSyntaxDocument;
import bluej.parser.ExpressionTypeInfo;
import bluej.parser.entity.EntityResolver;
import bluej.stride.framedjava.ast.JavaFragment;
import bluej.stride.framedjava.ast.JavaFragment.PosInSourceDoc;
import bluej.stride.framedjava.ast.JavaSource;
import bluej.stride.framedjava.ast.JavadocUnit;
import bluej.stride.framedjava.ast.NameDefSlotFragment;
import bluej.stride.framedjava.ast.SlotFragment;
import bluej.stride.framedjava.ast.TypeSlotFragment;
import bluej.stride.framedjava.frames.InterfaceFrame;
import bluej.stride.framedjava.frames.TopLevelFrame;
import bluej.stride.framedjava.slots.ExpressionSlot;
import bluej.stride.generic.Frame.ShowReason;
import bluej.utility.Utility;
public class InterfaceElement
extends DocumentContainerCodeElement implements TopLevelCodeElement{
public static final String ELEMENT = "interface";
private final NameDefSlotFragment interfaceName;
private final List<TypeSlotFragment> extendsTypes;
private JavadocUnit documentation;
| The package name (will not be null, but package name within may be blank
|
private final String packageName;
private final List<ImportElement> imports;
| The list of fields in this class
|
private final List<CodeElement> fields;
| The list of methods in this class
|
private final List<CodeElement> methods;
private final EntityResolver projectResolver;
private InterfaceFrame frame;
| The curly brackets and interface keyword in the generated code (saved for mapping positions)
|
private final FrameFragment openingCurly = new FrameFragment(this.frame, this, "{");
private final FrameFragment closingCurly = new FrameFragment(this.frame, this, "}");
private JavaFragment interfaceKeyword;
| The generated Java code for this interface, used for doing code completion without
| needing to always regenerate the document.
|
private DocAndPositions sourceDocument;
private ExpressionSlot<?> sourceDocumentCompleting;
| A map of documents for given contents. This guards against race hazards, so
| that we use the correct document for the given content, even when we are hopping
| across threads and potentially generating several documents in a short space
| of time, concurrent with looking up information in them.
|
| This cache does not have a size limit, but that shouldn't matter as it is per-instance
| so the only potential differences in source code are down to which slot is being completed,
| giving a limit on the number of documents we could generate for a given source version
| (each InterfaceElement is immutable).
|
private final HashMap<String, DocAndPositions> documentCache = new HashMap<>();
public InterfaceElement(InterfaceFrame frame, EntityResolver projectResolver, NameDefSlotFragment interfaceName,
List<TypeSlotFragment> extendsTypes, List<CodeElement> fields, List<CodeElement> methods,
JavadocUnit documentation, String packageName, List<ImportElement> imports, boolean enabled)
{
this.frame = frame;
this.interfaceName = interfaceName;
this.extendsTypes = extendsTypes == null ? new ArrayList<>() : new ArrayList<>(extendsTypes);
this.documentation = documentation != null ? documentation : new JavadocUnit("");
this.packageName = (packageName == null) ? "" : packageName;
this.imports = new ArrayList<>(imports);
this.fields = new ArrayList<>(fields);
this.fields.forEach(field -> field.setParent(this));
this.methods = new ArrayList<>(methods);
this.methods.forEach(method -> method.setParent(this));
this.enable = enabled;
this.projectResolver = projectResolver;
}
public InterfaceElement(Element el, EntityResolver projectResolver, String packageName)
{
this.projectResolver = projectResolver;
interfaceName = new NameDefSlotFragment(el.getAttributeValue("name"));
Element javadocEL = el.getFirstChildElement("javadoc");
if (javadocEL != null)
{
documentation = new JavadocUnit(javadocEL);
}
else
{
documentation = new JavadocUnit("");
}
extendsTypes = TopLevelCodeElement.xmlToTypeList(el, "extends", "extendstype", "type");
this.packageName = packageName;
imports = Utility.mapList(TopLevelCodeElement.fillChildrenElements(this, el, "imports"), e -> (ImportElement)e);
fields = TopLevelCodeElement.fillChildrenElements(this, el, "fields");
methods = TopLevelCodeElement.fillChildrenElements(this, el, "methods");
enable = Boolean.valueOf(el.getAttributeValue("enable"));
}
| Creates an interface element with minimum information (when creating new interface from template name)
|
public InterfaceElement(EntityResolver entityResolver, String interfaceName, String packageName)
{
this(null, entityResolver, new NameDefSlotFragment(interfaceName), null, Collections.emptyList(),
Collections.emptyList(), null, packageName, Collections.emptyList(), true);
}
@Override
public ExpressionTypeInfo getCodeSuggestions(PosInSourceDoc pos, ExpressionSlot<?> completing)
{
MoeSyntaxDocument doc = getSourceDocument(completing);
return doc.getParser().getExpressionType(pos.offset, getSourceDocument(completing));
}
@Override
public LocatableElement toXML()
{
LocatableElement interfaceEl = new LocatableElement(this, ELEMENT);
interfaceEl.addAttributeCode("name", interfaceName);
if (!extendsTypes.isEmpty())
{
interfaceEl.appendChild(
TopLevelCodeElement.typeListToXML(
extendsTypes,
"extends", "extendstype", "type"));
}
addEnableAttribute(interfaceEl);
if (documentation != null) {
interfaceEl.appendChild(documentation.toXML());
}
appendCollection(interfaceEl, imports, "imports");
appendCollection(interfaceEl, fields, "fields");
appendCollection(interfaceEl, methods, "methods");
interfaceEl.addAttribute(TopLevelCodeElement.getStrideVersionAttribute());
return interfaceEl;
}
private void appendCollection(Element topEl, List<? extends CodeElement> collection, String name)
{
Element collectionEl = new Element(name);
collection.forEach(element -> collectionEl.appendChild(element.toXML()));
topEl.appendChild(collectionEl);
}
@Override
@OnThread(Tag.FXPlatform)
public JavaSource toJavaSource()
{
return getDAP(null).java;
}
@OnThread(Tag.FXPlatform)
private JavaSource generateJavaSource()
{
List<JavaFragment> header = new ArrayList<>();
header.add(new FrameFragment(frame, this, "public "));
interfaceKeyword = new FrameFragment(frame, this, "interface ");
Collections.addAll(header, interfaceKeyword, interfaceName);
if (!extendsTypes.isEmpty()) {
Collections.addAll(header, space(), f(frame, "extends"), space());
header.addAll(extendsTypes.stream().collect(Utility.intersperse(() -> f(frame, ", "))));
}
JavaSource java = new JavaSource(null, header);
java.prependJavadoc(documentation.getJavaCode());
java.prependLine(Arrays.asList((JavaFragment) f(frame, "")), null);
Utility.backwards(CodeElement.toJavaCodes(imports)).forEach(imp -> java.prepend(imp));
java.prependLine(Collections.singletonList(f(frame, "import lang.stride.*;")), null);
if (!packageName.equals(""))
java.prependLine(Arrays.asList(f(frame, "package " + packageName + ";")), null);
openingCurly.setFrame(frame);
java.appendLine(Arrays.asList(openingCurly), null);
fields.stream().filter(f -> f.isEnable()).forEach(f -> java.addIndented(f.toJavaSource()));
methods.stream().filter(m -> m.isEnable()).forEach(m -> {
java.appendLine(Arrays.asList((JavaFragment) f(frame, "")), null);
java.addIndented(m.toJavaSource());
});
closingCurly.setFrame(frame);
java.appendLine(Arrays.asList(closingCurly), null);
return java;
}
@Override
public InterfaceFrame createFrame(InteractionManager editor)
{
frame = new InterfaceFrame(editor, projectResolver, packageName, imports, documentation, interfaceName, extendsTypes, enable);
fields.forEach(member -> frame.getfieldsCanvas().insertBlockAfter(member.createFrame(editor), null));
methods.forEach(member -> frame.getMethodsCanvas().insertBlockAfter(member.createFrame(editor), null));
return frame;
}
@Override
public InterfaceFrame createTopLevelFrame(InteractionManager editor)
{
return createFrame(editor);
}
@Override
public List getImports()
{
return Collections.unmodifiableList(imports);
}
@Override
public String getName()
{
return interfaceName.getContent();
}
public List getExtends()
{
return Utility.mapList(extendsTypes, TypeSlotFragment::getContent);
}
public List extends CodeElement> getMethods()
{
return methods;
}
public List extends CodeElement> getFields()
{
return fields;
}
@Override
public List childrenUpTo(CodeElement c)
{
List<CodeElement> joined = new ArrayList<>();
joined.addAll(fields);
joined.addAll(methods);
return joined.subList(0, joined.indexOf(c));
}
@Override
public String getStylePrefix()
{
return "interface-";
}
@Override
public EntityResolver getResolver()
{
return getSourceDocument(null).getParser();
}
@Override
public TopLevelFrame getFrame()
{
return frame;
}
@Override
public InteractionManager getEditor()
{
return frame.getEditor();
}
@Override
public void show(ShowReason reason)
{
frame.show(reason);
}
@OnThread(Tag.FXPlatform)
private MoeSyntaxDocument getSourceDocument(ExpressionSlot completing)
{
return getDAP(completing).getDocument(projectResolver);
}
@OnThread(Tag.FXPlatform)
private synchronized DocAndPositions getDAP(ExpressionSlot completing)
{
if (sourceDocument == null || sourceDocumentCompleting != completing)
{
IdentityHashMap<JavaFragment, Integer> positions = new IdentityHashMap<>();
sourceDocumentCompleting = completing;
JavaSource java = generateJavaSource();
String src = java.toMemoryJavaCodeString(positions, completing);
if (documentCache.containsKey(src))
{
sourceDocument = documentCache.get(src);
sourceDocument.fragmentPositions.putAll(positions);
}
else
{
sourceDocument = new DocAndPositions(src, java, positions);
documentCache.put(src, sourceDocument);
}
}
return sourceDocument;
}
@Override
public Stream streamContained()
{
Stream<CodeElement> result = streamContained(fields);
return Stream.concat(result, streamContained(methods));
}
@Override
protected Stream getDirectSlotFragments()
{
return Stream.<SlotFragment>concat(Stream.<SlotFragment>of(interfaceName), extendsTypes.stream()).filter(s -> s != null);
}
@Override
public void updateSourcePositions()
{
Platform.runLater(() -> getSourceDocument(null));
}
@Override
public List getSuperConstructors()
{
return Collections.emptyList();
}
@Override
public List getThisConstructors()
{
return Collections.emptyList();
}
private static class DocAndPositions
{
public final JavaSource java;
public final IdentityHashMap<JavaFragment, Integer> fragmentPositions;
private String src;
private MoeSyntaxDocument document;
public DocAndPositions(String src, JavaSource java, IdentityHashMap<JavaFragment, Integer> fragmentPositions)
{
this.src = src;
this.java = java;
this.fragmentPositions = fragmentPositions;
}
@OnThread(Tag.FXPlatform)
public MoeSyntaxDocument getDocument(EntityResolver projectResolver)
{
if (document == null)
{
document = new MoeSyntaxDocument(projectResolver);
document.insertString(0, src);
document.enableParser(true);
}
return document;
}
}
@Override
public Stream findEarlyErrors()
{
return findEarlyErrors(toXML().buildLocationMap());
}
}
top,
use,
map,
class InterfaceElement
. InterfaceElement
. InterfaceElement
. InterfaceElement
. getCodeSuggestions
. toXML
. appendCollection
. toJavaSource
. generateJavaSource
. createFrame
. createTopLevelFrame
. getImports
. getName
. getExtends
. getMethods
. getFields
. childrenUpTo
. getStylePrefix
. getResolver
. getFrame
. getEditor
. show
. getSourceDocument
. getDAP
. streamContained
. getDirectSlotFragments
. updateSourcePositions
. getSuperConstructors
. getThisConstructors
top,
use,
map,
class DocAndPositions
. DocAndPositions
. getDocument
. findEarlyErrors
478 neLoCode
+ 15 LoComm