Convert to Kotlin KotlinLightClassForPackage: fixes for j2k converter
This commit is contained in:
+1
-1
@@ -217,7 +217,7 @@ public class CliLightClassGenerationSupport extends LightClassGenerationSupport
|
|||||||
if (PackagePartClassUtils.getPackageFilesWithCallables(filesInPackage).isEmpty()) return Collections.emptyList();
|
if (PackagePartClassUtils.getPackageFilesWithCallables(filesInPackage).isEmpty()) return Collections.emptyList();
|
||||||
|
|
||||||
//noinspection RedundantTypeArguments
|
//noinspection RedundantTypeArguments
|
||||||
return UtilsPackage.<PsiClass>emptyOrSingletonList(KotlinLightClassForPackage.create(psiManager, packageFqName, scope, filesInPackage));
|
return UtilsPackage.<PsiClass>emptyOrSingletonList(KotlinLightClassForPackage.OBJECT$.create(psiManager, packageFqName, scope, filesInPackage));
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
|
|||||||
+88
-183
@@ -24,9 +24,7 @@ import com.intellij.psi.*
|
|||||||
import com.intellij.psi.impl.compiled.ClsFileImpl
|
import com.intellij.psi.impl.compiled.ClsFileImpl
|
||||||
import com.intellij.psi.impl.light.LightEmptyImplementsList
|
import com.intellij.psi.impl.light.LightEmptyImplementsList
|
||||||
import com.intellij.psi.impl.light.LightModifierList
|
import com.intellij.psi.impl.light.LightModifierList
|
||||||
import com.intellij.psi.javadoc.PsiDocComment
|
|
||||||
import com.intellij.psi.search.GlobalSearchScope
|
import com.intellij.psi.search.GlobalSearchScope
|
||||||
import com.intellij.psi.stubs.PsiClassHolderFileStub
|
|
||||||
import com.intellij.psi.util.CachedValue
|
import com.intellij.psi.util.CachedValue
|
||||||
import com.intellij.psi.util.CachedValueProvider
|
import com.intellij.psi.util.CachedValueProvider
|
||||||
import com.intellij.psi.util.CachedValuesManager
|
import com.intellij.psi.util.CachedValuesManager
|
||||||
@@ -41,34 +39,17 @@ import org.jetbrains.kotlin.psi.JetFile
|
|||||||
|
|
||||||
import javax.swing.*
|
import javax.swing.*
|
||||||
|
|
||||||
public class KotlinLightClassForPackage private(manager: PsiManager, private val packageFqName: FqName, private val searchScope: GlobalSearchScope, files: Collection<JetFile>) : KotlinWrappingLightClass(manager), JetJavaMirrorMarker {
|
public class KotlinLightClassForPackage private(
|
||||||
|
manager: PsiManager,
|
||||||
|
private val packageFqName: FqName,
|
||||||
|
private val searchScope: GlobalSearchScope,
|
||||||
|
files: Collection<JetFile>) : KotlinWrappingLightClass(manager), JetJavaMirrorMarker {
|
||||||
|
|
||||||
public class FileStubCache(private val project: Project) {
|
public class FileStubCache(private val project: Project) {
|
||||||
|
private data class Key(val fqName: FqName, val searchScope: GlobalSearchScope)
|
||||||
private class Key private(private val fqName: FqName, private val searchScope: GlobalSearchScope) {
|
|
||||||
|
|
||||||
override fun equals(o: Any?): Boolean {
|
|
||||||
if (this == o) return true
|
|
||||||
if (o == null || javaClass != o.javaClass) return false
|
|
||||||
|
|
||||||
val key = o as Key
|
|
||||||
|
|
||||||
if (fqName != key.fqName) return false
|
|
||||||
if (searchScope != key.searchScope) return false
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun hashCode(): Int {
|
|
||||||
var result = fqName.hashCode()
|
|
||||||
result = 31 * result + searchScope.hashCode()
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private inner class CacheData {
|
private inner class CacheData {
|
||||||
|
val cache = object : SLRUCache<Key, CachedValue<KotlinPackageLightClassData>>(20, 30) {
|
||||||
private val cache = object : SLRUCache<Key, CachedValue<KotlinPackageLightClassData>>(20, 30) {
|
|
||||||
override fun createValue(key: Key): CachedValue<KotlinPackageLightClassData> {
|
override fun createValue(key: Key): CachedValue<KotlinPackageLightClassData> {
|
||||||
val stubProvider = KotlinJavaFileStubProvider.createForPackageClass(project, key.fqName, key.searchScope)
|
val stubProvider = KotlinJavaFileStubProvider.createForPackageClass(project, key.fqName, key.searchScope)
|
||||||
return CachedValuesManager.getManager(project).createCachedValue<KotlinPackageLightClassData>(stubProvider, /*trackValue = */false)
|
return CachedValuesManager.getManager(project).createCachedValue<KotlinPackageLightClassData>(stubProvider, /*trackValue = */false)
|
||||||
@@ -76,15 +57,9 @@ public class KotlinLightClassForPackage private(manager: PsiManager, private val
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private val cachedValue: CachedValue<CacheData>
|
private val cachedValue: CachedValue<CacheData> = CachedValuesManager.getManager(project).createCachedValue<CacheData>(
|
||||||
|
{ CachedValueProvider.Result.create<CacheData>(CacheData(), PsiModificationTracker.OUT_OF_CODE_BLOCK_MODIFICATION_COUNT) },
|
||||||
{
|
/*trackValue = */ false)
|
||||||
this.cachedValue = CachedValuesManager.getManager(project).createCachedValue<CacheData>(object : CachedValueProvider<CacheData> {
|
|
||||||
override fun compute(): CachedValueProvider.Result<CacheData>? {
|
|
||||||
return CachedValueProvider.Result.create<CacheData>(CacheData(), PsiModificationTracker.OUT_OF_CODE_BLOCK_MODIFICATION_COUNT)
|
|
||||||
}
|
|
||||||
}, /*trackValue = */ false)
|
|
||||||
}
|
|
||||||
|
|
||||||
public fun get(qualifiedName: FqName, searchScope: GlobalSearchScope): CachedValue<KotlinPackageLightClassData> {
|
public fun get(qualifiedName: FqName, searchScope: GlobalSearchScope): CachedValue<KotlinPackageLightClassData> {
|
||||||
synchronized (cachedValue) {
|
synchronized (cachedValue) {
|
||||||
@@ -93,172 +68,112 @@ public class KotlinLightClassForPackage private(manager: PsiManager, private val
|
|||||||
}
|
}
|
||||||
|
|
||||||
class object {
|
class object {
|
||||||
|
|
||||||
public fun getInstance(project: Project): FileStubCache {
|
public fun getInstance(project: Project): FileStubCache {
|
||||||
return ServiceManager.getService<FileStubCache>(project, javaClass<FileStubCache>())
|
return ServiceManager.getService<FileStubCache>(project, javaClass<FileStubCache>())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private val packageClassFqName: FqName // derived from packageFqName
|
{
|
||||||
public val files: Collection<JetFile>
|
assert(!files.isEmpty()) { "No files for package " + packageFqName }
|
||||||
private val hashCode: Int
|
}
|
||||||
private val lightClassDataCache: CachedValue<KotlinPackageLightClassData>
|
|
||||||
private val modifierList: PsiModifierList
|
public val files: Collection<JetFile> =
|
||||||
private val implementsList: LightEmptyImplementsList
|
Sets.newHashSet<JetFile>(files) // needed for hashCode
|
||||||
|
|
||||||
|
private val packageClassFqName: FqName =
|
||||||
|
PackageClassUtils.getPackageClassFqName(packageFqName)
|
||||||
|
|
||||||
|
private val hashCode: Int =
|
||||||
|
computeHashCode()
|
||||||
|
|
||||||
|
private val lightClassDataCache: CachedValue<KotlinPackageLightClassData> =
|
||||||
|
FileStubCache.getInstance(getProject()).get(packageFqName, searchScope)
|
||||||
|
|
||||||
|
private val modifierList: PsiModifierList =
|
||||||
|
LightModifierList(manager, JetLanguage.INSTANCE, PsiModifier.PUBLIC, PsiModifier.FINAL)
|
||||||
|
|
||||||
|
private val implementsList: LightEmptyImplementsList =
|
||||||
|
LightEmptyImplementsList(manager)
|
||||||
|
|
||||||
private val packageClsFile: ClsFileImpl
|
private val packageClsFile: ClsFileImpl
|
||||||
|
|
||||||
{
|
{
|
||||||
this.modifierList = LightModifierList(manager, JetLanguage.INSTANCE, PsiModifier.PUBLIC, PsiModifier.FINAL)
|
|
||||||
this.implementsList = LightEmptyImplementsList(manager)
|
|
||||||
this.packageClassFqName = PackageClassUtils.getPackageClassFqName(packageFqName)
|
|
||||||
assert(!files.isEmpty()) { "No files for package " + packageFqName }
|
|
||||||
this.files = Sets.newHashSet<JetFile>(files) // needed for hashCode
|
|
||||||
this.hashCode = computeHashCode()
|
|
||||||
this.lightClassDataCache = FileStubCache.getInstance(getProject()).get(packageFqName, searchScope)
|
|
||||||
|
|
||||||
val virtualFile = KotlinJavaFileStubProvider.getRepresentativeVirtualFile(files)
|
val virtualFile = KotlinJavaFileStubProvider.getRepresentativeVirtualFile(files)
|
||||||
packageClsFile = object : ClsFileImpl(ClassFileViewProvider(PsiManager.getInstance(getProject()), virtualFile)) {
|
packageClsFile = object : ClsFileImpl(ClassFileViewProvider(PsiManager.getInstance(getProject()), virtualFile)) {
|
||||||
override fun getStub(): PsiClassHolderFileStub<PsiFile> {
|
override fun getStub() = (getDelegate().getContainingFile() as ClsFileImpl).getStub()
|
||||||
return (getDelegate().getContainingFile() as ClsFileImpl).getStub()
|
override fun getPackageName() = this@KotlinLightClassForPackage.packageFqName.asString()
|
||||||
}
|
|
||||||
|
|
||||||
override fun getPackageName(): String {
|
|
||||||
return this@KotlinLightClassForPackage.packageFqName.asString()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
packageClsFile.setPhysical(false)
|
packageClsFile.setPhysical(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getOrigin(): JetClassOrObject? {
|
override fun getOrigin(): JetClassOrObject? = null
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getModifierList(): PsiModifierList? {
|
override fun getFqName(): FqName = packageClassFqName
|
||||||
return modifierList
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun hasModifierProperty(NonNls name: String): Boolean {
|
override fun getModifierList() = modifierList
|
||||||
return modifierList.hasModifierProperty(name)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun isDeprecated(): Boolean {
|
override fun hasModifierProperty(NonNls name: String) = modifierList.hasModifierProperty(name)
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun isInterface(): Boolean {
|
override fun isDeprecated() = false
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun isAnnotationType(): Boolean {
|
override fun isInterface() = false
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun isEnum(): Boolean {
|
override fun isAnnotationType() = false
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getContainingClass(): PsiClass? {
|
override fun isEnum() = false
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getContainingFile(): PsiFile {
|
override fun getContainingClass() = null
|
||||||
return packageClsFile
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun hasTypeParameters(): Boolean {
|
override fun getContainingFile() = packageClsFile
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getTypeParameters(): Array<PsiTypeParameter> {
|
override fun hasTypeParameters() = false
|
||||||
return PsiTypeParameter.EMPTY_ARRAY
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getTypeParameterList(): PsiTypeParameterList? {
|
override fun getTypeParameters() = PsiTypeParameter.EMPTY_ARRAY
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getDocComment(): PsiDocComment? {
|
override fun getTypeParameterList() = null
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getImplementsList(): PsiReferenceList? {
|
override fun getDocComment() = null
|
||||||
return implementsList
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getImplementsListTypes(): Array<PsiClassType> {
|
override fun getImplementsList() = implementsList
|
||||||
return PsiClassType.EMPTY_ARRAY
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getExtendsList(): PsiReferenceList? {
|
override fun getImplementsListTypes() = PsiClassType.EMPTY_ARRAY
|
||||||
// TODO: Find a way to return just Object
|
|
||||||
return super.getExtendsList()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getExtendsListTypes(): Array<PsiClassType> {
|
// TODO: Find a way to return just Object
|
||||||
// TODO see getExtendsList()
|
override fun getExtendsList() = super<KotlinWrappingLightClass>.getExtendsList()
|
||||||
return super.getExtendsListTypes()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getSuperClass(): PsiClass? {
|
// TODO see getExtendsList()
|
||||||
// TODO see getExtendsList()
|
override fun getExtendsListTypes() = super<KotlinWrappingLightClass>.getExtendsListTypes()
|
||||||
return super.getSuperClass()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getSupers(): Array<PsiClass> {
|
// TODO see getExtendsList()
|
||||||
// TODO see getExtendsList()
|
override fun getSuperClass(): PsiClass? = super<KotlinWrappingLightClass>.getSuperClass()
|
||||||
return super.getSupers()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getSuperTypes(): Array<PsiClassType> {
|
// TODO see getExtendsList()
|
||||||
// TODO see getExtendsList()
|
override fun getSupers(): Array<PsiClass> = super<KotlinWrappingLightClass>.getSupers()
|
||||||
return super.getSuperTypes()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getInterfaces(): Array<PsiClass> {
|
// TODO see getExtendsList()
|
||||||
return PsiClass.EMPTY_ARRAY
|
override fun getSuperTypes() = super<KotlinWrappingLightClass>.getSuperTypes()
|
||||||
}
|
|
||||||
|
|
||||||
override fun getInnerClasses(): Array<PsiClass> {
|
override fun getInterfaces() = PsiClass.EMPTY_ARRAY
|
||||||
return PsiClass.EMPTY_ARRAY
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getOwnInnerClasses(): List<PsiClass> {
|
override fun getInnerClasses() = PsiClass.EMPTY_ARRAY
|
||||||
return listOf()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getAllInnerClasses(): Array<PsiClass> {
|
override fun getOwnInnerClasses(): List<PsiClass> = listOf()
|
||||||
return PsiClass.EMPTY_ARRAY
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getInitializers(): Array<PsiClassInitializer> {
|
override fun getAllInnerClasses() = PsiClass.EMPTY_ARRAY
|
||||||
return PsiClassInitializer.EMPTY_ARRAY
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun findInnerClassByName(NonNls name: String, checkBases: Boolean): PsiClass? {
|
override fun getInitializers() = PsiClassInitializer.EMPTY_ARRAY
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getFqName(): FqName {
|
override fun findInnerClassByName(NonNls name: String, checkBases: Boolean) = null
|
||||||
return packageClassFqName
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getName(): String? {
|
override fun getName() = packageClassFqName.shortName().asString()
|
||||||
return packageClassFqName.shortName().asString()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getQualifiedName(): String? {
|
override fun getQualifiedName() = packageClassFqName.asString()
|
||||||
return packageClassFqName.asString()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun isValid(): Boolean {
|
override fun isValid() = files.all { it.isValid() }
|
||||||
return allValid(files)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun copy(): PsiElement {
|
override fun copy() = KotlinLightClassForPackage(getManager(), packageFqName, searchScope, files)
|
||||||
return KotlinLightClassForPackage(getManager(), packageFqName, searchScope, files)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getDelegate(): PsiClass {
|
override fun getDelegate(): PsiClass {
|
||||||
val psiClass = LightClassUtil.findClass(packageClassFqName, lightClassDataCache.getValue().javaFileStub)
|
val psiClass = LightClassUtil.findClass(packageClassFqName, lightClassDataCache.getValue().javaFileStub)
|
||||||
@@ -268,21 +183,15 @@ public class KotlinLightClassForPackage private(manager: PsiManager, private val
|
|||||||
return psiClass
|
return psiClass
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getNavigationElement(): PsiElement {
|
override fun getNavigationElement() = files.iterator().next()
|
||||||
return files.iterator().next()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun isEquivalentTo(another: PsiElement?): Boolean {
|
override fun isEquivalentTo(another: PsiElement?): Boolean {
|
||||||
return another is PsiClass && Comparing.equal((another as PsiClass).getQualifiedName(), getQualifiedName())
|
return another is PsiClass && Comparing.equal(another.getQualifiedName(), getQualifiedName())
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getElementIcon(flags: Int): Icon? {
|
override fun getElementIcon(flags: Int): Icon? = throw UnsupportedOperationException("This should be done by JetIconProvider")
|
||||||
throw UnsupportedOperationException("This should be done byt JetIconProvider")
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun hashCode(): Int {
|
override fun hashCode() = hashCode
|
||||||
return hashCode
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun computeHashCode(): Int {
|
private fun computeHashCode(): Int {
|
||||||
var result = getManager().hashCode()
|
var result = getManager().hashCode()
|
||||||
@@ -291,13 +200,13 @@ public class KotlinLightClassForPackage private(manager: PsiManager, private val
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun equals(obj: Any?): Boolean {
|
override fun equals(other: Any?): Boolean {
|
||||||
if (this == obj) return true
|
if (other == null || javaClass != other.javaClass) {
|
||||||
if (obj == null || javaClass != obj.javaClass) {
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
val lightClass = obj as KotlinLightClassForPackage
|
val lightClass = other as KotlinLightClassForPackage
|
||||||
|
if (this === other) return true
|
||||||
|
|
||||||
if (this.hashCode != lightClass.hashCode) return false
|
if (this.hashCode != lightClass.hashCode) return false
|
||||||
if (getManager() != lightClass.getManager()) return false
|
if (getManager() != lightClass.getManager()) return false
|
||||||
@@ -314,24 +223,20 @@ public class KotlinLightClassForPackage private(manager: PsiManager, private val
|
|||||||
catch (e: Throwable) {
|
catch (e: Throwable) {
|
||||||
return javaClass<KotlinLightClassForPackage>().getSimpleName() + ":" + e.toString()
|
return javaClass<KotlinLightClassForPackage>().getSimpleName() + ":" + e.toString()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class object {
|
class object {
|
||||||
|
public fun create(
|
||||||
public fun create(manager: PsiManager, qualifiedName: FqName, searchScope: GlobalSearchScope, files: Collection<JetFile> // this is redundant, but computing it multiple times is costly
|
manager: PsiManager,
|
||||||
|
qualifiedName: FqName,
|
||||||
|
searchScope: GlobalSearchScope,
|
||||||
|
files: Collection<JetFile> // this is redundant, but computing it multiple times is costly
|
||||||
): KotlinLightClassForPackage? {
|
): KotlinLightClassForPackage? {
|
||||||
for (file in files) {
|
if (files.any { LightClassUtil.belongsToKotlinBuiltIns(it) }) {
|
||||||
if (LightClassUtil.belongsToKotlinBuiltIns(file)) return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
return KotlinLightClassForPackage(manager, qualifiedName, searchScope, files)
|
return KotlinLightClassForPackage(manager, qualifiedName, searchScope, files)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun allValid(files: Collection<JetFile>): Boolean {
|
|
||||||
for (file in files) {
|
|
||||||
if (!file.isValid()) return false
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}//NOTE: this is only needed to compute plugin module info
|
}
|
||||||
|
|||||||
+1
-1
@@ -282,7 +282,7 @@ public class IDELightClassGenerationSupport extends LightClassGenerationSupport
|
|||||||
IdeaModuleInfo moduleInfo = info.getModuleInfo();
|
IdeaModuleInfo moduleInfo = info.getModuleInfo();
|
||||||
if (moduleInfo instanceof ModuleSourceInfo) {
|
if (moduleInfo instanceof ModuleSourceInfo) {
|
||||||
KotlinLightClassForPackage lightClass =
|
KotlinLightClassForPackage lightClass =
|
||||||
KotlinLightClassForPackage.create(psiManager, packageFqName, moduleInfo.contentScope(), files);
|
KotlinLightClassForPackage.OBJECT$.create(psiManager, packageFqName, moduleInfo.contentScope(), files);
|
||||||
if (lightClass == null) continue;
|
if (lightClass == null) continue;
|
||||||
|
|
||||||
result.add(lightClass);
|
result.add(lightClass);
|
||||||
|
|||||||
@@ -113,7 +113,7 @@ private fun KotlinLightElement<*, *>.getModuleInfoForLightElement(): IdeaModuleI
|
|||||||
}
|
}
|
||||||
val element = getOrigin() ?: when (this) {
|
val element = getOrigin() ?: when (this) {
|
||||||
is FakeLightClassForFileOfPackage -> this.getContainingFile()!!
|
is FakeLightClassForFileOfPackage -> this.getContainingFile()!!
|
||||||
is KotlinLightClassForPackage -> this.getFiles().first()
|
is KotlinLightClassForPackage -> this.files.first()
|
||||||
else -> throw IllegalStateException("Unknown light class without origin is referenced by IDE lazy resolve: $javaClass")
|
else -> throw IllegalStateException("Unknown light class without origin is referenced by IDE lazy resolve: $javaClass")
|
||||||
}
|
}
|
||||||
return element.getModuleInfo()
|
return element.getModuleInfo()
|
||||||
|
|||||||
Reference in New Issue
Block a user