in android/src/com/android/tools/idea/res/ResourceFolderRepository.java [1119:1296]
public void childAdded(@NotNull PsiTreeChangeEvent event) {
ResourceUpdateTracer.log(() -> getSimpleId(this) + ".childAdded " + pathForLogging(event.getFile()));
try {
PsiFile psiFile = event.getFile();
if (psiFile != null && isRelevantFile(psiFile)) {
VirtualFile virtualFile = psiFile.getVirtualFile();
// If the file is currently being scanned, schedule a new scan to avoid a race condition
// between the incremental update and the running scan.
if (rescheduleScanIfRunning(virtualFile)) {
return;
}
// Some child was added within a file.
ResourceFolderType folderType = IdeResourcesUtil.getFolderType(psiFile);
if (folderType != null && isResourceFile(psiFile)) {
PsiElement child = event.getChild();
PsiElement parent = event.getParent();
if (folderType == VALUES) {
if (child instanceof XmlTag) {
XmlTag tag = (XmlTag)child;
if (convertToPsiIfNeeded(psiFile, folderType)) {
return;
}
scheduleUpdate(() -> {
if (!tag.isValid()) {
scan(psiFile, folderType);
return;
}
if (isItemElement(tag)) {
ResourceItemSource<?> source = mySources.get(virtualFile);
if (source != null) {
assert source instanceof PsiResourceFile;
PsiResourceFile psiResourceFile = (PsiResourceFile)source;
String name = tag.getAttributeValue(ATTR_NAME);
if (isValidValueResourceName(name)) {
ResourceType type = getResourceTypeForResourceTag(tag);
if (type == ResourceType.STYLEABLE) {
// Can't handle declare styleable additions incrementally yet; need to update paired attr items.
scan(psiFile, folderType);
return;
}
if (type != null) {
PsiResourceItem item = PsiResourceItem.forXmlTag(name, type, ResourceFolderRepository.this, tag);
synchronized (ITEM_MAP_LOCK) {
getOrCreateMap(type).put(name, item);
psiResourceFile.addItem(item);
setModificationCount(ourModificationCounter.incrementAndGet());
invalidateParentCaches(ResourceFolderRepository.this, type);
}
return;
}
}
}
}
// See if you just added a new item inside a <style> or <array> or <declare-styleable> etc.
XmlTag parentTag = tag.getParentTag();
if (parentTag != null && getResourceTypeForResourceTag(parentTag) != null) {
// Yes just invalidate the corresponding cached value.
ResourceItem parentItem = findValueResourceItem(parentTag, psiFile);
if (parentItem instanceof PsiResourceItem) {
if (((PsiResourceItem)parentItem).recomputeValue()) {
setModificationCount(ourModificationCounter.incrementAndGet());
}
ResourceUpdateTracer.log(() -> getSimpleId(this) + ".childAdded " + pathForLogging(event.getFile()) +
" recomputed: " + parentItem);
return;
}
}
// Else: fall through and do full file rescan.
scan(psiFile, folderType);
});
}
else if (parent instanceof XmlText) {
// If the edit is within an item tag.
XmlText text = (XmlText)parent;
handleValueXmlTextEdit(text.getParentTag(), psiFile);
}
else if (child instanceof XmlText) {
// If the edit is within an item tag.
handleValueXmlTextEdit(parent, psiFile);
}
else if (!(parent instanceof XmlComment) && !(child instanceof XmlComment)) {
scheduleScan(virtualFile, folderType);
}
// Can ignore comment edits or new comments.
return;
}
else if (FolderTypeRelationship.isIdGeneratingFolderType(folderType) && psiFile.getFileType() == XmlFileType.INSTANCE) {
if (parent instanceof XmlComment || child instanceof XmlComment) {
return;
}
if (parent instanceof XmlText || (child instanceof XmlText && child.getText().trim().isEmpty())) {
return;
}
if (parent instanceof XmlElement && child instanceof XmlElement) {
if (child instanceof XmlTag) {
scheduleUpdate(() -> {
if (!child.isValid()) {
scan(psiFile, folderType);
return;
}
Map<ResourceType, ListMultimap<String, ResourceItem>> result = new HashMap<>();
List<PsiResourceItem> items = new ArrayList<>();
addIds(child, items, result);
if (!items.isEmpty()) {
ResourceItemSource<?> resourceFile = mySources.get(psiFile.getVirtualFile());
if (!(resourceFile instanceof PsiResourceFile)) {
scan(psiFile, folderType);
return;
}
PsiResourceFile psiResourceFile = (PsiResourceFile)resourceFile;
for (PsiResourceItem item : items) {
psiResourceFile.addItem(item);
}
commitToRepository(result);
setModificationCount(ourModificationCounter.incrementAndGet());
invalidateParentCaches(ResourceFolderRepository.this, ResourceType.ID);
}
});
return;
}
if (child instanceof XmlAttribute || parent instanceof XmlAttribute) {
// We check both because invalidation might come from XmlAttribute if it is inserted at once.
XmlAttribute attribute = parent instanceof XmlAttribute ? (XmlAttribute)parent : (XmlAttribute)child;
String id = createIdNameFromAttribute(attribute);
if (id != null) {
if (convertToPsiIfNeeded(psiFile, folderType)) {
return;
}
scheduleUpdate(() -> {
if (!attribute.isValid()) {
scan(psiFile, folderType);
return;
}
PsiResourceItem newIdResource =
PsiResourceItem.forXmlTag(id, ResourceType.ID, ResourceFolderRepository.this, attribute.getParent());
synchronized (ITEM_MAP_LOCK) {
ResourceItemSource<?> resourceFile = mySources.get(psiFile.getVirtualFile());
if (resourceFile != null) {
assert resourceFile instanceof PsiResourceFile;
PsiResourceFile psiResourceFile = (PsiResourceFile)resourceFile;
psiResourceFile.addItem(newIdResource);
ResourceUpdateTracer.log(() -> getSimpleId(this) + ": Adding id/" + newIdResource.getName());
getOrCreateMap(ResourceType.ID).put(newIdResource.getName(), newIdResource);
setModificationCount(ourModificationCounter.incrementAndGet());
invalidateParentCaches(ResourceFolderRepository.this, ResourceType.ID);
}
}
});
return;
}
}
}
}
else if (folderType == FONT) {
clearFontCache(psiFile.getVirtualFile());
}
}
}
myIgnoreChildrenChanged = true;
}
finally {
ResourceUpdateTracer.log(() -> getSimpleId(this) + ".childAdded " + pathForLogging(event.getFile()) + " end");
}
}