package org.jetbrains.android.compiler; import com.android.resources.ResourceType; import com.android.sdklib.IAndroidTarget; import com.intellij.openapi.compiler.ValidityState; import com.intellij.openapi.module.Module; import com.intellij.openapi.util.Pair; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.util.ArrayUtil; import com.intellij.util.containers.HashMap; import org.jetbrains.android.dom.resources.DeclareStyleable; import org.jetbrains.android.dom.resources.ResourceElement; import org.jetbrains.android.dom.resources.Resources; import org.jetbrains.android.facet.AndroidFacet; import org.jetbrains.android.facet.AndroidRootUtil; import org.jetbrains.android.resourceManagers.FileResourceProcessor; import org.jetbrains.android.resourceManagers.LocalResourceManager; import org.jetbrains.android.sdk.AndroidPlatform; import org.jetbrains.android.util.AndroidCommonUtils; import org.jetbrains.android.util.AndroidResourceUtil; import org.jetbrains.android.util.ResourceEntry; import org.jetbrains.android.util.ResourceFileData; import org.jetbrains.annotations.NotNull; import java.io.DataInput; import java.io.DataOutput; import java.io.IOException; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Map; /** * @author Eugene.Kudelevsky */ public class ResourceNamesValidityState implements ValidityState { private final Map<String, ResourceFileData> myResources = new HashMap<String, ResourceFileData>(); private final String myAndroidTargetHashString; private final long myManifestTimestamp; public ResourceNamesValidityState(@NotNull Module module) { final AndroidFacet facet = AndroidFacet.getInstance(module); assert facet != null; final AndroidPlatform platform = facet.getConfiguration().getAndroidPlatform(); final IAndroidTarget target = platform != null ? platform.getTarget() : null; myAndroidTargetHashString = target != null ? target.hashString() : ""; final VirtualFile manifestFile = AndroidRootUtil.getManifestFile(facet); myManifestTimestamp = manifestFile != null ? manifestFile.getTimeStamp() : -1; final LocalResourceManager manager = facet.getLocalResourceManager(); for (final Pair<Resources, VirtualFile> pair : manager.getResourceElements()) { final Resources resources = pair.getFirst(); final VirtualFile file = pair.getSecond(); for (final ResourceType resType : AndroidResourceUtil.VALUE_RESOURCE_TYPES) { addValueResources(file, resType, AndroidResourceUtil.getValueResourcesFromElement(resType.getName(), resources), myResources, ""); } addValueResources(file, ResourceType.ATTR, resources.getAttrs(), myResources, ""); final List<DeclareStyleable> styleables = resources.getDeclareStyleables(); addValueResources(file, ResourceType.DECLARE_STYLEABLE, styleables, myResources, ""); for (DeclareStyleable styleable : styleables) { final String styleableName = styleable.getName().getValue(); if (styleableName != null) { addValueResources(file, ResourceType.DECLARE_STYLEABLE, styleable.getAttrs(), myResources, styleableName); } } } manager.processFileResources(null, new FileResourceProcessor() { @Override public boolean process(@NotNull VirtualFile resFile, @NotNull String resName, @NotNull String resFolderType) { final ResourceType type = ResourceType.getEnum(resFolderType); final boolean idProvidingResource = type != null && ArrayUtil.find(AndroidCommonUtils.ID_PROVIDING_RESOURCE_TYPES, type) >= 0; ResourceFileData data = myResources.get(resFile.getPath()); if (data == null) { data = new ResourceFileData(); myResources.put(resFile.getPath(), data); } if (idProvidingResource) { data.setTimestamp(resFile.getTimeStamp()); } return true; } }); } private static void addValueResources(VirtualFile file, ResourceType resType, Collection<? extends ResourceElement> resourceElements, Map<String, ResourceFileData> result, String context) { for (ResourceElement element : resourceElements) { final String name = element.getName().getValue(); if (name != null) { ResourceFileData data = result.get(file.getPath()); if (data == null) { data = new ResourceFileData(); result.put(file.getPath(), data); } data.addValueResource(new ResourceEntry(resType.getName(), name, context)); } } } public ResourceNamesValidityState(@NotNull DataInput in) throws IOException { myAndroidTargetHashString = in.readUTF(); myManifestTimestamp = in.readLong(); final int resourcesCount = in.readInt(); for (int i = 0; i < resourcesCount; i++) { final String filePath = in.readUTF(); final int valueResourcesCount = in.readInt(); final List<ResourceEntry> valueResources = new ArrayList<ResourceEntry>(valueResourcesCount); for (int j = 0; j < valueResourcesCount; j++) { final String resType = in.readUTF(); final String resName = in.readUTF(); final String resContext = in.readUTF(); valueResources.add(new ResourceEntry(resType, resName, resContext)); } final long fileTimestamp = in.readLong(); myResources.put(filePath, new ResourceFileData(valueResources, fileTimestamp)); } } @Override public boolean equalsTo(ValidityState otherState) { if (!(otherState instanceof ResourceNamesValidityState)) { return false; } final ResourceNamesValidityState other = (ResourceNamesValidityState)otherState; return other.myAndroidTargetHashString.equals(myAndroidTargetHashString) && other.myManifestTimestamp == myManifestTimestamp && other.myResources.equals(myResources); } @Override public void save(DataOutput out) throws IOException { out.writeUTF(myAndroidTargetHashString); out.writeLong(myManifestTimestamp); out.writeInt(myResources.size()); for (Map.Entry<String, ResourceFileData> entry : myResources.entrySet()) { out.writeUTF(entry.getKey()); final ResourceFileData data = entry.getValue(); final List<ResourceEntry> valueResources = data.getValueResources(); out.writeInt(valueResources.size()); for (ResourceEntry resource : valueResources) { out.writeUTF(resource.getType()); out.writeUTF(resource.getName()); out.writeUTF(resource.getContext()); } out.writeLong(data.getTimestamp()); } } }