How to obtain "package" of a class?


Hi,

I am trying to get hold of all packages that are referenced from a module
and its dependencies.

So I get hold of GlobalSearchScopes ?

Module[] depModules = rootModel.getModuleDependencies();
for( Module depModule : depModules )
{
GlobalSearchScope scope = depModule.getModuleScope();

I can then ask for all the classes in these scopes

PsiManager psiManager = PsiManager.getInstance( m_project );
PsiSearchHelper searchHelper = psiManager.getSearchHelper();
PsiClass[] psiClasses = searchHelper.findAllClasses( scope );

But PsiClass doesn't have a getPackage() method (strange!), and I doubt I
should extract the name manually, since I am then outside the Psi tree...
Also, this won't work if a package doesn't have classes in it. So, I am
pretty convinced that I should not bother with the classes at all, but then
what??

Cheers
Niclas

15 comments

Package statements belong to the file not the class. This means you can use the getPackageName() and getPackageStatement() methods available in PsiJavaFIle. Something like this should do the trick:


Bas

0

Bas Leijdekkers wrote:

Package statements belong to the file not the class. This means you can
use the getPackageName() and getPackageStatement() methods available in
PsiJavaFIle. Something like this should do the trick:

 final String packageName =
> ((PsiJavaFile)aClass.getContainingFile()).getPackageName();
> ]]>


I beg to differ, and very strongly indeed! ;o)

1. That assumes there are files, classes can be created on the fly.
2. A Class belongs to a package, irregardless of how it was created.
3. java.lang.Package
4. Packages can exist without Java files in them, resources for instance.
5. Packages can exist without anything in them, in UI do New->Package
6. Will getContainingFile().getPackageName return something valid for
entries in Libraries?


So, back to the original question...

I am trying to get hold of all packages that are referenced from a module
and its dependencies. "Referenced" in this context are;
1. All created "Packages" in the Module(s) involved.
2. All packages of classes in Libraries.
3. And perhaps (not sure yet) all directories in Libraries.


Cheers
Niclas

0

Hello Niclas,

NH> 1. That assumes there are files, classes can be created on the fly.
NH> 2. A Class belongs to a package, irregardless of how it was created.

As far as I understand, this is not true in IntelliJ IDEA. A class created
by PsiElementFactory.createClass() does not belong to any package.

NH> 3. java.lang.Package
NH> 4. Packages can exist without Java files in them, resources for instance.
NH> 5. Packages can exist without anything in them, in UI do New->Package
NH> 6. Will getContainingFile().getPackageName return something valid for
NH> entries in Libraries?
NH> So, back to the original question...
NH>
NH> I am trying to get hold of all packages that are referenced from a
NH> module
NH> and its dependencies. "Referenced" in this context are;
NH> 1. All created "Packages" in the Module(s) involved.
NH> 2. All packages of classes in Libraries.
NH> 3. And perhaps (not sure yet) all directories in Libraries.

IDEA doesn't keep an index of all packages existing in a module or library.
If you care about empty packages, you need to iterate the PsiDirectory tree
and call PsiDirectory.getPackage().

--
Dmitry Jemerov
Software Developer
http://www.jetbrains.com/
"Develop with Pleasure!"


0

Dmitry Jemerov wrote:

IDEA doesn't keep an index of all packages existing in a module or
library. If you care about empty packages, you need to iterate the
PsiDirectory tree and call PsiDirectory.getPackage().


And what do I need to do to have my package references updated when there is
a Move or Rename refactoring going on??

Cheers
Niclas

0

Dmitry Jemerov wrote:

IDEA doesn't keep an index of all packages existing in a module or
library.


Also, what is the PsiPackage class for then??


Cheers
Niclas

0

Hello Niclas,

>> IDEA doesn't keep an index of all packages existing in a module or
>> library. If you care about empty packages, you need to iterate the
>> PsiDirectory tree and call PsiDirectory.getPackage().
>>
NH> And what do I need to do to have my package references updated when
NH> there is a Move or Rename refactoring going on??

Depending on where the references are. References in code will be updated
through the regular mechanism of PsiReference.handleElementRename() and PsiReference.bindToElement().
References outside of code can be renamed by adding a refactoring listener
to RefactoringListenerManager.

--
Dmitry Jemerov
Software Developer
http://www.jetbrains.com/
"Develop with Pleasure!"


