| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| |
| package org.netbeans.nbbuild; |
| |
| import java.awt.Dimension; |
| import java.io.BufferedOutputStream; |
| import java.io.ByteArrayInputStream; |
| import java.io.File; |
| import java.io.FileFilter; |
| import java.io.FileInputStream; |
| import java.io.FileNotFoundException; |
| import java.io.FileOutputStream; |
| import java.io.FilenameFilter; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.io.PrintWriter; |
| import java.util.ArrayList; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.StringTokenizer; |
| |
| import org.apache.tools.ant.BuildException; |
| import org.apache.tools.ant.DirectoryScanner; |
| import org.apache.tools.ant.Project; |
| import org.apache.tools.ant.taskdefs.Copy; |
| import org.apache.tools.ant.taskdefs.MatchingTask; |
| import org.apache.tools.ant.types.FileSet; |
| |
| /** |
| * Task for copying out icons from NetBeans projects. It creates paralel directory |
| * structure in destdir with only copied icons. It creates index.html page that |
| * lists all found icons and group them according to nb modules. Icons are considered |
| * images with dimensions 8x8, 16x16, 24x24, 32x32 of type PNG or GIF. |
| * Inclusion filter for images in each module is given by attribute 'iconincludes', |
| * default inclusion filter is all gifs and pngs under src folder. Exclusion filter |
| * is defined by attribute 'iconexcludes'. Filters are regular ant inclusion and |
| * exclusion filters. Task tries to find all nb modules up to depth defined by |
| * 'depth' attribute (default is 4) in the nbsrcroot. Modules without icons are |
| * not listed by default, they can be shown by setting attribute 'showempty' to true. |
| * |
| * Required attributes are: |
| * nbsrcroot ... root of NetBeans Hg checkout |
| * destdir ... dir for copying icons |
| * |
| * @author Milan Kubec |
| */ |
| public class CopyIcons extends MatchingTask { |
| |
| private int depth = 0; |
| private List<File> projectDirList = new ArrayList<>(); |
| private List<ProjectIconInfo> prjIconInfoList = new ArrayList<>(); |
| |
| File baseDir = null; |
| public void setNbsrcroot(File f) { |
| baseDir = f; |
| } |
| |
| File destDir = null; |
| public void setDestdir(File f) { |
| destDir = f; |
| } |
| |
| int userDepth = 2; |
| public void setDepth(int n) { |
| userDepth = n; |
| } |
| |
| String iconIncludes = "src/**/*.png,src/**/*.gif"; |
| public void setIconincludes(String s) { |
| iconIncludes = s; |
| } |
| |
| String iconExcludes = ""; |
| public void setIconexcludes(String s) { |
| iconExcludes = s; |
| } |
| |
| String prjIncludes = ""; |
| public void setPrjincludes(String s) { |
| prjIncludes = s; |
| } |
| |
| String prjExcludes = ""; |
| public void setPrjExcludes(String s) { |
| prjExcludes = s; |
| } |
| |
| boolean showEmpty = false; |
| public void setShowempty(boolean b) { |
| showEmpty = b; |
| } |
| |
| public void execute() throws BuildException { |
| if (baseDir == null || destDir == null) { |
| log("Nbsrcroot or destdir are not specified."); |
| return; |
| } |
| scanForProjectDirs(baseDir); |
| for (Iterator<File> iter = projectDirList.iterator(); iter.hasNext(); ) { |
| File f = iter.next(); |
| processProjectDir(f); |
| } |
| copyToDestDir(prjIconInfoList); |
| dumpListToHTML(prjIconInfoList); |
| } |
| |
| private ImageInfo readImageInfo(File fl) throws IOException { |
| Dimension dim = null; |
| ImageInfo imageInfo = null; |
| try (ByteArrayInputStream bais = readSomeBytes(fl)) { |
| if (isGIF(bais)) { |
| imageInfo = new ImageInfo(readGIFDimension(bais), ImageInfo.GIF); |
| } else if (isPNG(bais)) { |
| imageInfo = new ImageInfo(readPNGDimension(bais), ImageInfo.PNG); |
| } |
| } catch (IOException ex) { |
| ex.printStackTrace(); |
| } |
| return imageInfo; |
| } |
| |
| private ByteArrayInputStream readSomeBytes(File fl) throws IOException { |
| byte buffer[] = new byte[30]; |
| FileInputStream fis = null; |
| fis = new FileInputStream(fl); |
| fis.read(buffer); |
| fis.close(); |
| return new ByteArrayInputStream(buffer); |
| } |
| |
| private boolean isGIF(InputStream is) throws IOException { |
| is.reset(); |
| byte buf[]; |
| is.read(buf = new byte[3]); |
| int signatureBuffer[] = parseUnsigned(buf); |
| if (((char) signatureBuffer[0] == 'G') && |
| ((char) signatureBuffer[1] == 'I') && |
| ((char) signatureBuffer[2] == 'F')) { |
| return true; |
| } |
| return false; |
| } |
| |
| private boolean isPNG(InputStream is) throws IOException { |
| is.reset(); |
| byte buf[]; |
| is.read(buf = new byte[8]); |
| int signatureBuffer[] = parseUnsigned(buf); |
| if ((signatureBuffer[0] == 137) && // PNG signature |
| (signatureBuffer[1] == 80) && |
| (signatureBuffer[2] == 78) && |
| (signatureBuffer[3] == 71) && |
| (signatureBuffer[4] == 13) && |
| (signatureBuffer[5] == 10) && |
| (signatureBuffer[6] == 26) && |
| (signatureBuffer[7] == 10)) { |
| return true; |
| } |
| return false; |
| } |
| |
| private Dimension readGIFDimension(InputStream is) throws IOException { |
| byte buf []; |
| is.read(new byte[3]); // GIF version |
| is.read(buf = new byte[2]); // width |
| int widthBuf[] = parseUnsigned(buf); |
| int width = (widthBuf[1] << 8) + widthBuf[0]; |
| is.read(buf = new byte[2]); // height |
| int heightBuf[] = parseUnsigned(buf); |
| int height = (heightBuf[1] << 8) + heightBuf[0]; |
| return new Dimension(width, height); |
| } |
| |
| private Dimension readPNGDimension(InputStream is) throws IOException { |
| byte buf[]; |
| is.read(new byte[4]); // length |
| is.read(new byte[4]); // type |
| is.read(buf = new byte[4]); // width |
| int widthBuf[] = parseUnsigned(buf); |
| int width = (widthBuf[0] << 24) + (widthBuf[1] << 16) + (widthBuf[2] << 8) + widthBuf[3]; |
| is.read(buf = new byte[4]); // height |
| int heightBuf[] = parseUnsigned(buf); |
| int height = (heightBuf[0] << 24) + (heightBuf[1] << 16) + (heightBuf[2] << 8) + heightBuf[3]; |
| return new Dimension(width, height); |
| } |
| |
| private static int[] parseUnsigned(byte[] src) { |
| int[] val = new int[src.length]; |
| for (int i = 0; i < src.length; i ++) { |
| val[i] = (src[i] < 0) && (src[i] >= -128) ? 256 + src[i] : src[i]; |
| } |
| return val; |
| } |
| |
| // ------------------------------------------------------------------------- |
| |
| // public static void main(String[] args) { |
| // File buildFile = new File("e:/development/projects/CopyIcons/testscript.xml"); |
| // Project p = new Project(); |
| // p.setUserProperty("ant.file", buildFile.getAbsolutePath()); |
| // p.init(); |
| // ProjectHelper helper = ProjectHelper.getProjectHelper(); |
| // p.addReference("ant.projectHelper", helper); |
| // helper.parse(p, buildFile); |
| // p.executeTarget(p.getDefaultTarget()); |
| // } |
| |
| // ------------------------------------------------------------------------- |
| |
| private static class ImageInfo { |
| |
| public static final int GIF = 1; |
| public static final int PNG = 2; |
| |
| private Dimension dim; |
| private int type; |
| private String path; |
| private String ext; |
| |
| public ImageInfo(Dimension dm, int tp) { |
| this(null, dm, tp); |
| } |
| |
| public ImageInfo(String pth, Dimension dm, int tp) { |
| path = pth; |
| dim = dm; |
| type = tp; |
| } |
| |
| public String getPath() { |
| return path; |
| } |
| |
| public void setPath(String pth) { |
| path = pth; |
| } |
| |
| public int getHeight() { |
| return dim.height; |
| } |
| |
| public int getWidth() { |
| return dim.width; |
| } |
| |
| public String getType() { |
| if (type == GIF) { |
| return "GIF"; |
| } else if (type == PNG) { |
| return "PNG"; |
| } |
| return ""; |
| } |
| |
| public String getExt() { |
| return ext; |
| } |
| |
| public String setExt(String ex) { |
| return ext = ex; |
| } |
| |
| } |
| |
| private static class ProjectIconInfo { |
| |
| public String prjPath; |
| public List<ImageInfo> matchingIcons; |
| public List<ImageInfo> notmatchingIcons; |
| |
| public ProjectIconInfo(String pth, List<ImageInfo> mi, List<ImageInfo> nmi) { |
| prjPath = pth; |
| matchingIcons = mi; |
| notmatchingIcons = nmi; |
| } |
| |
| } |
| |
| // ------------------------------------------------------------------------- |
| |
| private void dumpListToHTML(List<ProjectIconInfo> lst) { |
| File reportFile = new File(destDir, "index.html"); |
| FileOutputStream fos = null; |
| try { |
| fos = new FileOutputStream(reportFile); |
| } catch (FileNotFoundException ex) { |
| ex.printStackTrace(); |
| } |
| try (PrintWriter pw = new PrintWriter(new BufferedOutputStream(fos))) { |
| pw.println("<html>"); |
| pw.println("<head>"); |
| pw.println("<style type=\"text/css\">\nbody { font-family: Tahoma, Verdana, sans-serif }\n</style>"); |
| pw.println("</head>"); |
| pw.println("<body>"); |
| pw.println("<h2>List of icons in NetBeans projects</h2>"); |
| pw.println("<h3>NetBeans Source Root: " + baseDir + "<br/>"); |
| pw.println("Destination Directory: " + destDir + "</h3>"); |
| pw.println("<p style=\"width: 70%\"><b>Description:</b><br/> New Image is image in the Destination Directory, Orig. Image is image in NetBeans Source Root. " + |
| "By replacing the image in Destination Directory under coresponding module and path you can prepare rebranded " + |
| "icons in paralel directory structure and then copy them over to NetBeans Source Root. Orig. Image is " + |
| "in the table just for comparison and reference what image was already changed.</p>"); |
| for (Iterator<ProjectIconInfo> iter = prjIconInfoList.iterator(); iter.hasNext(); ) { |
| ProjectIconInfo info = iter.next(); |
| if (!showEmpty && info.matchingIcons.size() == 0) { |
| continue; |
| } |
| pw.println("<p>Module name: <b>" + info.prjPath + "</b></p>"); |
| pw.println("<p style=\"margin-left: 20px\">"); |
| if (info.matchingIcons.size() == 0) { |
| pw.println("<i>--- No icons ---</i>"); |
| pw.println("</p>"); |
| continue; |
| } |
| pw.println("<table width=\"80%\"border=\"1\" cellpadding=\"3\" cellspacing=\"0\">"); |
| pw.println("<tr><td><b>Resource Path</b></td>" + |
| "<td align=\"center\"><b> New Image </b></td>" + |
| "<td align=\"center\"><b> Orig. image </b></td>" + |
| "<td align=\"center\"><b> W x H </b></td>" + |
| "<td align=\"center\"><b> Extension </b></td>" + |
| "<td align=\"center\"><b> Real Type </b></td></tr>"); |
| for (Iterator<ImageInfo> goodIter = info.matchingIcons.iterator(); goodIter.hasNext(); ) { |
| ImageInfo goodInfo = goodIter.next(); |
| String iconPath = goodInfo.getPath(); |
| String copiedIconPath = info.prjPath + File.separator + iconPath; |
| String originalIconPath = baseDir.getAbsolutePath() + File.separator + info.prjPath + File.separator + iconPath; |
| pw.println("<tr>"); |
| pw.println("<td>"); |
| pw.println("<a href=\"" + copiedIconPath + "\">" + iconPath + "</a>"); |
| pw.println("</td>"); |
| pw.println("<td align=\"center\">"); |
| pw.println("<img src=\"" + copiedIconPath + "\"/>"); // copied image |
| pw.println("</td>"); |
| pw.println("<td align=\"center\">"); |
| pw.println("<img src=\"file://" + originalIconPath + "\"/>"); // original image |
| pw.println("</td>"); |
| pw.println("<td align=\"center\">" + goodInfo.getWidth() + " x " + goodInfo.getHeight() + "</td>"); |
| pw.println("<td align=\"center\">" + goodInfo.getExt().toUpperCase() + "</td>"); |
| if (!goodInfo.getExt().equalsIgnoreCase(goodInfo.getType())) { |
| pw.println("<td align=\"center\"><font color=\"Orange\">" + goodInfo.getType() + "</font></td>"); |
| } else { |
| pw.println("<td align=\"center\">" + goodInfo.getType() + "</td>"); |
| } |
| pw.println("</tr>"); |
| } |
| pw.println("</table>"); |
| pw.println("</p>"); |
| } |
| pw.println("</body>"); |
| pw.println("</html>"); |
| pw.flush(); |
| } |
| log("---> Report was written to file: " + reportFile.getAbsolutePath()); |
| } |
| |
| private void copyToDestDir(List<ProjectIconInfo> prjInfoList) { |
| FileSet fs = null; |
| for (Iterator<ProjectIconInfo> iter = prjInfoList.iterator(); iter.hasNext(); ) { |
| fs = new FileSet(); |
| log("Setting basedir for fileset: " + baseDir, Project.MSG_VERBOSE); |
| ProjectIconInfo prjIconInfo = iter.next(); |
| fs.setDir(new File(baseDir, prjIconInfo.prjPath)); |
| int numFilesToCopy = prjIconInfo.matchingIcons.size() + prjIconInfo.notmatchingIcons.size(); |
| for (Iterator<ImageInfo> matchInfoIter = prjIconInfo.matchingIcons.iterator(); matchInfoIter.hasNext(); ) { |
| ImageInfo info = matchInfoIter.next(); |
| log("Adding file to matching fileset: " + info.getPath(), Project.MSG_VERBOSE); |
| fs.setIncludes(info.getPath()); |
| } |
| for (Iterator<ImageInfo> notmatchInfoIter = prjIconInfo.notmatchingIcons.iterator(); notmatchInfoIter.hasNext(); ) { |
| ImageInfo info = notmatchInfoIter.next(); |
| log("Adding file to notmatching fileset: " + info.getPath(), Project.MSG_VERBOSE); |
| fs.setIncludes(info.getPath()); |
| } |
| if (numFilesToCopy > 0) { |
| Copy copy = (Copy) getProject().createTask("copy"); |
| copy.addFileset(fs); |
| File dest = new File(destDir, prjIconInfo.prjPath); |
| dest.mkdir(); |
| copy.setTodir(dest); |
| copy.init(); |
| copy.setLocation(getLocation()); |
| copy.execute(); |
| } |
| } |
| } |
| |
| // ------------------------------------------------------------------------- |
| |
| private void scanForProjectDirs(File fl) { |
| if (depth > userDepth) return; |
| //if (isProjectDir(fl)) { |
| // projectDirList.add(fl); |
| //} |
| File allFiles[] = fl.listFiles(new FileFilter() { |
| public boolean accept(File pathname) { |
| if (pathname.isDirectory()) { |
| return true; |
| } |
| return false; |
| } |
| }); |
| depth++; |
| for (File f : allFiles) { |
| if (isProjectDir(f)) { |
| // here could be some project exclusion logic |
| projectDirList.add(f); |
| log(f.toString(), Project.MSG_VERBOSE); |
| scanForProjectDirs(f); |
| } else { |
| scanForProjectDirs(f); |
| } |
| } |
| depth--; |
| } |
| |
| private boolean isProjectDir(File fl) { |
| File prjDirs[] = fl.listFiles(new FilenameFilter() { |
| public boolean accept(File dir, String name) { |
| if ("nbproject".equals(name)) { |
| return true; |
| } |
| return false; |
| } |
| }); |
| if (prjDirs.length != 1) { |
| return false; |
| } |
| String prjFiles[] = prjDirs[0].list(new FilenameFilter() { |
| public boolean accept(File dir, String name) { |
| if ("project.xml".equals(name)) { |
| return true; |
| } |
| return false; |
| } |
| }); |
| if (prjFiles.length != 1) { |
| return false; |
| } |
| return true; |
| } |
| |
| private void processProjectDir(File f) { |
| String prjPath = null; |
| //if (f.getAbsolutePath().equals(baseDir.getAbsolutePath())) { |
| // prjPath = ""; |
| //} else { |
| prjPath = f.getAbsolutePath().substring(baseDir.getAbsolutePath().length() + 1); |
| //} |
| log("Processing project dir: " + prjPath); |
| DirectoryScanner ds = new DirectoryScanner(); |
| ds.setBasedir(f); |
| ds.setIncludes(getAsArray(iconIncludes)); |
| ds.setExcludes(getAsArray(iconExcludes)); |
| ds.setCaseSensitive(false); |
| ds.scan(); |
| String[] files = ds.getIncludedFiles(); |
| log(" Found " + files.length + " files in " + f); |
| List<ImageInfo> goodIcons = new ArrayList<>(); |
| List<ImageInfo> badIcons = new ArrayList<>(); |
| for (String file : files) { |
| String ext = file.substring(file.lastIndexOf('.') + 1); |
| if (ext.equalsIgnoreCase("gif") || ext.equalsIgnoreCase("png")) { |
| File iconFile = new File(f, file); |
| try { |
| ImageInfo imageInfo = null; |
| imageInfo = readImageInfo(iconFile); |
| if (imageInfo != null) { |
| imageInfo.setPath(file); |
| imageInfo.setExt(ext); |
| int w = imageInfo.getWidth(); |
| int h = imageInfo.getHeight(); |
| if ((w == 8 && h == 8) || (w == 16 && h == 16) || |
| (w == 24 && h == 24) || (w == 32 && h == 32)) { |
| goodIcons.add(imageInfo); |
| } else { |
| badIcons.add(imageInfo); |
| } |
| } |
| } catch (IOException ex) { |
| ex.printStackTrace(); |
| } |
| } |
| } |
| ProjectIconInfo prjIconInfo = new ProjectIconInfo(prjPath, goodIcons, badIcons); |
| prjIconInfoList.add(prjIconInfo); |
| } |
| |
| private String[] getAsArray(String s) { |
| List<String> list = new ArrayList<>(); |
| for (StringTokenizer stok = new StringTokenizer(s, ","); stok.hasMoreTokens(); ) { |
| String token = stok.nextToken().trim(); |
| list.add(token); |
| } |
| return list.toArray(new String[list.size()]); |
| } |
| |
| } |