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.modules.ant.freeform;
043:
044: import java.io.File;
045: import java.io.IOException;
046: import java.text.Collator;
047: import java.util.ArrayList;
048: import java.util.Collections;
049: import java.util.Iterator;
050: import java.util.List;
051: import java.util.Set;
052: import java.util.SortedSet;
053: import java.util.TreeSet;
054: import java.util.regex.Pattern;
055: import javax.swing.event.ChangeListener;
056: import org.apache.tools.ant.module.api.AntProjectCookie;
057: import org.apache.tools.ant.module.api.support.TargetLister;
058: import org.netbeans.api.project.Project;
059: import org.netbeans.modules.ant.freeform.spi.HelpIDFragmentProvider;
060: import org.openide.ErrorManager;
061: import org.openide.filesystems.FileObject;
062: import org.openide.filesystems.FileUtil;
063: import org.openide.loaders.DataObject;
064: import org.openide.loaders.DataObjectNotFoundException;
065: import org.openide.xml.XMLUtil;
066: import org.w3c.dom.Document;
067: import org.w3c.dom.Element;
068: import org.xml.sax.ErrorHandler;
069: import org.xml.sax.InputSource;
070: import org.xml.sax.SAXException;
071: import org.xml.sax.SAXParseException;
072:
073: /**
074: * Miscellaneous utilities.
075: * @author Jesse Glick
076: */
077: public class Util {
078:
079: private Util() {
080: }
081:
082: public static final ErrorManager err = ErrorManager.getDefault()
083: .getInstance("org.netbeans.modules.ant.freeform"); // NOI18N
084:
085: /**
086: * Returns name of the Ant script represented by the given file object.
087: * @param fo Ant script which name should be returned
088: * @return name of the Ant script as specified in name attribute of
089: * project element or null if fo does not represent valid Ant script
090: * or the script is anonymous
091: */
092: public static String getAntScriptName(FileObject fo) {
093: AntProjectCookie apc = getAntProjectCookie(fo);
094: if (apc == null) {
095: return null;
096: }
097: Element projEl = apc.getProjectElement();
098: if (projEl == null) {
099: return null;
100: }
101: String name = projEl.getAttribute("name"); // NOI18N
102: // returns "" if no such attribute
103: return name.length() > 0 ? name : null;
104: }
105:
106: private static AntProjectCookie getAntProjectCookie(FileObject fo) {
107: DataObject dob;
108: try {
109: dob = DataObject.find(fo);
110: } catch (DataObjectNotFoundException ex) {
111: Util.err.notify(ErrorManager.INFORMATIONAL, ex);
112: return null;
113: }
114: assert dob != null;
115: AntProjectCookie apc = dob.getCookie(AntProjectCookie.class);
116: if (apc == null && /* #88430 */fo.isData()) {
117: // Some file that *could* be an Ant script and just wasn't recognized
118: // as such? Cf. also TargetLister.getAntProjectCookie, which has the
119: // advantage of being inside the Ant module and therefore able to
120: // directly instantiate AntProjectSupport.
121: try {
122: apc = forceParse(fo);
123: } catch (IOException e) {
124: err.notify(ErrorManager.INFORMATIONAL, e);
125: } catch (SAXException e) {
126: err.log("Parse error in " + fo + ": " + e);
127: }
128: }
129: return apc;
130: }
131:
132: private static final Pattern VALIDATION = Pattern
133: .compile("([A-Za-z0-9])+"); // NOI18N
134:
135: public static String getMergedHelpIDFragments(Project p) {
136: List<String> fragments = new ArrayList<String>();
137:
138: for (HelpIDFragmentProvider provider : p.getLookup().lookupAll(
139: HelpIDFragmentProvider.class)) {
140: String fragment = provider.getHelpIDFragment();
141:
142: if (fragment == null
143: || !VALIDATION.matcher(fragment).matches()) {
144: throw new IllegalStateException(
145: "HelpIDFragmentProvider \""
146: + provider
147: + "\" provided invalid help ID fragment \""
148: + fragment + "\"."); // NOI18N
149: }
150:
151: fragments.add(fragment);
152: }
153:
154: Collections.sort(fragments);
155:
156: StringBuffer result = new StringBuffer();
157:
158: for (Iterator<String> i = fragments.iterator(); i.hasNext();) {
159: result.append(i.next());
160:
161: if (i.hasNext()) {
162: result.append('.');
163: }
164: }
165:
166: return result.toString();
167: }
168:
169: /**
170: * Returns sorted list of targets name of the Ant script represented by the
171: * given file object.
172: * @param fo Ant script which target names should be returned
173: * @return sorted list of target names or null if fo does not represent
174: * valid Ant script
175: */
176: public static List<String> getAntScriptTargetNames(FileObject fo) {
177: if (fo == null) {
178: throw new IllegalArgumentException(
179: "Cannot call Util.getAntScriptTargetNames with null"); // NOI18N
180: }
181: AntProjectCookie apc = getAntProjectCookie(fo);
182: if (apc == null) {
183: return null;
184: }
185: Set<TargetLister.Target> allTargets;
186: try {
187: allTargets = TargetLister.getTargets(apc);
188: } catch (IOException e) {
189: err.notify(ErrorManager.INFORMATIONAL, e);
190: return null;
191: }
192: SortedSet<String> targetNames = new TreeSet<String>(Collator
193: .getInstance());
194: for (TargetLister.Target target : allTargets) {
195: if (target.isOverridden()) {
196: // Cannot call it directly.
197: continue;
198: }
199: if (target.isInternal()) {
200: // Should not be called from outside.
201: continue;
202: }
203: targetNames.add(target.getName());
204: }
205: return new ArrayList<String>(targetNames);
206: }
207:
208: /**
209: * Try to parse a (presumably XML) file even though it is not known to be an Ant script.
210: */
211: private static AntProjectCookie forceParse(FileObject fo)
212: throws IOException, SAXException {
213: Document doc = XMLUtil.parse(new InputSource(fo.getURL()
214: .toExternalForm()), false, true, new ErrH(), null);
215: return new TrivialAntProjectCookie(fo, doc);
216: }
217:
218: private static final class ErrH implements ErrorHandler {
219: public ErrH() {
220: }
221:
222: public void fatalError(SAXParseException exception)
223: throws SAXException {
224: throw exception;
225: }
226:
227: public void error(SAXParseException exception)
228: throws SAXException {
229: throw exception;
230: }
231:
232: public void warning(SAXParseException exception)
233: throws SAXException {
234: // ignore that
235: }
236: }
237:
238: private static final class TrivialAntProjectCookie implements
239: AntProjectCookie.ParseStatus {
240:
241: private final FileObject fo;
242: private final Document doc;
243:
244: public TrivialAntProjectCookie(FileObject fo, Document doc) {
245: this .fo = fo;
246: this .doc = doc;
247: }
248:
249: public FileObject getFileObject() {
250: return fo;
251: }
252:
253: public File getFile() {
254: return FileUtil.toFile(fo);
255: }
256:
257: public Document getDocument() {
258: return doc;
259: }
260:
261: public Element getProjectElement() {
262: return doc.getDocumentElement();
263: }
264:
265: public boolean isParsed() {
266: return true;
267: }
268:
269: public Throwable getParseException() {
270: return null;
271: }
272:
273: public void addChangeListener(ChangeListener l) {
274: }
275:
276: public void removeChangeListener(ChangeListener l) {
277: }
278:
279: }
280:
281: }
|