org.robovm.compiler.config.Resource Maven / Gradle / Ivy
/*
* Copyright (C) 2013 Trillian Mobile AB
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package org.robovm.compiler.config;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import org.robovm.compiler.target.Target;
import org.robovm.compiler.util.AntPathMatcher;
import org.simpleframework.xml.Element;
import org.simpleframework.xml.ElementList;
/**
* Specifies resources needed by the application the compiler produces. A resource can be
* specified using a single {@link File}:
* <resource>path/to/the/resource.txt</resource>
* If the path specifies a directory the directory including its contents (except for the default
* excludes, see below) will be copied. If the path specifies a file, that file will be copied.
*
* A resource be also be specified with a base directory, a target path and include and exclude
* filters (similar to Maven's <resource> element):
*
<resource>
* <targetPath>data</targetPath>
* <directory>resources</directory>
* <includes>
* <include>**/*</include>
* </includes>
* <excludes>
* <exclude>**/*.bak</exclude>
* </excludes>
* <flatten>false</flatten>
* <ignoreDefaultExcludes>false</ignoreDefaultExcludes>
* <skipPngCrush>false</skipPngCrush>
*</resource>
* Each element represents a property in this class. Please see the documentation for each
* property's getter method for more information.
*
*
* The current {@link Target} may transform and rename a resource while being copied (e.g. running
* {@code pngcrush} or converting {@code xib} files to {@code nib} files).
*
* Default excludes
* (The same as those used by ANT 1.9)
*
* Miscellaneous typical temporary files:
*
* - **/*~
* - **/#*#
* - **/.#*
* - **/%*%
* - **/._*
*
* CVS:
*
* - **/CVS
* - **/CVS/**
* - **/.cvsignore
*
* SCCS:
*
* - **/SCCS
* - **/SCCS/**
*
* Visual SourceSafe:
*
* - **/vssver.scc
*
* Subversion:
*
* - **/.svn
* - **/.svn/**
*
* Git:
*
* - **/.git
* - **/.git/**
* - **/.gitattributes
* - **/.gitignore
* - **/.gitmodules
*
* Mercurial:
*
* - **/.hg
* - **/.hg/**
* - **/.hgignore
* - **/.hgsub
* - **/.hgsubstate
* - **/.hgtags
*
* Bazaar:
*
* - **/.bzr
* - **/.bzr/**
* - **/.bzrignore
*
* Mac:
*
* - **/.DS_Store
*
*
*/
public class Resource {
/**
* Interface used by {@link Resource#walk(Walker, File)} to walk the paths matched by a
* {@link Resource}.
*/
public interface Walker {
/**
* Processes the specified file. This typically copies the file to the destination directory
* possibly renaming/transforming the file in some way in the process.
*/
void process(Resource resource, File file, File destDir) throws IOException;
}
private static final AntPathMatcher MATCH_ALL_MATCHER = new AntPathMatcher("**/*");
private static final String[] DEFAULTEXCLUDES = {
// Miscellaneous typical temporary files
"**/*~",
"**/#*#",
"**/.#*",
"**/%*%",
"**/._*",
// CVS
"**/CVS",
"**/CVS/**",
"**/.cvsignore",
// SCCS
"**/SCCS",
"**/SCCS/**",
// Visual SourceSafe
"**/vssver.scc",
// Subversion
"**/.svn",
"**/.svn/**",
// Git
"**/.git",
"**/.git/**",
"**/.gitattributes",
"**/.gitignore",
"**/.gitmodules",
// Mercurial
"**/.hg",
"**/.hg/**",
"**/.hgignore",
"**/.hgsub",
"**/.hgsubstate",
"**/.hgtags",
// Bazaar
"**/.bzr",
"**/.bzr/**",
"**/.bzrignore",
// Mac
"**/.DS_Store"
};
private static final List DEFAULTEXCLUDESMATCHERS;
static {
DEFAULTEXCLUDESMATCHERS = toAntPathMatchers(Arrays.asList(DEFAULTEXCLUDES));
}
@Element(required = false)
private String targetPath;
@Element(required = false)
private File directory;
@ElementList(required = false, entry = "include")
private ArrayList includes;
@ElementList(required = false, entry = "exclude")
private ArrayList excludes;
@Element(required = false)
private Boolean flatten;
@Element(required = false)
private Boolean ignoreDefaultExcludes;
@Element(required = false)
private Boolean skipPngCrush;
@Element(required = false)
private File path;
protected Resource() {}
/**
* Creates a new simple {@link Resource} which will copy the specified file or directory.
*
* @param path the {@link File} which will be copied.
*/
public Resource(File path) {
this.path = path;
}
/**
* Creates a new {@link Resource} which will copy all files in the specified base directory
* to the specified target path in the application directory.
*
* @param directory the base directory.
* @param targetPath the target path.
*/
public Resource(File directory, String targetPath) {
this.directory = directory;
this.targetPath = targetPath;
}
/**
* Returns the path which will be copied for simple {@link Resource}s or {@code null} if this
* isn't a simple {@link Resource}.
*
* @return the path.
*/
public File getPath() {
return path;
}
/**
* Returns the target path relative to the application directory (i.e. app bundle directory for
* iOS apps) where paths matching this {@link Resource} will be copied. If not specified paths
* will be copied directly to the application directory.
*
* @return the target path or {@code null} if not specified.
*/
public String getTargetPath() {
return targetPath;
}
/**
* Returns the base directory containing the files and directories copied by the
* {@link Resource}.
*
* @return the base directory.
*/
public File getDirectory() {
return directory;
}
/**
* Returns a list of Ant-style patterns (using **, *, ? as wildcards) matching files which will
* be included when copying this {@link Resource}.
*
* @return the include patterns.
* @see AntPathMatcher
*/
public List getIncludes() {
return includes != null ? Collections.unmodifiableList(includes) : Collections.emptyList();
}
/**
* Adds include patterns.
*
* @return {@code this}
* @see #getIncludes()
*/
public Resource include(String ... patterns) {
if (includes == null) {
includes = new ArrayList();
}
includes.addAll(Arrays.asList(patterns));
return this;
}
/**
* Returns a list of Ant-style patterns (using **, *, ? as wildcards) matching files which will
* be excluded when copying this {@link Resource}.
*
* @return the exclude patterns.
* @see AntPathMatcher
*/
public List getExcludes() {
return excludes != null ? Collections.unmodifiableList(excludes) : Collections.emptyList();
}
/**
* Adds exclude patterns.
*
* @return {@code this}
* @see #getExcludes()
*/
public Resource exclude(String ... patterns) {
if (excludes == null) {
excludes = new ArrayList();
}
excludes.addAll(Arrays.asList(patterns));
return this;
}
/**
* Returns true
if the files matched by this {@link Resource} should be copied
* directly into the application directory without preserving the directory structure of the
* source directory. The default is false
.
*
* @return true
if the directory structure should be flattened.
*/
public boolean isFlatten() {
return flatten == null ? false : flatten.booleanValue();
}
/**
* Sets the {@code flatten} property and returns {@code this}.
*
* @param b the new value.
* @return {@code this}
* @see #isFlatten()
*/
public Resource flatten(boolean b) {
this.flatten = b;
return this;
}
/**
* Returns true
if the default excludes should be
* ignored and copied for this {@link Resource}. The default is false
, i.e. don't
* copy files matching the default excludes.
*
* @return true
if default excludes should be ignored.
*/
public boolean isIgnoreDefaultExcludes() {
return ignoreDefaultExcludes == null ? false : ignoreDefaultExcludes.booleanValue();
}
/**
* Sets the {@code ignoreDefaultExcludes} property and returns {@code this}.
*
* @param b the new value.
* @return {@code this}
* @see #isIgnoreDefaultExcludes()
*/
public Resource ignoreDefaultExcludes(boolean b) {
this.ignoreDefaultExcludes = b;
return this;
}
/**
* Returns true
if {@code pngcrush} should not be called for PNG files matching
* this {@link Resource} when targeting iOS. The default is false
, i.e.
* {@code pngcrush} WILL be called for PNG files.
*
* @return true
if {@code pngcrush} should not be called.
*/
public boolean isSkipPngCrush() {
return skipPngCrush == null ? false : skipPngCrush.booleanValue();
}
/**
* Sets the {@code skipPngCrush} property and returns {@code this}.
*
* @param b the new value.
* @return {@code this}
* @see #isSkipPngCrush()
*/
public Resource skipPngCrush(boolean b) {
this.skipPngCrush = b;
return this;
}
public void walk(Walker walker, File destDir) throws IOException {
if (targetPath != null && targetPath.trim().length() > 0) {
destDir = new File(destDir, targetPath);
}
if (path != null) {
// Walk path and all its descendants (if a directory) including everything except the
// default excludes.
walk(walker, path.getParentFile(), path.getName(), destDir,
Collections.singletonList(MATCH_ALL_MATCHER), DEFAULTEXCLUDESMATCHERS);
} else {
List inc = toAntPathMatchers(includes);
if (inc.isEmpty()) {
inc.add(MATCH_ALL_MATCHER);
}
List exc = toAntPathMatchers(excludes);
if (!isIgnoreDefaultExcludes()) {
exc.addAll(DEFAULTEXCLUDESMATCHERS);
}
File dir = null;
if (directory != null) {
dir = directory.getCanonicalFile();
} else {
dir = new File("").getCanonicalFile();
}
if (dir.exists() && dir.isDirectory()) {
for (File f : dir.listFiles()) {
walk(walker, dir, f.getName(), destDir, inc, exc);
}
}
}
}
private void walk(Walker walker, File baseDir, String path, File destDir,
List inc, List exc) throws IOException {
File f = new File(baseDir, path);
// Always descend into directories unless explicitly excluded.
if ((f.isDirectory() || matches(path, inc)) && !matches(path, exc)) {
if (f.isFile()) {
walker.process(this, f, destDir);
} else if (f.isDirectory()) {
File newDestDir = destDir;
if (!isFlatten()) {
newDestDir = new File(destDir, f.getName());
}
for (File child : f.listFiles()) {
walk(walker, baseDir, path + File.separator + child.getName(), newDestDir, inc, exc);
}
}
}
}
private static ArrayList toAntPathMatchers(List listOfStrings) {
ArrayList l = new ArrayList();
if (listOfStrings != null) {
for (String pattern : listOfStrings) {
l.add(new AntPathMatcher(pattern));
}
}
return l;
}
private boolean matches(String path, List matchers) {
for (AntPathMatcher matcher : matchers) {
if (matcher.matches(path)) {
return true;
}
}
return false;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result
+ ((directory == null) ? 0 : directory.hashCode());
result = prime * result
+ ((excludes == null) ? 0 : excludes.hashCode());
result = prime * result + ((flatten == null) ? 0 : flatten.hashCode());
result = prime
* result
+ ((ignoreDefaultExcludes == null) ? 0 : ignoreDefaultExcludes
.hashCode());
result = prime * result
+ ((includes == null) ? 0 : includes.hashCode());
result = prime * result + ((path == null) ? 0 : path.hashCode());
result = prime * result
+ ((skipPngCrush == null) ? 0 : skipPngCrush.hashCode());
result = prime * result
+ ((targetPath == null) ? 0 : targetPath.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
Resource other = (Resource) obj;
if (directory == null) {
if (other.directory != null) {
return false;
}
} else if (!directory.equals(other.directory)) {
return false;
}
if (excludes == null) {
if (other.excludes != null) {
return false;
}
} else if (!excludes.equals(other.excludes)) {
return false;
}
if (flatten == null) {
if (other.flatten != null) {
return false;
}
} else if (!flatten.equals(other.flatten)) {
return false;
}
if (ignoreDefaultExcludes == null) {
if (other.ignoreDefaultExcludes != null) {
return false;
}
} else if (!ignoreDefaultExcludes.equals(other.ignoreDefaultExcludes)) {
return false;
}
if (includes == null) {
if (other.includes != null) {
return false;
}
} else if (!includes.equals(other.includes)) {
return false;
}
if (path == null) {
if (other.path != null) {
return false;
}
} else if (!path.equals(other.path)) {
return false;
}
if (skipPngCrush == null) {
if (other.skipPngCrush != null) {
return false;
}
} else if (!skipPngCrush.equals(other.skipPngCrush)) {
return false;
}
if (targetPath == null) {
if (other.targetPath != null) {
return false;
}
} else if (!targetPath.equals(other.targetPath)) {
return false;
}
return true;
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("Resource [targetPath=").append(targetPath)
.append(", directory=").append(directory).append(", includes=")
.append(includes).append(", excludes=").append(excludes)
.append(", flatten=").append(flatten)
.append(", ignoreDefaultExcludes=")
.append(ignoreDefaultExcludes).append(", skipPngCrush=")
.append(skipPngCrush).append(", path=").append(path)
.append("]");
return builder.toString();
}
}