0

Hello Niclas,

>> IDEA doesn't keep an index of all packages existing in a module or
>> library.
>>
NH> Also, what is the PsiPackage class for then??

Instances of PsiPackage are created on demand when a package for a directory
is requested.

--
Dmitry Jemerov
Software Developer
http://www.jetbrains.com/
"Develop with Pleasure!"


0

Dmitry Jemerov wrote:

Depending on where the references are. References in code will be updated
through the regular mechanism of PsiReference.handleElementRename() and
PsiReference.bindToElement().


??
If classes doesn't belong to packages, and PsiPackage instances are not kept
alive, how can I hold a PsiReference to a package?


Cheers
Niclas


0

Dmitry Jemerov wrote:

Instances of PsiPackage are created on demand when a package for a
directory is requested.


If I do (to my surprise, figured this out);

private Set getPackages( Module module ) { Set packages = new HashSet(); PsiManager psiMan = PsiManager.getInstance( m_project ); PsiPackage psiPackage = psiMan.findPackage( "" ); GlobalSearchScope scope = module.getModuleRuntimeScope( false ); getSubPackages( psiPackage, scope, packages ); return packages; } private void getSubPackages( PsiPackage psiPackage, GlobalSearchScope scope, Set]]> result )
{
for( PsiPackage child : psiPackage.getSubPackages( scope ) )
{
result.add( child );
getSubPackages( child, scope, result );
}
}

I get hold of all the packages in a particular Module. Are you saying that
these PsiPackage instances will not be 'renamed' and 'moved' automatically
if I hold on to them??


Cheers
Niclas

0

Hello Niclas,

>> Instances of PsiPackage are created on demand when a package for a
>> directory is requested.
>>
NH> If I do (to my surprise, figured this out);
NH>
NH> private Set getPackages( Module module ) NH> { NH> Set packages = new HashSet(); NH> PsiManager psiMan = PsiManager.getInstance( m_project ); NH> PsiPackage psiPackage = psiMan.findPackage( "" ); NH> GlobalSearchScope scope = module.getModuleRuntimeScope( NH> false ); NH> getSubPackages( psiPackage, scope, packages ); NH> return packages; NH> } NH> private void getSubPackages( PsiPackage psiPackage, NH> GlobalSearchScope NH> scope, Set result ) NH> { NH> for( PsiPackage child : psiPackage.getSubPackages( scope ) ) NH> { NH> result.add( child ); NH> getSubPackages( child, scope, result ); NH> } NH> } NH> I get hold of all the packages in a particular Module. Are you NH> saying that these PsiPackage instances will not be 'renamed' and NH>]]> 'moved' automatically if I hold on to them??

Holding on to PsiPackage instances is a bad idea. Internally, PsiManager
holds a map (cache) of qualified name to PsiPackage instances, and the map
is cleared on many PSI changes. If you keep a reference to a PsiPackage and
then the map is cleared and a different PsiPackage instance is created for
the same package name, your reference will not be updated by any refactorings.

--
Dmitry Jemerov
Software Developer
http://www.jetbrains.com/
"Develop with Pleasure!"


0

Hello Niclas,

>> Depending on where the references are. References in code will be
>> updated through the regular mechanism of
>> PsiReference.handleElementRename() and PsiReference.bindToElement().
>>
NH> ??
NH> If classes doesn't belong to packages, and PsiPackage instances are
NH> not kept alive, how can I hold a PsiReference to a package?

In order to have a reference to a package, it's not necessary for the reference
to resolve to exactly the same PsiPackage instance every time resolve() is
called. (In fact, every import statement can contain a number of references
to packages.)

--
Dmitry Jemerov
Software Developer
http://www.jetbrains.com/
"Develop with Pleasure!"


0

Dmitry Jemerov wrote:

Holding on to PsiPackage instances is a bad idea. Internally, PsiManager
holds a map (cache) of qualified name to PsiPackage instances, and the map
is cleared on many PSI changes. If you keep a reference to a PsiPackage
and then the map is cleared and a different PsiPackage instance is created
for the same package name, your reference will not be updated by any
refactorings.


Ok, I am getting the messages of things I can't do and I am still as
clueless as before of what I should do. (Don't get me wrong, your responses
are very timely and I do appreciate the support provided)

Let's try this in a context;

