private void createNewResourceValue(ResourceType resourceType) { CreateXmlResourceDialog dialog = new CreateXmlResourceDialog(myModule, resourceType, null, null, true); dialog.setTitle( "New " + StringUtil.capitalize(resourceType.getDisplayName()) + " Value Resource"); if (!dialog.showAndGet()) { return; } Module moduleToPlaceResource = dialog.getModule(); if (moduleToPlaceResource == null) { return; } String fileName = dialog.getFileName(); List<String> dirNames = dialog.getDirNames(); String resValue = dialog.getValue(); String resName = dialog.getResourceName(); if (!AndroidResourceUtil.createValueResource( moduleToPlaceResource, resName, resourceType, fileName, dirNames, resValue)) { return; } PsiDocumentManager.getInstance(myModule.getProject()).commitAllDocuments(); myResultResourceName = "@" + resourceType.getName() + "/" + resName; close(OK_EXIT_CODE); }
/** * Returns true if this resource repository contains a resource of the given name. * * @param url the resource URL * @return true if the resource is known */ public boolean hasResourceItem(@NonNull String url) { // Handle theme references if (url.startsWith(PREFIX_THEME_REF)) { String remainder = url.substring(PREFIX_THEME_REF.length()); if (url.startsWith(ATTR_REF_PREFIX)) { url = PREFIX_RESOURCE_REF + url.substring(PREFIX_THEME_REF.length()); return hasResourceItem(url); } int colon = url.indexOf(':'); if (colon != -1) { // Convert from ?android:progressBarStyleBig to ?android:attr/progressBarStyleBig if (remainder.indexOf('/', colon) == -1) { remainder = remainder.substring(0, colon) + RESOURCE_CLZ_ATTR + '/' + remainder.substring(colon); } url = PREFIX_RESOURCE_REF + remainder; return hasResourceItem(url); } else { int slash = url.indexOf('/'); if (slash == -1) { url = PREFIX_RESOURCE_REF + RESOURCE_CLZ_ATTR + '/' + remainder; return hasResourceItem(url); } } } if (!url.startsWith(PREFIX_RESOURCE_REF)) { return false; } assert url.startsWith("@") || url.startsWith("?") : url; ensureInitialized(); int typeEnd = url.indexOf('/', 1); if (typeEnd != -1) { int nameBegin = typeEnd + 1; // Skip @ and @+ int typeBegin = url.startsWith("@+") ? 2 : 1; // $NON-NLS-1$ int colon = url.lastIndexOf(':', typeEnd); if (colon != -1) { typeBegin = colon + 1; } String typeName = url.substring(typeBegin, typeEnd); ResourceType type = ResourceType.getEnum(typeName); if (type != null) { String name = url.substring(nameBegin); return hasResourceItem(type, name); } } return false; }
@Nullable public static ValidationInfo checkIfResourceAlreadyExists( @NotNull Module selectedModule, @NotNull String resourceName, @NotNull ResourceType resourceType, @NotNull List<String> dirNames, @NotNull String fileName) { if (resourceName.length() == 0 || dirNames.size() == 0 || fileName.length() == 0) { return null; } final AndroidFacet facet = AndroidFacet.getInstance(selectedModule); final VirtualFile resourceDir = facet != null ? AndroidRootUtil.getResourceDir(facet) : null; if (resourceDir == null) { return null; } for (String directoryName : dirNames) { final VirtualFile resourceSubdir = resourceDir.findChild(directoryName); if (resourceSubdir == null) { continue; } final VirtualFile resFile = resourceSubdir.findChild(fileName); if (resFile == null) { continue; } if (resFile.getFileType() != StdFileTypes.XML) { return new ValidationInfo( "File " + FileUtil.toSystemDependentName(resFile.getPath()) + " is not XML file"); } final Resources resources = AndroidUtils.loadDomElement(selectedModule, resFile, Resources.class); if (resources == null) { return new ValidationInfo( AndroidBundle.message( "not.resource.file.error", FileUtil.toSystemDependentName(resFile.getPath()))); } for (ResourceElement element : AndroidResourceUtil.getValueResourcesFromElement(resourceType.getName(), resources)) { if (resourceName.equals(element.getName().getValue())) { return new ValidationInfo( "resource '" + resourceName + "' already exists in " + FileUtil.toSystemDependentName(resFile.getPath())); } } } return null; }
/** * Returns a map from name to resource types for all resources known to this library. This is * used to make sure that when the {@link #isPrivate(ResourceType, String)} query method is * called, it can tell the difference between a resource implicitly private by not being * declared as public and a resource unknown to this library (e.g. defined by a different * library or the user's own project resources.) * * @return a map from name to resource type for all resources in this library */ @Nullable private Multimap<String, ResourceType> computeAllMap() { // getSymbolFile() is not defined in AndroidLibrary, only in the subclass LibraryBundle File symbolFile = new File(mLibrary.getPublicResources().getParentFile(), FN_RESOURCE_TEXT); if (!symbolFile.exists()) { return null; } try { List<String> lines = Files.readLines(symbolFile, Charsets.UTF_8); Multimap<String, ResourceType> result = ArrayListMultimap.create(lines.size(), 2); ResourceType previousType = null; String previousTypeString = ""; int lineIndex = 1; final int count = lines.size(); for (; lineIndex <= count; lineIndex++) { String line = lines.get(lineIndex - 1); if (line.startsWith("int ")) { // not int[] definitions for styleables // format is "int <type> <class> <name> <value>" int typeStart = 4; int typeEnd = line.indexOf(' ', typeStart); // Items are sorted by type, so we can avoid looping over types in // ResourceType.getEnum() for each line by sharing type in each section String typeString = line.substring(typeStart, typeEnd); ResourceType type; if (typeString.equals(previousTypeString)) { type = previousType; } else { type = ResourceType.getEnum(typeString); previousTypeString = typeString; previousType = type; } if (type == null) { // some newly introduced type continue; } int nameStart = typeEnd + 1; int nameEnd = line.indexOf(' ', nameStart); String name = line.substring(nameStart, nameEnd); result.put(name, type); } } return result; } catch (IOException ignore) { } return null; }
/** * Returns the resources values matching a given {@link FolderConfiguration} for the current * project. * * @param referenceConfig the configuration that each value must match. * @return a map with guaranteed to contain an entry for each {@link ResourceType} */ @NonNull protected final Map<ResourceType, Map<String, ResourceValue>> doGetConfiguredResources( @NonNull FolderConfiguration referenceConfig) { ensureInitialized(); Map<ResourceType, Map<String, ResourceValue>> map = new EnumMap<ResourceType, Map<String, ResourceValue>>(ResourceType.class); for (ResourceType key : ResourceType.values()) { // get the local results and put them in the map map.put(key, getConfiguredResource(key, referenceConfig)); } return map; }
private void createNewResourceFile(ResourceType resourceType) { AndroidFacet facet = AndroidFacet.getInstance(myModule); XmlFile newFile = CreateResourceFileAction.createFileResource( facet, resourceType, null, null, null, true, null); if (newFile != null) { String name = newFile.getName(); int index = name.lastIndexOf('.'); if (index != -1) { name = name.substring(0, index); } myResultResourceName = "@" + resourceType.getName() + "/" + name; close(OK_EXIT_CODE); } }
public ResourceGroup(ResourceType type, ResourceManager manager) { myType = type; final String resourceType = type.getName(); Collection<String> resourceNames = manager.getValueResourceNames(resourceType); for (String resourceName : resourceNames) { myItems.add(new ResourceItem(this, resourceName, null, RESOURCE_ITEM_ICON)); } final Set<String> fileNames = new HashSet<String>(); manager.processFileResources( resourceType, new FileResourceProcessor() { @Override public boolean process( @NotNull VirtualFile resFile, @NotNull String resName, @NotNull String resFolderType) { if (fileNames.add(resName)) { myItems.add( new ResourceItem( ResourceGroup.this, resName, resFile, resFile.getFileType().getIcon())); } return true; } }); if (type == ResourceType.ID) { for (String id : manager.getIds(true)) { if (!resourceNames.contains(id)) { myItems.add(new ResourceItem(this, id, null, RESOURCE_ITEM_ICON)); } } } Collections.sort( myItems, new Comparator<ResourceItem>() { @Override public int compare(ResourceItem resource1, ResourceItem resource2) { return resource1.toString().compareTo(resource2.toString()); } }); }
/** * Returns a map from name to applicable resource types where the presence of the type+name * combination means that the corresponding resource is explicitly public. * * <p>If the result is null, there is no {@code public.txt} definition for this library, so all * resources should be taken to be public. * * @return a map from name to resource type for public resources in this library */ @Nullable private Multimap<String, ResourceType> computeVisibilityMap() { File publicResources = mLibrary.getPublicResources(); if (!publicResources.exists()) { return null; } try { List<String> lines = Files.readLines(publicResources, Charsets.UTF_8); Multimap<String, ResourceType> result = ArrayListMultimap.create(lines.size(), 2); for (String line : lines) { // These files are written by code in MergedResourceWriter#postWriteAction // Format for each line: <type><space><name>\n // Therefore, we don't expect/allow variations in the format (we don't // worry about extra spaces needing to be trimmed etc) int index = line.indexOf(' '); if (index == -1 || line.isEmpty()) { continue; } String typeString = line.substring(0, index); ResourceType type = ResourceType.getEnum(typeString); if (type == null) { // This could in theory happen if in the future a new ResourceType is // introduced, and a newer version of the Gradle build system writes the // name of this type into the public.txt file, and an older version of // the IDE then attempts to read it. Just skip these symbols. continue; } String name = line.substring(index + 1); result.put(name, type); } return result; } catch (IOException ignore) { } return null; }
/** * Reads the public.xml file in data/res/values/ for a given resource folder and builds up a map * of public resources. * * <p>This map is a subset of the full resource map that only contains framework resources that * are public. * * @param logger a logger to report issues to */ public void loadPublicResources(@Nullable ILogger logger) { IAbstractFolder valueFolder = getResFolder().getFolder(SdkConstants.FD_RES_VALUES); if (valueFolder.exists() == false) { return; } IAbstractFile publicXmlFile = valueFolder.getFile("public.xml"); // $NON-NLS-1$ if (publicXmlFile.exists()) { Reader reader = null; try { reader = new BufferedReader(new InputStreamReader(publicXmlFile.getContents(), Charsets.UTF_8)); KXmlParser parser = new KXmlParser(); parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, false); parser.setInput(reader); ResourceType lastType = null; String lastTypeName = ""; while (true) { int event = parser.next(); if (event == XmlPullParser.START_TAG) { // As of API 15 there are a number of "java-symbol" entries here if (!parser.getName().equals("public")) { // $NON-NLS-1$ continue; } String name = null; String typeName = null; for (int i = 0, n = parser.getAttributeCount(); i < n; i++) { String attribute = parser.getAttributeName(i); if (attribute.equals("name")) { // $NON-NLS-1$ name = parser.getAttributeValue(i); if (typeName != null) { // Skip id attribute processing break; } } else if (attribute.equals("type")) { // $NON-NLS-1$ typeName = parser.getAttributeValue(i); } } if (name != null && typeName != null) { ResourceType type = null; if (typeName.equals(lastTypeName)) { type = lastType; } else { type = ResourceType.getEnum(typeName); lastType = type; lastTypeName = typeName; } if (type != null) { ResourceItem match = null; Map<String, ResourceItem> map = mResourceMap.get(type); if (map != null) { match = map.get(name); } if (match != null) { List<ResourceItem> publicList = mPublicResourceMap.get(type); if (publicList == null) { // Pick initial size for the list to hold the public // resources. We could just use map.size() here, // but they're usually much bigger; for example, // in one platform version, there are 1500 drawables // and 1200 strings but only 175 and 25 public ones // respectively. int size; switch (type) { case STYLE: size = 500; break; case ATTR: size = 1000; break; case DRAWABLE: size = 200; break; case ID: size = 50; break; case LAYOUT: case COLOR: case STRING: case ANIM: case INTERPOLATOR: size = 30; break; default: size = 10; break; } publicList = new ArrayList<ResourceItem>(size); mPublicResourceMap.put(type, publicList); } publicList.add(match); } else { // log that there's a public resource that doesn't actually // exist? } } else { // log that there was a reference to a typo that doesn't actually // exist? } } } else if (event == XmlPullParser.END_DOCUMENT) { break; } } } catch (Exception e) { if (logger != null) { logger.error(e, "Can't read and parse public attribute list"); } } finally { if (reader != null) { try { reader.close(); } catch (IOException e) { // Nothing to be done here - we don't care if it closed or not. } } } } // put unmodifiable list for all res type in the public resource map // this will simplify access for (ResourceType type : ResourceType.values()) { List<ResourceItem> list = mPublicResourceMap.get(type); if (list == null) { list = Collections.emptyList(); } else { list = Collections.unmodifiableList(list); } // put the new list in the map mPublicResourceMap.put(type, list); } }
@Override public String toString() { return myType.getDisplayName(); }
public String getName() { return myType.getName(); }
public static void collectAllResources( @NotNull final AndroidFacet facet, final Set<ResourceEntry> resourceSet) { final LocalResourceManager manager = facet.getLocalResourceManager(); for (final String resType : ResourceType.getNames()) { for (final ResourceElement element : manager.getValueResources(resType)) { ApplicationManager.getApplication() .runReadAction( new Runnable() { @Override public void run() { if (!element.isValid() || facet.getModule().isDisposed() || facet.getModule().getProject().isDisposed()) { return; } final String name = element.getName().getValue(); if (name != null) { resourceSet.add(new ResourceEntry(resType, name)); } } }); } } for (final Resources resources : manager.getResourceElements()) { ApplicationManager.getApplication() .runReadAction( new Runnable() { @Override public void run() { if (!resources.isValid() || facet.getModule().isDisposed() || facet.getModule().getProject().isDisposed()) { return; } for (final Attr attr : resources.getAttrs()) { final String name = attr.getName().getValue(); if (name != null) { resourceSet.add(new ResourceEntry(ResourceType.ATTR.getName(), name)); } } for (final DeclareStyleable styleable : resources.getDeclareStyleables()) { final String name = styleable.getName().getValue(); if (name != null) { resourceSet.add( new ResourceEntry(ResourceType.DECLARE_STYLEABLE.getName(), name)); } } } }); } ApplicationManager.getApplication() .runReadAction( new Runnable() { @Override public void run() { if (facet.getModule().isDisposed() || facet.getModule().getProject().isDisposed()) { return; } for (String id : manager.getIds()) { resourceSet.add(new ResourceEntry(ResourceType.ID.getName(), id)); } } }); final HashSet<VirtualFile> visited = new HashSet<VirtualFile>(); for (VirtualFile subdir : manager.getResourceSubdirs(null)) { final HashSet<VirtualFile> resourceFiles = new HashSet<VirtualFile>(); AndroidUtils.collectFiles(subdir, visited, resourceFiles); for (VirtualFile file : resourceFiles) { resourceSet.add(new ResourceEntry(subdir.getName(), file.getName())); } } }