Home » Modeling » TMF (Xtext) » Import issues regarding qualified names
Import issues regarding qualified names [message #1759209] |
Sat, 08 April 2017 01:20 |
Finn Rayment Messages: 26 Registered: April 2017 |
Junior Member |
|
|
Hi Christian, (and anyone else available)
With my previous question asking about cross-referencing between files, it works brilliantly, but I have realised a flaw as a result of it.
Lets say I have two folders, one named "first" and one named "second"; they both have two files with the same name. How am I supposed to differentiate? Well I would add in the folder as part of the qualified name to differentiate:
use first.Test
use second.Test
class ChildOfFirst parent first.Test { }
class ChildOfSecond parent second.Test { }
The import code you gave me works fine if I only really use something out of one of these, but when I have two, how can I programmatically differentiate between the two? To be a pain, theres three ways I really need this to go:
- If a local type is defined:
- It can be referenced simply by typing in the types name, example Class
- If ANY AMOUNT of external types are defined:
- Define them by typing in the fully qualified name, example: test.Class
- If the local type is NOT defined:
- If ONE external type is defined:
- It can be referenced simply by typing in the types name, example Class
- If MORE THAN ONE external type is defined:
- Define them by typing in the fully qualified name, example: test.Class
Maybe I'm asking for too much I don't know....
My DSL is as follows:
Model
grammar org.thusix.omicron.Omicron with org.eclipse.xtext.common.Terminals
generate omicron "http://www.thusix.org/omicron/Omicron"
Model:
(
package=PackageDeclaration
imports+=ImportDeclaration*
declarations+=TopDeclaration*
)?
;
PackageDeclaration:
'package' name=FQN
;
ImportDeclaration:
'use' importURI=FQN
;
TopDeclaration:
Type | VariableDeclaration
;
TypeType:
Primitive | TypeRef
;
TypeRef:
ref=[Type | FQN]
;
Type:
FunctionType | ClassType | StructType | EnumType
;
FunctionType:
modifiers+=Modifier* 'func' name=ID '(' ')' ':' returnType=TypeType LBRACE
fields+=Field*
RBRACE
;
ClassType:
modifiers+=TypeModifier* 'class' name=ID ('parent' superType=TypeRef)? ('inherits' templates+=TypeRef*)? LBRACE
fields+=Field*
RBRACE
;
StructType:
modifiers+=TypeModifier* 'struct' name=ID LBRACE
declares+=VariableDeclaration*
RBRACE
;
EnumType:
modifiers+=TypeModifier* 'enum' name=ID LBRACE
values+=ID*
RBRACE
;
Field:
VariableDeclaration | Type
;
VariableDeclaration:
modifiers+=Modifier* name=ID ':' type=TypeType
;
/* Statements */
/* Terminals and strings. */
TypeModifier:
'public' |
'final' |
'abstract'
;
Modifier:
TypeModifier |
'protected' |
'private' |
'static'
;
Primitive:
name=('int' |
'long' |
'short' |
'float' |
'double' |
'str' |
'char' |
'bool' |
'obj' |
'byte' |
'nil')
;
FQN:
ID ('.' ID)*
;
terminal LBRACE: '{' ;
terminal RBRACE: '}' ;
Christian's Import Resolver:
package org.thusix.omicron.resolvers;
import com.google.inject.Inject
import org.eclipse.emf.ecore.EObject
import org.eclipse.xtext.resource.FileExtensionProvider
import org.eclipse.xtext.scoping.impl.ImportUriResolver
public class OmicronImportResolver extends ImportUriResolver {
@Inject
FileExtensionProvider fileExtensionProvider;
override String apply(EObject from) {
// TODO make more robust / potentially work in standlone mode if needed
// or classpath:/my/pack/xxxx.mydsl if needed as well
var apply = super.apply(from);
if (apply === null) {
return apply;
}
var result = "platform:/resource/" + from.eResource().getURI().segmentsList().get(1) + "/"
+ apply.replaceAll("\\.", "/") + "." + fileExtensionProvider.getPrimaryFileExtension();
return result;
}
}
I'll include anything else you need if required.
Any help is much appreciated all.
[Updated on: Sat, 08 April 2017 06:37] Report message to a moderator
|
|
| | | |
Re: Using filenames in cross-references and fixing import issues [message #1759217 is a reply to message #1759216] |
Sat, 08 April 2017 05:35 |
|
The other idea would be to simply give the elements
Simple and qualified names and let the default behaviour do. the job for that
public class StrangeDefaultResourceDescriptionStrategy extends DefaultResourceDescriptionStrategy {
@Override
public boolean createEObjectDescriptions(EObject eObject, IAcceptor<IEObjectDescription> acceptor) {
if (eObject instanceof Type) {
Type type = (Type)eObject;
if (type.getName() != null) {
acceptor.accept(EObjectDescription.create(QualifiedName.create(type.getName()), type));
}
}
return super.createEObjectDescriptions(eObject, acceptor);
}
}
def Class<? extends IDefaultResourceDescriptionStrategy> bindIDefaultResourceDescriptionStrategy() {
StrangeDefaultResourceDescriptionStrategy
}
this has unforunately a first one wins rule.
what you dont like if i got you right
Twitter : @chrdietrich
Blog : https://www.dietrich-it.de
[Updated on: Sat, 08 April 2017 06:23] Report message to a moderator
|
|
| |
Re: Using filenames in cross-references and fixing import issues [message #1759219 is a reply to message #1759217] |
Sat, 08 April 2017 06:22 |
|
well you can adapt this as well but i dont know if you can live with the perfomance impacts
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.xtext.naming.QualifiedName;
import org.eclipse.xtext.resource.IEObjectDescription;
import org.eclipse.xtext.resource.IResourceDescription;
import org.eclipse.xtext.resource.IResourceDescriptions;
import org.eclipse.xtext.resource.ISelectable;
import org.eclipse.xtext.scoping.IScope;
import org.eclipse.xtext.scoping.impl.ImportUriGlobalScopeProvider;
import org.eclipse.xtext.scoping.impl.MultimapBasedSelectable;
import org.eclipse.xtext.scoping.impl.SelectableBasedScope;
import com.google.common.base.Predicate;
import com.google.common.collect.Lists;
public class StrangeImportUriGlobalScopeProvider extends ImportUriGlobalScopeProvider {
@Override
protected IScope getScope(Resource resource, boolean ignoreCase, EClass type, Predicate<IEObjectDescription> filter) {
final LinkedHashSet<URI> uniqueImportURIs = getImportedUris(resource);
IResourceDescriptions descriptions = getResourceDescriptions(resource, uniqueImportURIs);
List<URI> urisAsList = Lists.newArrayList(uniqueImportURIs);
Collections.reverse(urisAsList);
IScope scope = IScope.NULLSCOPE;
List<IEObjectDescription> descs = new ArrayList<>();
for (URI uri : urisAsList) {
IResourceDescription description = descriptions.getResourceDescription(uri);
descs.addAll(Lists.newArrayList(description.getExportedObjects()));
}
MultimapBasedSelectable selectable = new MultimapBasedSelectable(descs);
return new SelectableBasedScope2(scope, selectable, filter, type, ignoreCase);
// for (URI uri : urisAsList) {
// scope = createLazyResourceScope(scope, uri, descriptions, type, filter, ignoreCase);
// }
// return scope;
}
protected IScope createLazyResourceScope(IScope parent, final URI uri, final IResourceDescriptions descriptions,
EClass type, final Predicate<IEObjectDescription> filter, boolean ignoreCase) {
IResourceDescription description = descriptions.getResourceDescription(uri);
return SelectableBasedScope.createScope(parent, description, filter, type, ignoreCase);
}
static class SelectableBasedScope2 extends SelectableBasedScope {
public SelectableBasedScope2(IScope outer, ISelectable selectable, Predicate<IEObjectDescription> filter,
EClass type, boolean ignoreCase) {
super(outer, selectable, filter, type, ignoreCase);
}
@Override
protected IEObjectDescription getSingleLocalElementByName(QualifiedName name) {
Iterable<IEObjectDescription> result = getLocalElementsByName(name);
Iterator<IEObjectDescription> iterator = result.iterator();
if (iterator.hasNext()) {
IEObjectDescription next = iterator.next();
if (iterator.hasNext()) {
return null;
}
return next;
}
return null;
}
}
}
override bindIGlobalScopeProvider() {
StrangeImportUriGlobalScopeProvider
}
Twitter : @chrdietrich
Blog : https://www.dietrich-it.de
|
|
| | | | | | | |
Re: Using filenames in cross-references and fixing import issues [message #1759228 is a reply to message #1759226] |
Sat, 08 April 2017 07:37 |
Finn Rayment Messages: 26 Registered: April 2017 |
Junior Member |
|
|
Pardon me, I use "test.Deck" because as I generate my Java source, that package declaration is what specifies the folder I place my files in.
The src-gen folder has a "test" package with both files in it.
Back in my previous questions, you were able to help me setup an import system that would trace through that package declaration system, not the literal file system, so regardless of where the files are located, I still want the package-system to be used.
Once I get the DSL going I'll obviously stick an error-checker in to force people to make the file system adhere to the package system. So for now, regardless of what the file system says, please assume that it is identical to that of the package one.
I've taken another screenshot with a bunch of examples of the current state, and some comments as to what is happening.
PS: I'm slapping that [Type | Model] thing in now to see how it goes.
PPS: It works brilliant! Thankyou. First part of the problem solved. Now just this above import nonsense.
[Updated on: Sat, 08 April 2017 07:41] Report message to a moderator
|
|
| | | | |
Re: Using filenames in cross-references and fixing import issues [message #1759234 is a reply to message #1759232] |
Sat, 08 April 2017 08:08 |
|
maybe
public class StrangeNameProvider extends DefaultDeclarativeQualifiedNameProvider {
QualifiedName qualifiedName(Model type) {
// use complete file name
String[] segments = type.eResource().getURI().trimFileExtension().segments();
return QualifiedName.create(Arrays.copyOfRange(segments, 2, segments.length));
}
}
// make model be a type
Model returns Type: {Model}
(
package=PackageDeclaration
imports+=ImportDeclaration*
declarations+=TopDeclaration*
)?
;
take care of type types and model type available under simply/qualified name
public class StrangeDefaultResourceDescriptionStrategy extends DefaultResourceDescriptionStrategy {
@Override
public boolean createEObjectDescriptions(EObject eObject, IAcceptor<IEObjectDescription> acceptor) {
if (eObject instanceof ClassType) {
ClassType type = (ClassType)eObject;
if (type.getName() != null) {
acceptor.accept(EObjectDescription.create(QualifiedName.create(type.getName()), type));
}
} else if (eObject instanceof EnumType) {
EnumType type = (EnumType)eObject;
if (type.getName() != null) {
acceptor.accept(EObjectDescription.create(QualifiedName.create(type.getName()), type));
}
} else if (eObject instanceof StructType) {
StructType type = (StructType)eObject;
if (type.getName() != null) {
acceptor.accept(EObjectDescription.create(QualifiedName.create(type.getName()), type));
}
} else if (eObject instanceof FunctionType) {
FunctionType type = (FunctionType)eObject;
if (type.getName() != null) {
acceptor.accept(EObjectDescription.create(QualifiedName.create(type.getName()), type));
}
} else if (eObject instanceof Model) {
Model type = (Model)eObject;
acceptor.accept(EObjectDescription.create(QualifiedName.create(type.eResource().getURI().trimFileExtension().lastSegment()), type));
}
return super.createEObjectDescriptions(eObject, acceptor);
}
}
Twitter : @chrdietrich
Blog : https://www.dietrich-it.de
|
|
| | |
Re: Using filenames in cross-references and fixing import issues [message #1759242 is a reply to message #1759237] |
Sat, 08 April 2017 09:06 |
|
well the number of customizations is going to explode.
i have my doubt there is anybody on the planet that can understand the requirements ....
and they get stranger again,
why cant you import nested classes under the simple name if it is unique ?!? ?????????
i have the feeling i should stop supporting you at this point
that is too much WORK for me.
override configureIScopeProviderDelegate(Binder binder) {
binder.bind(IScopeProvider).annotatedWith(Names.named(AbstractDeclarativeScopeProvider.NAMED_DELEGATE)).to(StrangeImportedNamespaceAwareLocalScopeProvider);
}
public class StrangeImportedNamespaceAwareLocalScopeProvider extends ImportedNamespaceAwareLocalScopeProvider {
@Inject
SimpleNameProvider2 snp;
@Override
protected ISelectable internalGetAllDescriptions(Resource resource) {
Iterable<EObject> allContents = new Iterable<EObject>(){
@Override
public Iterator<EObject> iterator() {
return EcoreUtil.getAllContents(resource, false);
}
};
Iterable<IEObjectDescription> allDescriptions = Scopes.scopedElementsFor(allContents, snp);
return new MultimapBasedSelectable(allDescriptions);
}
@Override
protected List<ImportNormalizer> getImportedNamespaceResolvers(EObject context, boolean ignoreCase) {
List<ImportNormalizer> importedNamespaceResolvers = super.getImportedNamespaceResolvers(context, ignoreCase);
if (context instanceof Model) {
for (ImportDeclaration decl : ((Model) context).getImports()) {
// relative import, might no do what you want
// class AClass parent TestClass.NestedClass {}
importedNamespaceResolvers.add(doCreateImportNormalizer(getQualifiedNameConverter().toQualifiedName(decl.getImportURI()), true, ignoreCase));
}
}
return importedNamespaceResolvers;
}
static class SimpleNameProvider2 extends SimpleNameProvider {
@Override
public QualifiedName getFullyQualifiedName(EObject obj) {
if (obj instanceof Model) {
return QualifiedName.create(obj.eResource().getURI().trimFileExtension().lastSegment());
}
return super.getFullyQualifiedName(obj);
}
}
}
maybe you should do a completely different approach and
- remove the customization of bindIGlobalScopeProvider
- remove the customization of bindImportUriResolver
- remove the customization of IDefaultResourceDescriptionStrategy
- remove the customization of configureIScopeProviderDelegate
- change the grammar to
ImportDeclaration:
'use' mode=[Model|FQN]
;
and implement this one here:
class MyDslFinnScopeProvider extends AbstractMyDslFinnScopeProvider {
override getScope(EObject context, EReference reference) {
if (MyDslFinnPackage.Literals.TYPE_REF__REF === reference) {
val List<IEObjectDescription> scope = new ArrayList
// TODO traverse content up to model (EcoreUtil2.getContainerOfType)
// collect all local types and the types referenced in the import.
// calculate the visible elements and there name
val EObject someObject = ....
val QualifiedName name = QualifiedName.create("a","b","C","D")
scope.add(EObjectDescription.create(name, someObject));
return new SimpleScope(IScope.NULLSCOPE, scope)
}
super.getScope(context, reference)
}
}
Twitter : @chrdietrich
Blog : https://www.dietrich-it.de
|
|
|
Re: Using filenames in cross-references and fixing import issues [message #1759244 is a reply to message #1759242] |
Sat, 08 April 2017 12:08 |
Finn Rayment Messages: 26 Registered: April 2017 |
Junior Member |
|
|
Thanks for all your help Christian, I'm glad you've been able to support me for so long with such absurd requests. If you feel it is too much for you, please let me know! You come before me!
I've been poking around for a while, and have come to an almost perfectly clear solution. If you look at the below picture:
The middle section specifically, I can get the three of them to work, but the other three wont work. Thats fine, because I want you to take a look at the DSLNameProvider:
class OmicronNameProvider extends DefaultDeclarativeQualifiedNameProvider {
def QualifiedName qualifiedName(Model type) {
return QualifiedName.create(type.eResource().getURI().trimFileExtension().lastSegment());
}
/*def QualifiedName qualifiedName(Model type) {
// use complete file name
var String[] segments = type.eResource().getURI().trimFileExtension().segments();
return QualifiedName.create(Arrays.copyOfRange(segments, 2, segments.length));
}
*/
}
This picture, is taken using the top solution. Except! If I instead decide to use the bottom function, and comment out the top one, this happens:
See how the errors flip between the two? I think I understand why this happens too, because it's validating one way as being correct but not the other. I was wondering if you know how to make it accept both of these functions, or somehow merge the two together? If we can get it to accept both, then thats literally all there is to it!
I also tinkered with the RuntimeModule, and here are the functions you gave me that seemed to work correctly. I'm sorry about before with all the crap with it...
class OmicronRuntimeModule extends AbstractOmicronRuntimeModule {
// Lets me use the local file as a type both with and without the folder as a part of the name...
override configureIScopeProviderDelegate(Binder binder) {
binder.bind(IScopeProvider).annotatedWith(Names.named(AbstractDeclarativeScopeProvider.NAMED_DELEGATE)).to(StrangeImportedNamespaceAwareLocalScopeProvider);
}
// Requires imports to be added before they become available in the local scope.
override bindIGlobalScopeProvider() {
ImportUriGlobalScopeProvider
}
// Provides the name of the type. This includes the folder prefixes.
override bindIQualifiedNameProvider() {
OmicronNameProvider
}
// Converts the import into a valid format.
def Class<? extends ImportUriResolver> bindImportUriResolver() {
OmicronImportUriResolver
}
}
And a few modifications to StrangeImportedNamespaceAwareLocalScopeProvider which was very useful so thankyou!
package org.thusix.omicron.resolvers;
import java.util.Arrays;
import java.util.Iterator;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.xtext.naming.QualifiedName;
import org.eclipse.xtext.naming.SimpleNameProvider;
import org.eclipse.xtext.resource.IEObjectDescription;
import org.eclipse.xtext.resource.ISelectable;
import org.eclipse.xtext.scoping.Scopes;
import org.eclipse.xtext.scoping.impl.ImportedNamespaceAwareLocalScopeProvider;
import org.eclipse.xtext.scoping.impl.MultimapBasedSelectable;
import org.thusix.omicron.omicron.ImportDeclaration;
import org.thusix.omicron.omicron.Model;
import org.thusix.omicron.omicron.Type;
import com.google.inject.Inject;
public class StrangeImportedNamespaceAwareLocalScopeProvider extends ImportedNamespaceAwareLocalScopeProvider {
@Inject
SimpleNameProvider2 snp;
@Override
protected ISelectable internalGetAllDescriptions(Resource resource) {
Iterable<EObject> allContents = new Iterable<EObject>(){
@Override
public Iterator<EObject> iterator() {
return EcoreUtil.getAllContents(resource, false);
}
};
Iterable<IEObjectDescription> allDescriptions = Scopes.scopedElementsFor(allContents, snp);
return new MultimapBasedSelectable(allDescriptions);
}
static class SimpleNameProvider2 extends SimpleNameProvider {
// Used for local qualified name usage with folder prefix at the beginning.
EObject localWithFolders = null;
@Override
public QualifiedName getFullyQualifiedName(EObject obj) {
if (obj instanceof ImportDeclaration) {
localWithFolders = obj;
}
if (obj instanceof Type && localWithFolders != null) {
String[] segments = localWithFolders.eResource().getURI().trimFileExtension().segments();
localWithFolders = null;
return QualifiedName.create(Arrays.copyOfRange(segments, 2, segments.length));
}
if (obj instanceof Model) {
return QualifiedName.create(obj.eResource().getURI().trimFileExtension().lastSegment());
}
return super.getFullyQualifiedName(obj);
}
}
}
[Updated on: Sat, 08 April 2017 12:12] Report message to a moderator
|
|
| |
Goto Forum:
Current Time: Thu Sep 26 08:51:01 GMT 2024
Powered by FUDForum. Page generated in 0.06964 seconds
|