OSGi is a very package oriented system. Packages are versioned and they are
the granularity available for establishing classloader namespaces and
cross-bundle 'class and resource wiring'.
IDEA has a concept of Package as well, both at UI level and also in the Psi
subsystem, but it now seems they are not "first class citizens" as I
expected.
I am attaching additional meta-information to packages, some coming from the
user and some is figured out automatically, that OSGi needs. When
refactorings are happening this can not be lost.

What approach would you guys take to this, then?

Perhaps;

RefactoringListenerManager
.getInstance( project )
.addListenerProvider( listenerProvider );

And upon a refactoring, the listenerProvider.getListener( PsiElement elem )
will be called, where 'elem' is the "current" PsiElement (PsiPackage?) and
the listeners method elementMoved()/elementRenamed() will be called with a
PsiElement (PsiPackage) of the new location/name.

Otoh, I would also need to figure out how to handle deletes and inserts.


Looking at the PsiElement interface, I get the impression that these are
part of tree structure, and holding on to these are also not a great idea,
e.g. replace() and delete().


PsiManager has a addPsiTreeChangeListener() which seems to allow all kinds
of notifications. Is that what I am expected to use?


Cheers
Niclas Hedhman

0

Niclas Hedhman wrote:

Looking at the PsiElement interface, I get the impression that these are
part of tree structure, and holding on to these are also not a great idea,
e.g. replace() and delete().


PsiElement extends UserDataHolder, does that one can add meta-info to each
node?? And if so, what would I need to do to persist that??


Cheers
Niclas



0

Hello Niclas,

NH> OSGi is a very package oriented system. Packages are versioned and they
are
NH> the granularity available for establishing classloader namespaces and
NH> cross-bundle 'class and resource wiring'.
NH> IDEA has a concept of Package as well, both at UI level and also in the
Psi
NH> subsystem, but it now seems they are not "first class citizens" as I
expected.
NH> I am attaching additional meta-information to packages, some coming from
the
NH> user and some is figured out automatically, that OSGi needs. When
NH> refactorings are happening this can not be lost.
NH> What approach would you guys take to this, then?

The best thing to do is to keep a map from package FQname to OSGi specific
data in a project component, and to install necessary listeners to keep the
information up-to-date.

NH> Perhaps;
NH>
NH> RefactoringListenerManager
NH> .getInstance( project )
NH> .addListenerProvider( listenerProvider );
NH> And upon a refactoring, the listenerProvider.getListener( PsiElement
NH> elem ) will be called, where 'elem' is the "current" PsiElement
NH> (PsiPackage?) and the listeners method
NH> elementMoved()/elementRenamed() will be called with a PsiElement
NH> (PsiPackage) of the new location/name.

Yes, it is correct. Refactoring listeners work for package renames/moves.

NH> Otoh, I would also need to figure out how to handle deletes and
NH> inserts.

Because of the PsiPackage lifetime rules, you will need to track creation
and deletion of directories (PsiDirectory items), not directly packages.

NH> Looking at the PsiElement interface, I get the impression that these
NH> are part of tree structure, and holding on to these are also not a
NH> great idea, e.g. replace() and delete().

I'm currently working on an article that will, among other things, document
the lifetime of different data structures in IDEA.
http://www.jetbrains.net/confluence/display/IDEADEV/IntelliJIDEA6.0ArchitecturalOverview

NH> PsiManager has a addPsiTreeChangeListener() which seems to allow all
NH> kinds of notifications. Is that what I am expected to use?

You can use it to track changes in the PsiDirectory structure, but you won't
receive any notifications about PsiPackage elements (if I understand correctly).

--
Dmitry Jemerov
Software Developer
http://www.jetbrains.com/
"Develop with Pleasure!"


0

Hello Niclas,

>> Looking at the PsiElement interface, I get the impression that these
>> are part of tree structure, and holding on to these are also not a
>> great idea, e.g. replace() and delete().
>>
NH> PsiElement extends UserDataHolder, does that one can add meta-info
NH> to each node?? And if so, what would I need to do to persist that??

Yes, you can add your own data to each PSI element, but there is no generic
framework for making this data survive PSI reparses, or for persisting it
between different IDEA sessions.

--
Dmitry Jemerov
Software Developer
http://www.jetbrains.com/
"Develop with Pleasure!"


0

Please sign in to leave a comment.