/** * Returns the {@link ResourceFile} matching the given name, {@link ResourceType} and * configuration. * * <p>This only works with files generating one resource named after the file (for instance, * layouts, bitmap based drawable, xml, anims). * * @param name the resource name or file name * @param type the folder type search for * @param config the folder configuration to match for * @return the matching file or <code>null</code> if no match was found. */ @Nullable public ResourceFile getMatchingFile( @NonNull String name, @NonNull ResourceType type, @NonNull FolderConfiguration config) { ensureInitialized(); String resourceName = name; int dot = resourceName.indexOf('.'); if (dot != -1) { resourceName = resourceName.substring(0, dot); } Map<String, ResourceItem> items = mResourceMap.get(type); if (items != null) { ResourceItem item = items.get(resourceName); if (item != null) { List<ResourceFile> files = item.getSourceFileList(); if (files != null) { if (files.size() > 1) { ResourceValue value = item.getResourceValue(type, config, isFrameworkRepository()); if (value != null) { String v = value.getValue(); if (v != null) { ResourceUrl url = ResourceUrl.parse(v); if (url != null) { return getMatchingFile(url.name, url.type, config); } else { // Looks like the resource value is pointing to a file // It's most likely one of the source files for this // resource item, so check those first for (ResourceFile f : files) { if (v.equals(f.getFile().getOsLocation())) { // Found the file return f; } } // No; look up the resource file from the full path File file = new File(v); if (file.exists()) { ResourceFile f = findResourceFile(file); if (f != null) { return f; } } } } } } else if (files.size() == 1) { // Single file: see if it matches ResourceFile matchingFile = files.get(0); if (matchingFile.getFolder().getConfiguration().isMatchFor(config)) { return matchingFile; } } } } } return null; }
@Override public void removeItem(@NonNull ResourceItem removedItem, @Nullable ResourceItem replacedBy) throws ConsumerException { ResourceFile.FileType removedType = removedItem.getSource().getType(); ResourceFile.FileType replacedType = replacedBy != null ? replacedBy.getSource().getType() : null; if (removedType == replacedType) { // if the type is multi, then we make sure to flag the qualifier as deleted. if (removedType == ResourceFile.FileType.MULTI) { mQualifierWithDeletedValues.add(removedItem.getSource().getQualifiers()); } else { // both are single type resources, so we actually don't delete the previous // file as the new one will replace it instead. } } else if (removedType == ResourceFile.FileType.SINGLE) { // removed type is single. // The case of both single type is above, so here either, there is no replacement // or the replacement is multi. We always need to remove the old file. // if replacedType is non-null, then it was values, if not, removeOutFile(removedItem.getSource()); } else { // removed type is multi. // whether the new type is single or doesn't exist, we always need to mark the qualifier // for rewrite. mQualifierWithDeletedValues.add(removedItem.getSource().getQualifiers()); } }
/** * Returns the list of source files for a given resource. Optionally, if a {@link * FolderConfiguration} is given, then only the best match for this config is returned. * * @param type the type of the resource. * @param name the name of the resource. * @param referenceConfig an optional config for which only the best match will be returned. * @return a list of files generating this resource or null if it was not found. */ @Nullable public List<ResourceFile> getSourceFiles( @NonNull ResourceType type, @NonNull String name, @Nullable FolderConfiguration referenceConfig) { ensureInitialized(); Collection<ResourceItem> items = getResourceItemsOfType(type); for (ResourceItem item : items) { if (name.equals(item.getName())) { if (referenceConfig != null) { Configurable match = referenceConfig.findMatchingConfigurable(item.getSourceFileList()); if (match instanceof ResourceFile) { return Collections.singletonList((ResourceFile) match); } return null; } return item.getSourceFileList(); } } return null; }
@Override public void valueChanged(@Nullable TreeSelectionEvent e) { Component selectedComponent = myContentPanel.getSelectedComponent(); if (selectedComponent == myColorPickerPanel) { Color color = myColorPicker.getColor(); myNewResourceAction.setEnabled(false); myResultResourceName = ResourceHelper.colorToString(color); updateResourceNameStatus(); } else { boolean isProjectPanel = selectedComponent == myProjectPanel.myComponent; ResourcePanel panel = isProjectPanel ? myProjectPanel : mySystemPanel; ResourceItem element = getSelectedElement(panel.myTreeBuilder, ResourceItem.class); setOKActionEnabled(element != null); myNewResourceAction.setEnabled( isProjectPanel && !panel.myTreeBuilder.getSelectedElements().isEmpty()); if (element == null) { myResultResourceName = null; } else { String prefix = panel == myProjectPanel ? "@" : ANDROID; myResultResourceName = prefix + element.getName(); } panel.showPreview(element); } notifyResourcePickerListeners(myResultResourceName); }
@Override public Object getParentElement(Object element) { if (element instanceof ResourceItem) { ResourceItem resource = (ResourceItem) element; return resource.getGroup(); } return null; }
private void select(String type, String name) { for (ResourceGroup group : myGroups) { if (type.equalsIgnoreCase(group.getName())) { for (ResourceItem item : group.getItems()) { if (name.equals(item.toString())) { myTreeBuilder.select(item); return; } } return; } } }
/** * Looks up an existing {@link ResourceItem} by {@link ResourceType} and name. This ignores inline * resources. * * @param type the Resource Type. * @param name the Resource name. * @return the existing ResourceItem or null if no match was found. */ @Nullable private ResourceItem findDeclaredResourceItem(@NonNull ResourceType type, @NonNull String name) { Map<String, ResourceItem> map = mResourceMap.get(type); if (map != null) { ResourceItem resourceItem = map.get(name); if (resourceItem != null && !resourceItem.isDeclaredInline()) { return resourceItem; } } return null; }
@Override public void addItem(@NonNull final ResourceItem item) throws ConsumerException { ResourceFile.FileType type = item.getSource().getType(); if (type == ResourceFile.FileType.MULTI) { // this is a resource for the values files // just add the node to write to the map based on the qualifier. // We'll figure out later if the files needs to be written or (not) mValuesResMap.put(item.getSource().getQualifiers(), item); } else { // This is a single value file. // Only write it if the state is TOUCHED. if (item.isTouched()) { getExecutor() .execute( new Callable<Void>() { @Override public Void call() throws Exception { ResourceFile resourceFile = item.getSource(); File file = resourceFile.getFile(); String filename = file.getName(); String folderName = item.getType().getName(); String qualifiers = resourceFile.getQualifiers(); if (!qualifiers.isEmpty()) { folderName = folderName + RES_QUALIFIER_SEP + qualifiers; } File typeFolder = new File(getRootFolder(), folderName); createDir(typeFolder); File outFile = new File(typeFolder, filename); if (mAaptRunner != null && filename.endsWith(DOT_PNG)) { // run aapt in single crunch mode on the original file to write the // destination file. mAaptRunner.crunchPng(file, outFile); } else if (filename.endsWith(DOT_XML)) { copyXmlWithComment(file, outFile, createPathComment(file)); } else { Files.copy(file, outFile); } return null; } }); } } }
/** Cleans up the repository of resource items that have no source file anymore. */ public void postUpdateCleanUp() { // Since removed files/folders remove source files from existing ResourceItem, loop through // all resource items and remove the ones that have no source files. Collection<Map<String, ResourceItem>> maps = mResourceMap.values(); for (Map<String, ResourceItem> map : maps) { Set<String> keySet = map.keySet(); Iterator<String> iterator = keySet.iterator(); while (iterator.hasNext()) { String name = iterator.next(); ResourceItem resourceItem = map.get(name); if (resourceItem.hasNoSourceFile()) { iterator.remove(); } } } }
private void showComboPreview(ResourceItem element) { java.util.List<ResourceElement> resources = element.getPreviewResources(); String selection = (String) myComboBox.getSelectedItem(); if (selection == null) { selection = element.getPreviewComboDefaultSelection(); } int index = element.getPreviewComboModel().getIndexOf(selection); if (index == -1) { index = 0; } myComboBox.setModel(element.getPreviewComboModel()); myComboBox.putClientProperty(COMBO, resources); myComboBox.setSelectedIndex(index); myComboTextArea.setText(getResourceElementValue(resources.get(index))); CardLayout layout = (CardLayout) myPreviewPanel.getLayout(); layout.show(myPreviewPanel, COMBO); }
private LauncherButton getButtonForResourceItem(ResourceItem resourceItem) { for (Node n : flinger.getContent().getChildren()) { if (n instanceof LauncherButton) { final LauncherButton launcherButton = (LauncherButton) n; if (resourceItem.equals(launcherButton.getResourceItem())) { return launcherButton; } } } return null; }
protected void removeFile(@NonNull ResourceType type, @NonNull ResourceFile file) { Map<String, ResourceItem> map = mResourceMap.get(type); if (map != null) { Collection<ResourceItem> values = map.values(); List<ResourceItem> toDelete = null; for (ResourceItem item : values) { item.removeFile(file); if (item.hasNoSourceFile()) { if (toDelete == null) { toDelete = new ArrayList<ResourceItem>(values.size()); } toDelete.add(item); } } if (toDelete != null) { for (ResourceItem item : toDelete) { map.remove(item.getName()); } } } }
/** * Returns a map of (resource name, resource value) for the given {@link ResourceType}. * * <p>The values returned are taken from the resource files best matching a given {@link * FolderConfiguration}. * * @param type the type of the resources. * @param referenceConfig the configuration to best match. */ @NonNull private Map<String, ResourceValue> getConfiguredResource( @NonNull ResourceType type, @NonNull FolderConfiguration referenceConfig) { // get the resource item for the given type Map<String, ResourceItem> items = mResourceMap.get(type); if (items == null) { return new HashMap<String, ResourceValue>(); } // create the map HashMap<String, ResourceValue> map = new HashMap<String, ResourceValue>(items.size()); for (ResourceItem item : items.values()) { ResourceValue value = item.getResourceValue(type, referenceConfig, isFrameworkRepository()); if (value != null) { map.put(item.getName(), value); } } return map; }
public void showPreview(@Nullable ResourceItem element) { CardLayout layout = (CardLayout) myPreviewPanel.getLayout(); if (element == null || element.getGroup().getType() == ResourceType.ID) { layout.show(myPreviewPanel, NONE); return; } try { VirtualFile file = element.getFile(); if (file == null) { String value = element.getPreviewString(); if (value == null) { java.util.List<ResourceElement> resources = element.getPreviewResources(); if (resources == null) { long time = System.currentTimeMillis(); resources = myManager.findValueResources( element.getGroup().getType().getName(), element.toString()); if (ApplicationManagerEx.getApplicationEx().isInternal()) { System.out.println("Time: " + (System.currentTimeMillis() - time)); // XXX } int size = resources.size(); if (size == 1) { value = getResourceElementValue(resources.get(0)); element.setPreviewString(value); } else if (size > 1) { resources = new ArrayList<ResourceElement>(resources); Collections.sort( resources, new Comparator<ResourceElement>() { @Override public int compare(ResourceElement element1, ResourceElement element2) { PsiDirectory directory1 = element1.getXmlTag().getContainingFile().getParent(); PsiDirectory directory2 = element2.getXmlTag().getContainingFile().getParent(); if (directory1 == null && directory2 == null) { return 0; } if (directory2 == null) { return 1; } if (directory1 == null) { return -1; } return directory1.getName().compareTo(directory2.getName()); } }); DefaultComboBoxModel model = new DefaultComboBoxModel(); String defaultSelection = null; for (int i = 0; i < size; i++) { ResourceElement resource = resources.get(i); PsiDirectory directory = resource.getXmlTag().getContainingFile().getParent(); String name = directory == null ? "unknown-" + i : directory.getName(); model.addElement(name); if (defaultSelection == null && "values".equalsIgnoreCase(name)) { defaultSelection = name; } } element.setPreviewResources(resources, model, defaultSelection); showComboPreview(element); return; } else { layout.show(myPreviewPanel, NONE); return; } } else { showComboPreview(element); return; } } if (value == null) { layout.show(myPreviewPanel, NONE); return; } myTextArea.setText(value); layout.show(myPreviewPanel, TEXT); } else if (ImageFileTypeManager.getInstance().isImage(file)) { Icon icon = element.getPreviewIcon(); if (icon == null) { icon = new SizedIcon(100, 100, new ImageIcon(file.getPath())); element.setPreviewIcon(icon); } myImageComponent.setIcon(icon); layout.show(myPreviewPanel, IMAGE); } else if (file.getFileType() == XmlFileType.INSTANCE) { String value = element.getPreviewString(); if (value == null) { value = new String(file.contentsToByteArray()); element.setPreviewString(value); } myTextArea.setText(value); myTextArea.setEditable(false); layout.show(myPreviewPanel, TEXT); } else { layout.show(myPreviewPanel, NONE); } } catch (IOException e) { layout.show(myPreviewPanel, NONE); } }
/** * Returns a {@link ResourceItem} matching the given {@link ResourceType} and name. If none exist, * it creates one. * * @param type the resource type * @param name the name of the resource. * @return A resource item matching the type and name. */ @NonNull public ResourceItem getResourceItem(@NonNull ResourceType type, @NonNull String name) { ensureInitialized(); // looking for an existing ResourceItem with this type and name ResourceItem item = findDeclaredResourceItem(type, name); // create one if there isn't one already, or if the existing one is inlined, since // clearly we need a non inlined one (the inline one is removed too) if (item == null || item.isDeclaredInline()) { ResourceItem oldItem = item != null && item.isDeclaredInline() ? item : null; item = createResourceItem(name); Map<String, ResourceItem> map = mResourceMap.get(type); if (map == null) { if (isFrameworkRepository()) { // Pick initial size for the maps. Also change the load factor to 1.0 // to avoid rehashing the whole table when we (as expected) get near // the known rough size of each resource type map. int size; switch (type) { // Based on counts in API 16. Going back to API 10, the counts // are roughly 25-50% smaller (e.g. compared to the top 5 types below // the fractions are 1107 vs 1734, 831 vs 1508, 895 vs 1255, // 733 vs 1064 and 171 vs 783. case PUBLIC: size = 1734; break; case DRAWABLE: size = 1508; break; case STRING: size = 1255; break; case ATTR: size = 1064; break; case STYLE: size = 783; break; case ID: size = 347; break; case DECLARE_STYLEABLE: size = 210; break; case LAYOUT: size = 187; break; case COLOR: size = 120; break; case ANIM: size = 95; break; case DIMEN: size = 81; break; case BOOL: size = 54; break; case INTEGER: size = 52; break; case ARRAY: size = 51; break; case PLURALS: size = 20; break; case XML: size = 14; break; case INTERPOLATOR: size = 13; break; case ANIMATOR: size = 8; break; case RAW: size = 4; break; case MENU: size = 2; break; case MIPMAP: size = 2; break; case FRACTION: size = 1; break; default: size = 2; } map = new HashMap<String, ResourceItem>(size, 1.0f); } else { map = new HashMap<String, ResourceItem>(); } mResourceMap.put(type, map); } map.put(item.getName(), item); if (oldItem != null) { map.remove(oldItem.getName()); } } return item; }
private void rebuildIcons() { if (mode != Mode.LAUNCHERS) { return; } context.clearLoadQueue(); flinger.getContent().getChildren().clear(); // Type lastType = null; for (Map.Entry<ResourceGroupKey, ResourceGroupList> ig : icons.entrySet()) { Type type = ig.getKey().getType(); // if (lastType != null && type != lastType) { // shortcuts // .getChildren() // .add(new Separator( // cfg.topProperty().get() // || cfg.bottomProperty().get() ? Orientation.VERTICAL // : Orientation.HORIZONTAL)); // } switch (type) { case SSO: if (!ssoResources.isSelected()) continue; break; case BROWSER: if (!browserResources.isSelected()) continue; break; case FILE: if (!fileResources.isSelected()) continue; break; case NETWORK: if (!networkResources.isSelected()) continue; break; default: break; } List<ResourceGroupList> groupsAdded = new ArrayList<ResourceGroupList>(); for (ResourceItem item : ig.getValue().getItems()) { ResourceGroupKey gk = new ResourceGroupKey(item.getResource().getType(), item.getResource().getGroup()); ResourceGroupList groups = icons.get(gk); if (groups == null || groups.getItems().size() < 2) { flinger .getContent() .getChildren() .add( new IconButton(resources, item, context, ig.getValue()) { @Override protected void onFinishLaunch() { super.onFinishLaunch(); if (launchWait != null && launchWait.getStatus() == javafx.animation.Animation.Status.RUNNING) launchWait.stop(); launchWait = new Timeline(new KeyFrame(Duration.millis(Dock.LAUNCH_WAIT))); launchWait.play(); } }); } else { if (!groupsAdded.contains(groups)) { flinger .getContent() .getChildren() .add( new GroupButton(resources, context, groups) { @Override protected void onFinishLaunch() { super.onFinishLaunch(); if (launchWait != null && launchWait.getStatus() == javafx.animation.Animation.Status.RUNNING) launchWait.stop(); launchWait = new Timeline(new KeyFrame(Duration.millis(Dock.LAUNCH_WAIT))); launchWait.play(); } }); groupsAdded.add(groups); } } } // lastType = type; } }
@Override protected void postWriteAction() throws ConsumerException { // now write the values files. for (String key : mValuesResMap.keySet()) { // the key is the qualifier. // check if we have to write the file due to deleted values. // also remove it from that list anyway (to detect empty qualifiers later). boolean mustWriteFile = mQualifierWithDeletedValues.remove(key); // get the list of items to write List<ResourceItem> items = mValuesResMap.get(key); // now check if we really have to write it if (!mustWriteFile) { for (ResourceItem item : items) { if (item.isTouched()) { mustWriteFile = true; break; } } } if (mustWriteFile) { String folderName = key.isEmpty() ? ResourceFolderType.VALUES.getName() : ResourceFolderType.VALUES.getName() + RES_QUALIFIER_SEP + key; try { File valuesFolder = new File(getRootFolder(), folderName); createDir(valuesFolder); File outFile = new File(valuesFolder, FN_VALUES_XML); DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); factory.setNamespaceAware(true); factory.setValidating(false); factory.setIgnoringComments(true); DocumentBuilder builder; builder = factory.newDocumentBuilder(); Document document = builder.newDocument(); Node rootNode = document.createElement(TAG_RESOURCES); document.appendChild(rootNode); Collections.sort(items); ResourceFile currentFile = null; for (ResourceItem item : items) { ResourceFile source = item.getSource(); if (source != currentFile) { currentFile = source; rootNode.appendChild(document.createTextNode("\n")); File file = source.getFile(); rootNode.appendChild(document.createComment(createPathComment(file))); rootNode.appendChild(document.createTextNode("\n")); } Node adoptedNode = NodeUtils.adoptNode(document, item.getValue()); rootNode.appendChild(adoptedNode); } String content; try { content = XmlPrettyPrinter.prettyPrint(document, true); } catch (Throwable t) { content = XmlUtils.toXml(document, false); } Files.write(content, outFile, Charsets.UTF_8); } catch (Throwable t) { throw new ConsumerException(t); } } } // now remove empty values files. for (String key : mQualifierWithDeletedValues) { String folderName = key != null && !key.isEmpty() ? ResourceFolderType.VALUES.getName() + RES_QUALIFIER_SEP + key : ResourceFolderType.VALUES.getName(); removeOutFile(folderName, FN_VALUES_XML); } }
@Override public boolean ignoreItemInMerge(ResourceItem item) { return item.getIgnoredFromDiskMerge(); }
@Override public void updateResource(ResourceUpdateType type, Resource resource) { switch (type) { case CREATE: { rebuildResourceIcon(resource.getRealm(), resource); rebuildIcons(); Action[] actions = new Action[0]; // Find the button so we can launch on clicking the notify ResourceGroupKey key = new ResourceGroupKey(resource.getType(), resource.getGroup()); ResourceGroupList list = icons.get(key); if (list != null) { ResourceItem rit = list.getItemForResource(resource); if (rit != null) { LauncherButton lb = getButtonForResourceItem(rit); if (lb != null) { actions = new Action[] { new Action( resources.getString("resources.launch"), new Consumer<ActionEvent>() { @Override public void accept(ActionEvent t) { lb.launch(); } }) }; } } } notify( MessageFormat.format(resources.getString("resources.created"), resource.getName()), GUICallback.NOTIFY_INFO, actions); break; } case DELETE: { ResourceGroupKey key = new ResourceGroupKey(resource.getType(), resource.getIcon()); ResourceGroupList list = icons.get(key); if (list != null) { ResourceItem rit = list.getItemForResource(resource); if (rit != null) { list.getItems().remove(rit); rebuildIcons(); notify( MessageFormat.format( resources.getString("resources.deleted"), resource.getName()), GUICallback.NOTIFY_INFO); break; } } break; } default: { ResourceGroupKey key = new ResourceGroupKey(resource.getType(), resource.getGroup()); ResourceGroupList list = icons.get(key); if (list != null) { ResourceItem rit = list.getItemForResource(resource); if (rit != null) { rit.setResource(resource); rebuildIcons(); notify( MessageFormat.format( resources.getString("resources.updated"), resource.getName()), GUICallback.NOTIFY_INFO); break; } else { log.warn( String.format( "Could not find icon in icon group for resource %s (%s)", resource.getUid(), resource.getName())); } } else { log.warn( String.format( "Could not find icon group for resource %s (%s)", resource.getUid(), resource.getName())); } break; } } }
public IconButton( ResourceBundle resources, ResourceItem resourceItem, Client context, ResourceGroupList group) { super(resources, resourceItem, context); getStyleClass().add("iconButton"); setTooltipText(resourceItem.getResource().getName()); String typeName = resourceItem.getResource().getType().name(); try { String iconName = resourceItem.getResource().getIcon(); if (StringUtils.isBlank(iconName)) { iconName = "logo://96_autotype_autotype_auto.png"; } if (iconName.startsWith("res://")) { // Client specified icon (when retrieving resources) final String resourceName = iconName.substring(6); URL resource = getClass().getResource(resourceName); if (resource == null) { setText(resources.getString("resource.icon." + typeName)); log.warn( String.format( "Falling back to text based icon for type %s because %s could not be found (%s)", typeName, resourceName, resourceItem.getResource().getName())); } else { final ImageView imageView = new ImageView(resource.toString()); configureButton(imageView); setGraphic(imageView); } } else { // Server specified icon String iconPath = iconName; if (iconPath.indexOf("/") == -1) { iconPath = "files/download/" + iconName; } else { if (iconName.startsWith("logo://")) { try { iconPath = "logo/" + URLEncoder.encode(typeName, "UTF-8") + "/" + URLEncoder.encode(resourceItem.getResource().getName(), "UTF-8") + "/" + iconName.substring(7); } catch (UnsupportedEncodingException e) { throw new RuntimeException(e); } } } final ImageView imageView = new ImageView(getClass().getResource("ajax-loader.gif").toString()); configureButton(imageView); setGraphic(imageView); String cacheKey = resourceItem.getResourceRealm().getName() + "-" + iconPath; if (iconCache.containsKey(cacheKey)) { imageView.setImage(iconCache.get(cacheKey)); } else { context .getLoadQueue() .execute( new IconLoader( resourceItem.getResourceRealm().getName(), cacheKey, imageView, iconPath, context, group) { @Override protected void onImageLoaded() { sizeToImage(); } }); } } } catch (MissingResourceException mre) { setText("%" + typeName); } sizeToImage(); // Bouncer for click events double bounceSpeed = 50; bouncer.setCycleCount(3); bouncer .getKeyFrames() .addAll( makeKeyFrame(0, 0, 1.0, 1.0), makeKeyFrame(bounceSpeed * 2f, 4, 1.0, 1.0), makeKeyFrame(bounceSpeed * 2.5f, 4, 1.1, 0.9), makeKeyFrame(bounceSpeed * 4.5f, 0, 1.0, 1.0), makeKeyFrame(bounceSpeed * 6.5f, -4, 1.0, 1.0), makeKeyFrame(bounceSpeed * 8.5f, 0, 1.0, 1.0)); bouncer.setOnFinished( value -> { if (launching) { bouncer.play(); } }); // Grower for hover in grower.getKeyFrames().addAll(makeKeyFrame(0, 0, 1.0, 1.0), makeKeyFrame(100, 0, 1.2, 1.2)); // Shrinker for hover out shrinker.getKeyFrames().addAll(makeKeyFrame(0, 0, 1.2, 1.2), makeKeyFrame(100, 0, 1.0, 1.0)); // Monitor mouse activity setOnMouseEntered( value -> { grower.play(); }); setOnMouseExited( value -> { shrinker.play(); }); }