001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041:
042: package org.netbeans.nbbuild;
043:
044: import java.awt.Dimension;
045: import java.io.BufferedOutputStream;
046: import java.io.ByteArrayInputStream;
047: import java.io.File;
048: import java.io.FileFilter;
049: import java.io.FileInputStream;
050: import java.io.FileNotFoundException;
051: import java.io.FileOutputStream;
052: import java.io.FilenameFilter;
053: import java.io.IOException;
054: import java.io.InputStream;
055: import java.io.PrintWriter;
056: import java.util.ArrayList;
057: import java.util.Iterator;
058: import java.util.List;
059: import java.util.StringTokenizer;
060:
061: import org.apache.tools.ant.BuildException;
062: import org.apache.tools.ant.DirectoryScanner;
063: import org.apache.tools.ant.Project;
064: import org.apache.tools.ant.taskdefs.Copy;
065: import org.apache.tools.ant.taskdefs.MatchingTask;
066: import org.apache.tools.ant.types.FileSet;
067:
068: /**
069: * Task for copying out icons from NetBeans projects. It creates paralel directory
070: * structure in destdir with only copied icons. It creates index.html page that
071: * lists all found icons and group them according to nb modules. Icons are considered
072: * images with dimensions 8x8, 16x16, 24x24, 32x32 of type PNG or GIF.
073: * Inclusion filter for images in each module is given by attribute 'iconincludes',
074: * default inclusion filter is all gifs and pngs under src folder. Exclusion filter
075: * is defined by attribute 'iconexcludes'. Filters are regular ant inclusion and
076: * exclusion filters. Task tries to find all nb modules up to depth defined by
077: * 'depth' attribute (default is 4) in the nbsrcroot. Modules without icons are
078: * not listed by default, they can be shown by setting attribute 'showempty' to true.
079: *
080: * Required attributes are:
081: * nbsrcroot ... root of NetBeans Hg checkout
082: * destdir ... dir for copying icons
083: *
084: * @author Milan Kubec
085: */
086: public class CopyIcons extends MatchingTask {
087:
088: private int depth = 0;
089: private List<File> projectDirList = new ArrayList<File>();
090: private List<ProjectIconInfo> prjIconInfoList = new ArrayList<ProjectIconInfo>();
091:
092: File baseDir = null;
093:
094: public void setNbsrcroot(File f) {
095: baseDir = f;
096: }
097:
098: File destDir = null;
099:
100: public void setDestdir(File f) {
101: destDir = f;
102: }
103:
104: int userDepth = 2;
105:
106: public void setDepth(int n) {
107: userDepth = n;
108: }
109:
110: String iconIncludes = "src/**/*.png,src/**/*.gif";
111:
112: public void setIconincludes(String s) {
113: iconIncludes = s;
114: }
115:
116: String iconExcludes = "";
117:
118: public void setIconexcludes(String s) {
119: iconExcludes = s;
120: }
121:
122: String prjIncludes = "";
123:
124: public void setPrjincludes(String s) {
125: prjIncludes = s;
126: }
127:
128: String prjExcludes = "";
129:
130: public void setPrjExcludes(String s) {
131: prjExcludes = s;
132: }
133:
134: boolean showEmpty = false;
135:
136: public void setShowempty(boolean b) {
137: showEmpty = b;
138: }
139:
140: public void execute() throws BuildException {
141: if (baseDir == null || destDir == null) {
142: log("Nbsrcroot or destdir are not specified.");
143: return;
144: }
145: scanForProjectDirs(baseDir);
146: for (Iterator<File> iter = projectDirList.iterator(); iter
147: .hasNext();) {
148: File f = iter.next();
149: processProjectDir(f);
150: }
151: copyToDestDir(prjIconInfoList);
152: dumpListToHTML(prjIconInfoList);
153: }
154:
155: private ImageInfo readImageInfo(File fl) throws IOException {
156: Dimension dim = null;
157: ImageInfo imageInfo = null;
158: ByteArrayInputStream bais = null;
159: try {
160: bais = readSomeBytes(fl);
161: if (isGIF(bais)) {
162: imageInfo = new ImageInfo(readGIFDimension(bais),
163: ImageInfo.GIF);
164: } else if (isPNG(bais)) {
165: imageInfo = new ImageInfo(readPNGDimension(bais),
166: ImageInfo.PNG);
167: }
168: } catch (IOException ex) {
169: ex.printStackTrace();
170: } finally {
171: bais.close();
172: }
173: return imageInfo;
174: }
175:
176: private ByteArrayInputStream readSomeBytes(File fl)
177: throws IOException {
178: byte buffer[] = new byte[30];
179: FileInputStream fis = null;
180: fis = new FileInputStream(fl);
181: fis.read(buffer);
182: fis.close();
183: return new ByteArrayInputStream(buffer);
184: }
185:
186: private boolean isGIF(InputStream is) throws IOException {
187: is.reset();
188: byte buf[];
189: is.read(buf = new byte[3]);
190: int signatureBuffer[] = parseUnsigned(buf);
191: if (((char) signatureBuffer[0] == 'G')
192: && ((char) signatureBuffer[1] == 'I')
193: && ((char) signatureBuffer[2] == 'F')) {
194: return true;
195: }
196: return false;
197: }
198:
199: private boolean isPNG(InputStream is) throws IOException {
200: is.reset();
201: byte buf[];
202: is.read(buf = new byte[8]);
203: int signatureBuffer[] = parseUnsigned(buf);
204: if ((signatureBuffer[0] == 137)
205: && // PNG signature
206: (signatureBuffer[1] == 80)
207: && (signatureBuffer[2] == 78)
208: && (signatureBuffer[3] == 71)
209: && (signatureBuffer[4] == 13)
210: && (signatureBuffer[5] == 10)
211: && (signatureBuffer[6] == 26)
212: && (signatureBuffer[7] == 10)) {
213: return true;
214: }
215: return false;
216: }
217:
218: private Dimension readGIFDimension(InputStream is)
219: throws IOException {
220: byte buf[];
221: is.read(new byte[3]); // GIF version
222: is.read(buf = new byte[2]); // width
223: int widthBuf[] = parseUnsigned(buf);
224: int width = (widthBuf[1] << 8) + widthBuf[0];
225: is.read(buf = new byte[2]); // height
226: int heightBuf[] = parseUnsigned(buf);
227: int height = (heightBuf[1] << 8) + heightBuf[0];
228: return new Dimension(width, height);
229: }
230:
231: private Dimension readPNGDimension(InputStream is)
232: throws IOException {
233: byte buf[];
234: is.read(new byte[4]); // length
235: is.read(new byte[4]); // type
236: is.read(buf = new byte[4]); // width
237: int widthBuf[] = parseUnsigned(buf);
238: int width = (widthBuf[0] << 24) + (widthBuf[1] << 16)
239: + (widthBuf[2] << 8) + widthBuf[3];
240: is.read(buf = new byte[4]); // height
241: int heightBuf[] = parseUnsigned(buf);
242: int height = (heightBuf[0] << 24) + (heightBuf[1] << 16)
243: + (heightBuf[2] << 8) + heightBuf[3];
244: return new Dimension(width, height);
245: }
246:
247: private static int[] parseUnsigned(byte[] src) {
248: int[] val = new int[src.length];
249: for (int i = 0; i < src.length; i++) {
250: val[i] = (src[i] < 0) && (src[i] >= -128) ? 256 + src[i]
251: : src[i];
252: }
253: return val;
254: }
255:
256: // -------------------------------------------------------------------------
257:
258: // public static void main(String[] args) {
259: // File buildFile = new File("e:/development/projects/CopyIcons/testscript.xml");
260: // Project p = new Project();
261: // p.setUserProperty("ant.file", buildFile.getAbsolutePath());
262: // p.init();
263: // ProjectHelper helper = ProjectHelper.getProjectHelper();
264: // p.addReference("ant.projectHelper", helper);
265: // helper.parse(p, buildFile);
266: // p.executeTarget(p.getDefaultTarget());
267: // }
268:
269: // -------------------------------------------------------------------------
270:
271: private static class ImageInfo {
272:
273: public static final int GIF = 1;
274: public static final int PNG = 2;
275:
276: private Dimension dim;
277: private int type;
278: private String path;
279: private String ext;
280:
281: public ImageInfo(Dimension dm, int tp) {
282: this (null, dm, tp);
283: }
284:
285: public ImageInfo(String pth, Dimension dm, int tp) {
286: path = pth;
287: dim = dm;
288: type = tp;
289: }
290:
291: public String getPath() {
292: return path;
293: }
294:
295: public void setPath(String pth) {
296: path = pth;
297: }
298:
299: public int getHeight() {
300: return dim.height;
301: }
302:
303: public int getWidth() {
304: return dim.width;
305: }
306:
307: public String getType() {
308: if (type == GIF) {
309: return "GIF";
310: } else if (type == PNG) {
311: return "PNG";
312: }
313: return "";
314: }
315:
316: public String getExt() {
317: return ext;
318: }
319:
320: public String setExt(String ex) {
321: return ext = ex;
322: }
323:
324: }
325:
326: private static class ProjectIconInfo {
327:
328: public String prjPath;
329: public List<ImageInfo> matchingIcons;
330: public List<ImageInfo> notmatchingIcons;
331:
332: public ProjectIconInfo(String pth, List<ImageInfo> mi,
333: List<ImageInfo> nmi) {
334: prjPath = pth;
335: matchingIcons = mi;
336: notmatchingIcons = nmi;
337: }
338:
339: }
340:
341: // -------------------------------------------------------------------------
342:
343: private void dumpListToHTML(List<ProjectIconInfo> lst) {
344: File reportFile = new File(destDir, "index.html");
345: FileOutputStream fos = null;
346: try {
347: fos = new FileOutputStream(reportFile);
348: } catch (FileNotFoundException ex) {
349: ex.printStackTrace();
350: }
351: PrintWriter pw = new PrintWriter(new BufferedOutputStream(fos));
352: pw.println("<html>");
353: pw.println("<head>");
354: pw
355: .println("<style type=\"text/css\">\nbody { font-family: Tahoma, Verdana, sans-serif }\n</style>");
356: pw.println("</head>");
357: pw.println("<body>");
358: pw.println("<h2>List of icons in NetBeans projects</h2>");
359: pw.println("<h3>NetBeans Source Root: " + baseDir + "<br/>");
360: pw.println("Destination Directory: " + destDir + "</h3>");
361: pw
362: .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. "
363: + "By replacing the image in Destination Directory under coresponding module and path you can prepare rebranded "
364: + "icons in paralel directory structure and then copy them over to NetBeans Source Root. Orig. Image is "
365: + "in the table just for comparison and reference what image was already changed.</p>");
366: for (Iterator<ProjectIconInfo> iter = prjIconInfoList
367: .iterator(); iter.hasNext();) {
368: ProjectIconInfo info = iter.next();
369: if (!showEmpty && info.matchingIcons.size() == 0) {
370: continue;
371: }
372: pw.println("<p>Module name: <b>" + info.prjPath
373: + "</b></p>");
374: pw.println("<p style=\"margin-left: 20px\">");
375: if (info.matchingIcons.size() == 0) {
376: pw.println("<i>--- No icons ---</i>");
377: pw.println("</p>");
378: continue;
379: }
380: pw
381: .println("<table width=\"80%\"border=\"1\" cellpadding=\"3\" cellspacing=\"0\">");
382: pw
383: .println("<tr><td><b>Resource Path</b></td>"
384: + "<td align=\"center\"><b> New Image </b></td>"
385: + "<td align=\"center\"><b> Orig. image </b></td>"
386: + "<td align=\"center\"><b> W x H </b></td>"
387: + "<td align=\"center\"><b> Extension </b></td>"
388: + "<td align=\"center\"><b> Real Type </b></td></tr>");
389: for (Iterator<ImageInfo> goodIter = info.matchingIcons
390: .iterator(); goodIter.hasNext();) {
391: ImageInfo goodInfo = goodIter.next();
392: String iconPath = goodInfo.getPath();
393: String copiedIconPath = info.prjPath + File.separator
394: + iconPath;
395: String originalIconPath = baseDir.getAbsolutePath()
396: + File.separator + info.prjPath
397: + File.separator + iconPath;
398: pw.println("<tr>");
399: pw.println("<td>");
400: pw.println("<a href=\"" + copiedIconPath + "\">"
401: + iconPath + "</a>");
402: pw.println("</td>");
403: pw.println("<td align=\"center\">");
404: pw.println("<img src=\"" + copiedIconPath + "\"/>"); // copied image
405: pw.println("</td>");
406: pw.println("<td align=\"center\">");
407: pw.println("<img src=\"file://" + originalIconPath
408: + "\"/>"); // original image
409: pw.println("</td>");
410: pw.println("<td align=\"center\">"
411: + goodInfo.getWidth() + " x "
412: + goodInfo.getHeight() + "</td>");
413: pw.println("<td align=\"center\">"
414: + goodInfo.getExt().toUpperCase() + "</td>");
415: if (!goodInfo.getExt().equalsIgnoreCase(
416: goodInfo.getType())) {
417: pw
418: .println("<td align=\"center\"><font color=\"Orange\">"
419: + goodInfo.getType()
420: + "</font></td>");
421: } else {
422: pw.println("<td align=\"center\">"
423: + goodInfo.getType() + "</td>");
424: }
425: pw.println("</tr>");
426: }
427: pw.println("</table>");
428: pw.println("</p>");
429: }
430: pw.println("</body>");
431: pw.println("</html>");
432: pw.flush();
433: pw.close();
434: log("---> Report was written to file: "
435: + reportFile.getAbsolutePath());
436: }
437:
438: private void copyToDestDir(List<ProjectIconInfo> prjInfoList) {
439: FileSet fs = null;
440: for (Iterator<ProjectIconInfo> iter = prjInfoList.iterator(); iter
441: .hasNext();) {
442: fs = new FileSet();
443: log("Setting basedir for fileset: " + baseDir,
444: Project.MSG_VERBOSE);
445: ProjectIconInfo prjIconInfo = iter.next();
446: fs.setDir(new File(baseDir, prjIconInfo.prjPath));
447: int numFilesToCopy = prjIconInfo.matchingIcons.size()
448: + prjIconInfo.notmatchingIcons.size();
449: for (Iterator<ImageInfo> matchInfoIter = prjIconInfo.matchingIcons
450: .iterator(); matchInfoIter.hasNext();) {
451: ImageInfo info = matchInfoIter.next();
452: log("Adding file to matching fileset: "
453: + info.getPath(), Project.MSG_VERBOSE);
454: fs.setIncludes(info.getPath());
455: }
456: for (Iterator<ImageInfo> notmatchInfoIter = prjIconInfo.notmatchingIcons
457: .iterator(); notmatchInfoIter.hasNext();) {
458: ImageInfo info = notmatchInfoIter.next();
459: log("Adding file to notmatching fileset: "
460: + info.getPath(), Project.MSG_VERBOSE);
461: fs.setIncludes(info.getPath());
462: }
463: if (numFilesToCopy > 0) {
464: Copy copy = (Copy) getProject().createTask("copy");
465: copy.addFileset(fs);
466: File dest = new File(destDir, prjIconInfo.prjPath);
467: dest.mkdir();
468: copy.setTodir(dest);
469: copy.init();
470: copy.setLocation(getLocation());
471: copy.execute();
472: }
473: }
474: }
475:
476: // -------------------------------------------------------------------------
477:
478: private void scanForProjectDirs(File fl) {
479: if (depth > userDepth)
480: return;
481: //if (isProjectDir(fl)) {
482: // projectDirList.add(fl);
483: //}
484: File allFiles[] = fl.listFiles(new FileFilter() {
485: public boolean accept(File pathname) {
486: if (pathname.isDirectory()) {
487: return true;
488: }
489: return false;
490: }
491: });
492: depth++;
493: for (File f : allFiles) {
494: if (isProjectDir(f)) {
495: // here could be some project exclusion logic
496: projectDirList.add(f);
497: log(f.toString(), Project.MSG_VERBOSE);
498: scanForProjectDirs(f);
499: } else {
500: scanForProjectDirs(f);
501: }
502: }
503: depth--;
504: }
505:
506: private boolean isProjectDir(File fl) {
507: File prjDirs[] = fl.listFiles(new FilenameFilter() {
508: public boolean accept(File dir, String name) {
509: if ("nbproject".equals(name)) {
510: return true;
511: }
512: return false;
513: }
514: });
515: if (prjDirs.length != 1) {
516: return false;
517: }
518: String prjFiles[] = prjDirs[0].list(new FilenameFilter() {
519: public boolean accept(File dir, String name) {
520: if ("project.xml".equals(name)) {
521: return true;
522: }
523: return false;
524: }
525: });
526: if (prjFiles.length != 1) {
527: return false;
528: }
529: return true;
530: }
531:
532: private void processProjectDir(File f) {
533: String prjPath = null;
534: //if (f.getAbsolutePath().equals(baseDir.getAbsolutePath())) {
535: // prjPath = "";
536: //} else {
537: prjPath = f.getAbsolutePath().substring(
538: baseDir.getAbsolutePath().length() + 1);
539: //}
540: log("Processing project dir: " + prjPath);
541: DirectoryScanner ds = new DirectoryScanner();
542: ds.setBasedir(f);
543: ds.setIncludes(getAsArray(iconIncludes));
544: ds.setExcludes(getAsArray(iconExcludes));
545: ds.setCaseSensitive(false);
546: ds.scan();
547: String[] files = ds.getIncludedFiles();
548: log(" Found " + files.length + " files in " + f);
549: List<ImageInfo> goodIcons = new ArrayList<ImageInfo>();
550: List<ImageInfo> badIcons = new ArrayList<ImageInfo>();
551: for (String file : files) {
552: String ext = file.substring(file.lastIndexOf('.') + 1);
553: if (ext.equalsIgnoreCase("gif")
554: || ext.equalsIgnoreCase("png")) {
555: File iconFile = new File(f, file);
556: try {
557: ImageInfo imageInfo = null;
558: imageInfo = readImageInfo(iconFile);
559: if (imageInfo != null) {
560: imageInfo.setPath(file);
561: imageInfo.setExt(ext);
562: int w = imageInfo.getWidth();
563: int h = imageInfo.getHeight();
564: if ((w == 8 && h == 8) || (w == 16 && h == 16)
565: || (w == 24 && h == 24)
566: || (w == 32 && h == 32)) {
567: goodIcons.add(imageInfo);
568: } else {
569: badIcons.add(imageInfo);
570: }
571: }
572: } catch (IOException ex) {
573: ex.printStackTrace();
574: }
575: }
576: }
577: ProjectIconInfo prjIconInfo = new ProjectIconInfo(prjPath,
578: goodIcons, badIcons);
579: prjIconInfoList.add(prjIconInfo);
580: }
581:
582: private String[] getAsArray(String s) {
583: List<String> list = new ArrayList<String>();
584: for (StringTokenizer stok = new StringTokenizer(s, ","); stok
585: .hasMoreTokens();) {
586: String token = stok.nextToken().trim();
587: list.add(token);
588: }
589: return list.toArray(new String[list.size()]);
590: }
591:
592: }
|