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: package org.netbeans.tests.xml;
042:
043: import java.beans.PropertyChangeEvent;
044: import java.beans.PropertyChangeListener;
045: import java.beans.PropertyVetoException;
046: import java.io.*;
047: import java.net.MalformedURLException;
048: import java.net.URL;
049: import java.util.Iterator;
050: import java.util.Random;
051: import javax.swing.text.BadLocationException;
052: import javax.swing.text.StyledDocument;
053: import org.netbeans.api.java.classpath.ClassPath;
054: import org.netbeans.jellytools.Bundle;
055: import org.netbeans.modules.xml.tax.cookies.TreeDocumentCookie;
056: import org.netbeans.tax.TreeDocument;
057: import org.netbeans.tax.TreeException;
058: import org.netbeans.tax.TreeNode;
059: import org.netbeans.tax.io.XMLStringResult;
060: import org.openide.cookies.EditorCookie;
061: import org.openide.cookies.SaveCookie;
062: import org.openide.filesystems.*;
063: import org.openide.filesystems.FileSystem.AtomicAction;
064: import org.openide.loaders.DataFolder;
065: import org.openide.loaders.DataObject;
066: import org.openide.loaders.DataObjectNotFoundException;
067: import org.openide.loaders.XMLDataObject;
068: import org.openide.modules.ModuleInfo;
069: import org.openide.util.Lookup;
070: import org.openide.util.NbBundle;
071: import org.openide.util.Lookup.Template;
072:
073: /**
074: * Provides the basic support for XML API tests.
075: * @author mschovanek
076: */
077: public abstract class AbstractTestUtil {
078: protected static boolean DEBUG = true;
079:
080: public static final String CATALOG_PACKAGE = "org.netbeans.modules.xml.catalog";
081: public static final String CORE_PACKAGE = "org.netbeans.modules.xml.core";
082: public static final String TAX_PACKAGE = "org.netbeans.modules.xml.tax";
083: public static final String TEXT_PACKAGE = "org.netbeans.modules.xml.text";
084: public static final String TOOLS_PACKAGE = "org.netbeans.modules.xml.tools";
085: public static final String TREE_PACKAGE = "org.netbeans.modules.xml.tree";
086:
087: //--------------------------------------------------------------------------
088: // * * * X M L * * *
089: //--------------------------------------------------------------------------
090:
091: /**
092: * Converts the TreeNode to a string.
093: */
094: public static String nodeToString(TreeNode node) {
095: try {
096: return XMLStringResult.toString(node);
097: } catch (TreeException te) {
098: return null;
099: }
100: }
101:
102: /**
103: * Converts the node to a string.
104: */
105: public static String nodeToString(Object node) {
106: return node.toString();
107: }
108:
109: //--------------------------------------------------------------------------
110: // * * * S T R I N G S * * *
111: //--------------------------------------------------------------------------
112:
113: /** Search-and-replace string matches to expression "begin.*end"
114: * @param original the original string
115: * @param begin the begin of substring to be find
116: * @param end the end of substring to be find
117: * @param replaceTo the substring to replace it with
118: * @return a new string with 1st occurrence replaced
119: */
120: public static String replaceString(String original, String begin,
121: String end, String replaceTo) {
122: int bi = original.indexOf(begin);
123: int ei = original.indexOf(end, bi) + end.length();
124: if (bi < 0 || ei < 0) {
125: return original;
126: } else {
127: return original.substring(0, bi) + replaceTo
128: + original.substring(ei, original.length());
129: }
130: }
131:
132: /**
133: * Removes first character occurence from string.
134: */
135: public static String removeChar(String str, char ch) {
136: int index = str.indexOf(ch);
137:
138: if (index > -1) {
139: StringBuffer sb = new StringBuffer(str).deleteCharAt(str
140: .indexOf(ch));
141: return new String(sb);
142: } else {
143: return str;
144: }
145: }
146:
147: /**
148: * Joins elemets delimited by delim.
149: */
150: public static String joinElements(String[] elements, String delim) {
151: if (elements == null) {
152: return null;
153: }
154:
155: String path = elements[0];
156: for (int i = 1; i < elements.length; i++) {
157: path += (delim + elements[i]);
158: }
159: return path;
160: }
161:
162: /**
163: * Returns last element.
164: */
165: public static final String lastElement(String string, String delim) {
166: int index = string.lastIndexOf(delim);
167: if (index == -1) {
168: return string;
169: } else {
170: return string.substring(index + 1);
171: }
172: }
173:
174: //--------------------------------------------------------------------------
175: // * * * S T R I N G L O C A L I Z A T I O N * * *
176: //--------------------------------------------------------------------------
177:
178: /** Get localized string, removes '&' and cuts parameters like{0} from the end.
179: * @param key key of localized value.
180: * @return localized value.
181: */
182: public final String getStringTrimmed(String key) {
183: return Bundle.getStringTrimmed(getBundel(), key);
184: }
185:
186: /** Get localized string.
187: * @param key key of localized value.
188: * @return localized value.
189: */
190: public final String getString(String key) {
191: return NbBundle.getMessage(this .getClass(), key);
192: }
193:
194: /** Get localized string by passing parameter.
195: * @param key key of localized value.
196: * @param param argument to use when formating the message
197: * @return localized value.
198: */
199: public final String getString(String key, Object param) {
200: return NbBundle.getMessage(this .getClass(), key, param);
201: }
202:
203: /** Get localized string by passing parameter.
204: * @param key key of localized value.
205: * @param param1 argument to use when formating the message
206: * @param param2 the second argument to use for formatting
207: * @return localized value.
208: */
209: public final String getString(String key, Object param1,
210: Object param2) {
211: return NbBundle
212: .getMessage(this .getClass(), key, param1, param2);
213: }
214:
215: /** Get localized character. Usually used on mnemonic.
216: * @param key key of localized value.
217: * @return localized value.
218: */
219: public final char getChar(String key) {
220: return NbBundle.getMessage(this .getClass(), key).charAt(0);
221: }
222:
223: private String getBundel() {
224: return this .getClass().getPackage().getName() + ".Bundle";
225: }
226:
227: //--------------------------------------------------------------------------
228: // * * * D A T A O B J E C T S * * *
229: //--------------------------------------------------------------------------
230:
231: /** Converts DataObject to String.
232: */
233: public static String dataObjectToString(DataObject dataObject)
234: throws IOException, BadLocationException {
235: EditorCookie editorCookie = (EditorCookie) dataObject
236: .getCookie(EditorCookie.class);
237:
238: if (editorCookie != null) {
239: StyledDocument document = editorCookie.openDocument();
240: if (document != null) {
241: return document.getText(0, document.getLength());
242: }
243: }
244: return null;
245: }
246:
247: /** Saves DataObject
248: */
249: public static void saveDataObject(DataObject dataObject)
250: throws IOException {
251: SaveCookie cookie = (SaveCookie) dataObject
252: .getCookie(SaveCookie.class);
253: if (cookie == null)
254: throw new IllegalStateException(
255: "Cannot save document without SaveCookie.");
256: cookie.save();
257: }
258:
259: //--------------------------------------------------------------------------
260: // * * * F I L E S Y S T E M S * * *
261: //--------------------------------------------------------------------------
262:
263: /**
264: * Mounts local directory
265: */
266: public static LocalFileSystem mountDirectory(File dir)
267: throws PropertyVetoException, IOException {
268: LocalFileSystem fs = new LocalFileSystem();
269: fs.setRootDirectory(dir);
270: Repository rep = Repository.getDefault();
271: FileSystem ffs = rep.findFileSystem(fs.getSystemName());
272: if (ffs != null) {
273: rep.removeFileSystem(ffs);
274: }
275: rep.addFileSystem(fs);
276: return fs;
277: }
278:
279: /**
280: * Opens the XML Document with the given package, name and extension
281: */
282: public static TreeDocument openXMLDocument(String aPackage,
283: String name, String ext) throws IOException {
284: DataObject dao = findDataObject(aPackage, name, ext);
285:
286: if (dao == null) {
287: throw new IOException(aPackage + "." + name + "." + ext
288: + " data object not found.");
289: }
290:
291: XMLDataObject xmlDataObject;
292: if (XMLDataObject.class.isInstance(dao)) {
293: xmlDataObject = (XMLDataObject) dao;
294: } else {
295: throw new IOException(aPackage + "." + name + "." + ext
296: + " data object is not XMLDataObject.");
297: }
298:
299: TreeDocumentCookie cookie = (TreeDocumentCookie) xmlDataObject
300: .getCookie(TreeDocumentCookie.class);
301: if (cookie == null) {
302: throw new IOException("Missing TreeDocumentCookie at "
303: + aPackage + "." + name + "." + ext);
304: }
305:
306: TreeDocument document = (TreeDocument) cookie.getDocumentRoot();
307: if (document == null) {
308: throw new IOException("Ivalid XML data object" + aPackage
309: + "." + name + "." + ext);
310: }
311:
312: return document;
313: }
314:
315: /**
316: * Deletes FileObject.
317: */
318: public static void deleteFileObject(FileObject fo)
319: throws IOException {
320: DataObject dataObject = DataObject.find(fo);
321: dataObject.getNodeDelegate().destroy();
322: }
323:
324: /**
325: * Finds DataFolder.
326: */
327: public static DataFolder findFolder(String aPackage)
328: throws Exception {
329: return (DataFolder) findDataObject(aPackage, null, null);
330: }
331:
332: /**
333: * Finds absolut path for FileObject.
334: */
335: public static String toAbsolutePath(FileObject fo) {
336: return FileUtil.toFile(fo).getAbsolutePath();
337: }
338:
339: /**
340: * Finds the DataObject with the given package, name and extension
341: */
342: public static DataObject findDataObject(String aPackage,
343: String name, String ext) throws DataObjectNotFoundException {
344: FileObject fo = null;
345: fo = Repository.getDefault().find(aPackage, name, ext);
346: if (fo == null) {
347: return null;
348: } else {
349: return DataObject.find(fo);
350: }
351: }
352:
353: /**
354: * Finds the DataObject with the given package, name and extension
355: */
356: public static FileObject findFileObject(String aPackage,
357: String name, String ext) {
358: return Repository.getDefault().find(aPackage, name, ext);
359: }
360:
361: /**
362: * Finds the DataObject with the given name in test's 'data' folder. The name of a resource is
363: * a "/"-separated path name that identifies the resource relatively to 'data' folder.<p />
364: * <i>e.g. "sub_dir/data.xml"</i>
365: */
366: public DataObject findData(String name)
367: throws DataObjectNotFoundException {
368: //Repository.getDefault().
369: String resName = this .getClass().getPackage().getName();
370: resName = resName.replace('.', '/');
371: resName += "/data/" + name;
372: FileObject fo = ClassPath.getClassPath(null, ClassPath.EXECUTE)
373: .findResource(resName);
374: if (fo == null) {
375: if (DEBUG) {
376: System.err
377: .println("Cannot find FileObject: " + resName);
378: }
379: return null;
380: } else {
381: return DataObject.find(fo);
382: }
383: }
384:
385: /**
386: * Finds the DataObject with the given name. The name of a resource is
387: * a "/"-separated path name that identifies the resource or Nbfs URL.
388: */
389: public static DataObject findDataObject(String name)
390: throws DataObjectNotFoundException {
391: FileObject fo = findFileObject(name);
392: if (fo == null) {
393: if (DEBUG) {
394: System.err.println("Cannot find FileObject: " + name);
395: }
396: return null;
397: } else {
398: return DataObject.find(fo);
399: }
400: }
401:
402: /**
403: * Finds the FileObject with the given name. The name of a resource is
404: * a "/"-separated path name that identifies the resource or Nbfs URL.
405: */
406: public static FileObject findFileObject(String name) {
407: FileObject fo = null;
408: if (name.startsWith("nbfs:")) {
409: try {
410: fo = URLMapper.findFileObject(new URL(name));
411: } catch (MalformedURLException mue) {
412: }
413: ;
414: } else {
415: fo = Repository.getDefault().findResource(name);
416: }
417: return fo;
418: }
419:
420: /**
421: * Finds the template with the given name.
422: */
423: public static DataObject getTemplate(String tname)
424: throws DataObjectNotFoundException {
425: FileObject fileObject = Repository.getDefault().findResource(
426: "Templates/" + tname);
427: if (fileObject == null) {
428: throw new IllegalArgumentException("Cannot find template: "
429: + tname);
430: }
431: return DataObject.find(fileObject);
432: }
433:
434: /**
435: * Creates new DataObject at the folder with given name from the template
436: * with the given tname.
437: */
438: public static DataObject newFromTemplate(String tname,
439: String folder, String name) throws IOException {
440: DataObject dataObject = getTemplate(tname);
441: DataFolder dataFolder = (DataFolder) findDataObject(folder);
442: return dataObject.createFromTemplate(dataFolder, name);
443: }
444:
445: /**
446: * Removes the DataObject with the given name. The name of a resource is
447: * a "/"-separated path name that identifies the resource or Nbfs URL.
448: */
449: public static boolean removeDocument(String name)
450: throws IOException {
451: DataObject dataObject = findDataObject(name);
452: if (dataObject != null) {
453: dataObject.delete();
454: return true;
455: } else {
456: return false;
457: }
458: }
459:
460: /**
461: * Creates new Data Object
462: */
463: public static DataObject createDataObject(DataFolder folder,
464: final String name, final String extension,
465: final String content) throws IOException {
466: final FileObject targetFolder = folder.getPrimaryFile();
467: FileSystem filesystem = targetFolder.getFileSystem();
468:
469: final FileObject[] fileObject = new FileObject[1];
470: AtomicAction fsAction = new AtomicAction() {
471: public void run() throws IOException {
472: FileObject fo = targetFolder
473: .createData(name, extension);
474: FileLock lock = null;
475: try {
476: lock = fo.lock();
477: OutputStream out = fo.getOutputStream(lock);
478: out = new BufferedOutputStream(out, 999);
479: Writer writer = new OutputStreamWriter(out, "UTF8"); // NOI18N
480: writer.write(content + '\n'); // NOI18N
481: writer.flush();
482: writer.close();
483:
484: // return DataObject
485: lock.releaseLock();
486: lock = null;
487:
488: fileObject[0] = fo;
489:
490: } finally {
491: if (lock != null)
492: lock.releaseLock();
493: }
494: }
495: };
496:
497: filesystem.runAtomicAction(fsAction);
498: return DataObject.find(fileObject[0]);
499: }
500:
501: //--------------------------------------------------------------------------
502: // * * * O T H E R * * *
503: //--------------------------------------------------------------------------
504:
505: /**
506: * Enbles <code>enable = true</code> or disables <code>enable = false</code> the module.
507: */
508: public static void switchModule(String codeName, boolean enable)
509: throws Exception {
510: String statusFile = "Modules/" + codeName.replace('.', '-')
511: + ".xml";
512: ModuleInfo mi = getModuleInfo(codeName);
513: /*
514: FileObject fo = findFileObject(statusFile);
515: Document document = XMLUtil.parse(new InputSource(fo.getInputStream()), false, false, null, EntityCatalog.getDefault());
516: //Document document = XMLUtil.parse(new InputSource(data.getPrimaryFile().getInputStream()), false, false, null, EntityCatalog.getDefault());
517: NodeList list = document.getElementsByTagName("param");
518:
519: for (int i = 0; i < list.getLength(); i++) {
520: Element ele = (Element) list.item(i);
521: if (ele.getAttribute("name").equals("enabled")) {
522: ele.getFirstChild().setNodeValue(enable ? "true" : "false");
523: break;
524: }
525: }
526:
527: FileLock lock = fo.lock();
528: OutputStream os = fo.getOutputStream(lock);
529: XMLUtil.write(document, os, "UTF-8");
530: lock.releaseLock();
531: os.close();
532: */
533:
534: // module is switched
535: if (mi.isEnabled() == enable) {
536: return;
537: }
538:
539: DataObject data = findDataObject(statusFile);
540: EditorCookie ec = (EditorCookie) data
541: .getCookie(EditorCookie.class);
542: StyledDocument doc = ec.openDocument();
543:
544: // Change parametr enabled
545: String stag = "<param name=\"enabled\">";
546: String etag = "</param>";
547: String enabled = enable ? "true" : "false";
548: String result;
549:
550: String str = doc.getText(0, doc.getLength());
551: int sindex = str.indexOf(stag);
552: int eindex = str.indexOf(etag, sindex);
553: if (sindex > -1 && eindex > sindex) {
554: result = str.substring(0, sindex + stag.length()) + enabled
555: + str.substring(eindex);
556: //System.err.println(result);
557: } else {
558: //throw new IllegalStateException("Invalid format of: " + statusFile + ", missing parametr 'enabled'");
559: // Probably autoload module
560: return;
561: }
562:
563: // prepare synchronization and register listener
564: final Waiter waiter = new Waiter();
565: final PropertyChangeListener pcl = new PropertyChangeListener() {
566: public void propertyChange(PropertyChangeEvent evt) {
567: if (evt.getPropertyName().equals("enabled")) {
568: waiter.notifyFinished();
569: }
570: }
571: };
572: mi.addPropertyChangeListener(pcl);
573:
574: // save document
575: doc.remove(0, doc.getLength());
576: doc.insertString(0, result, null);
577: ec.saveDocument();
578:
579: // wait for enabled propety change and remove listener
580: waiter.waitFinished();
581: mi.removePropertyChangeListener(pcl);
582: }
583:
584: /**
585: * Switch on all XML modules and returns <code>true</code> if change state of any module else <code>false</code>.
586: */
587: public static boolean switchAllXMLModules(boolean enable)
588: throws Exception {
589: boolean result = false;
590: Iterator it = Lookup.getDefault().lookup(
591: new Template(ModuleInfo.class)).allInstances()
592: .iterator();
593:
594: while (it.hasNext()) {
595: ModuleInfo mi = (ModuleInfo) it.next();
596: if (mi.getCodeNameBase().startsWith(
597: "org.netbeans.modules.xml.")
598: && (mi.isEnabled() != enable)) {
599: switchModule(mi.getCodeNameBase(), enable);
600: result = true;
601: }
602: }
603: return result;
604: }
605:
606: /**
607: * Returns module's info or <code>null</null>.
608: */
609: public static ModuleInfo getModuleInfo(String codeName) {
610: Iterator it = Lookup.getDefault().lookup(
611: new Template(ModuleInfo.class)).allInstances()
612: .iterator();
613:
614: while (it.hasNext()) {
615: ModuleInfo mi = (ModuleInfo) it.next();
616: // if (mi.getCodeNameBase().equals(codeName) && mi.isEnabled()) {
617: if (mi.getCodeNameBase().equals(codeName)) {
618: return mi;
619: }
620: }
621: return null;
622: }
623:
624: /**
625: * Returns <code>true</code> if module is enabled else <code>false</code>.
626: */
627: public static boolean isModuleEnabled(String codeName) {
628: ModuleInfo mi = getModuleInfo(codeName);
629: if (mi == null) {
630: throw new IllegalArgumentException("Invalid codeName: "
631: + codeName);
632: }
633:
634: return mi.isEnabled();
635: }
636:
637: protected static Random randomGenerator = new Random();
638:
639: /**
640: * Generates random integer.
641: */
642: public static int randomInt(int n) {
643: return randomGenerator.nextInt(n);
644: }
645:
646: // ************************
647: // * * * C L A S E S * * *
648: // ************************
649:
650: static class Waiter {
651: private boolean finished = false;
652:
653: /** Restarts Synchronizer.
654: */
655: public void start() {
656: finished = false;
657: }
658:
659: /** Wait until the task is finished.
660: */
661: public void waitFinished() {
662: if (!finished) {
663: synchronized (this ) {
664: while (!finished) {
665: try {
666: wait();
667: } catch (InterruptedException ex) {
668: }
669: }
670: }
671: }
672: }
673:
674: /** Notify all waiters that this task has finished.
675: */
676: public void notifyFinished() {
677: synchronized (this ) {
678: finished = true;
679: notifyAll();
680: }
681: }
682: }
683: }
|