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.compapp.projects.common;
043:
044: import java.io.File;
045: import java.io.IOException;
046: import java.net.URI;
047: import java.net.URISyntaxException;
048: import java.util.ArrayList;
049: import java.util.Arrays;
050: import java.util.List;
051: import java.util.Set;
052: import java.util.logging.Level;
053: import java.util.logging.Logger;
054: import org.netbeans.api.project.FileOwnerQuery;
055: import org.netbeans.api.project.Project;
056: import org.netbeans.api.project.ProjectUtils;
057: import org.netbeans.api.project.SourceGroup;
058: import org.netbeans.api.project.Sources;
059: import org.netbeans.modules.compapp.projects.common.CatalogWSDL.Entry;
060: import org.netbeans.modules.compapp.projects.common.CatalogWSDL.EntryType;
061: import org.netbeans.modules.xml.catalogsupport.DefaultProjectCatalogSupport;
062: import org.netbeans.modules.xml.catalogsupport.ProjectConstants;
063: import org.netbeans.modules.xml.catalogsupport.util.ProjectReferenceUtility;
064: import org.netbeans.modules.xml.retriever.catalog.CatalogWriteModel;
065: import org.netbeans.modules.xml.retriever.catalog.CatalogWriteModelFactory;
066: import org.netbeans.modules.xml.retriever.catalog.ProjectCatalogSupport;
067: import org.netbeans.modules.xml.xam.locator.CatalogModelException;
068: import org.netbeans.spi.project.SubprojectProvider;
069: import org.netbeans.spi.project.support.ant.AntProjectHelper;
070: import org.netbeans.spi.project.support.ant.PropertyUtils;
071: import org.netbeans.spi.project.support.ant.ReferenceHelper;
072: import org.openide.filesystems.FileObject;
073: import org.openide.filesystems.FileUtil;
074:
075: /**
076: * This class represents the main interface to support the implicit namespace
077: * reference resolver in SOA projects based on composite application project
078: * infrastructure.
079: * <p>
080: * This class implements ProjectCatalogSupport to support cross project wsdl/xsd
081: * import references as well as the implicit namespace references corresponding
082: * to the wsdl/xsd files.
083: * <p>
084: * Each SOA project(Service Unit deployment project for SE or BC), would register
085: * this implemenation in the Project's lookup in addition to the DefaultProjectCatalogSupport
086: * from the catalog support module to resolve the implicit references as well as the
087: * explicit references of the wsdl/xsd files.
088: * <p>
089: * SU project should use the ImplicitCatalogSupport(Project, AntProjectHelper,ReferenceHelper)
090: * constructor to create the instance to register it in the project's lookup and use
091: * ImplicitCatalogSupport.getInstance(Project) to get the instance of this class which looks up
092: * the instance from the project's lookup or create a new one to the user.
093: *
094: * <p><blockquote><pre>
095: * // registering the instance in project's lookup
096: * Lookups.fixed(new Object[] {
097: * ...,
098: * new DefaultProjectCatalogSupport(prj, antHelper, refHelper),
099: * ...,
100: * new ImplicitCatalogSupport(prj, antHelper, refHelper)
101: * });
102: * </blockquote></pre></p>
103: * <p><blockquote><pre>
104: * // get the instance of ImplicitCatalogSupport instance
105: * // using project object
106: * ImplicitCatalogSupport catSupport = ImplicitCatalogSupport.getInstance(prj);
107: * // or source file object
108: * ImplicitCatalogSupport catSupport = ImplicitCatalogSupport.getInstance(myXmlFO);
109: * // add the implicit reference to the catalog support
110: * URI uriRef = catSupport.createImplicitCatalogEntry(...);
111: *
112: * // resolve the implicit reference to the file object
113: * FileObject xsdFO =
114: * catSupport.resolveImplicitReference("http://ns/mynamespace", EntryType.XSD);
115: *
116: * </blockquote></pre></p>
117: * <p>
118: * @see CatalogWSDL
119: * @author chikkala
120: */
121: public class ImplicitCatalogSupport extends ProjectCatalogSupport {
122:
123: /** logger */
124: private static final Logger sLogger = Logger
125: .getLogger(ImplicitCatalogSupport.class.getName());
126: /** project that registered this instance in its lookup*/
127: private Project mProject;
128: /** ant project helper of the project */
129: private AntProjectHelper mAntPrjHelper;
130: /** project reference helper of the project */
131: private ReferenceHelper mPrjRefHelper;
132:
133: /**
134: * Constructor that will be used in adding this class instance in the project's lookup
135: * to support the Implicit wsdl/xsd references in this project's sources.
136: * @param project
137: * @param helper
138: * @param refHelper
139: */
140: public ImplicitCatalogSupport(Project project,
141: AntProjectHelper helper, ReferenceHelper refHelper) {
142: assert project != null;
143: this .mProject = project;
144: this .mAntPrjHelper = helper;
145: this .mPrjRefHelper = refHelper;
146: }
147:
148: /**
149: * Constructor that will be used to create a default instance in #getInstance
150: * implemenation if user did not register the instance of this class in project
151: * lookup support on a particular project.
152: * @see #getInstance
153: * @param project
154: */
155: protected ImplicitCatalogSupport(Project project) {
156: this (project, null, null);
157: }
158:
159: /**
160: * this will try to lookup the instance from the project's lookup. if not
161: * existing, will create a new instance.
162: * @param prj
163: * @return ImplicitCatalogSupport
164: */
165: public static ImplicitCatalogSupport getInstance(Project prj) {
166: Project owner = prj;
167: if (owner != null) {
168: ImplicitCatalogSupport support = (ImplicitCatalogSupport) owner
169: .getLookup().lookup(ImplicitCatalogSupport.class);
170: if (support != null) {
171: return support;
172: }
173: }
174: return new ImplicitCatalogSupport(owner);
175: }
176:
177: /**
178: * DefaultProjectCatalogSupport equivalent creation method.
179: * @param source
180: * @return ImplicitCatalogSupport
181: */
182: public static ImplicitCatalogSupport getInstance(FileObject source) {
183: return getInstance(FileOwnerQuery.getOwner(source));
184: }
185:
186: /**
187: * used to delegate most of the implementation to the DefaultProjectCatalog.
188: * @return DefaultProjectCatalogSupport
189: */
190: protected DefaultProjectCatalogSupport getDefaultProjectCatalogSupport() {
191: Project owner = this .mProject;
192: if (owner != null) {
193: DefaultProjectCatalogSupport support = (DefaultProjectCatalogSupport) owner
194: .getLookup().lookup(
195: DefaultProjectCatalogSupport.class);
196: if (support != null) {
197: return support;
198: }
199: }
200: if (this .mAntPrjHelper != null) {
201: return new DefaultProjectCatalogSupport(owner,
202: this .mAntPrjHelper, this .mPrjRefHelper);
203: } else {
204: return new DefaultProjectCatalogSupport(owner);
205: }
206: }
207:
208: /**
209: * delegates the method call to the default project catalog support
210: * @see org.netbeans.modules.xml.catalogsupport.ProjectCatalogSupport
211: * @param foTobeAddedInCat
212: * @return
213: */
214: @Override
215: public URI constructProjectProtocol(FileObject foTobeAddedInCat) {
216: return getDefaultProjectCatalogSupport()
217: .constructProjectProtocol(foTobeAddedInCat);
218: }
219:
220: /**
221: * delegates the method call to the default project catalog support
222: * @see org.netbeans.modules.xml.catalogsupport.ProjectCatalogSupport
223: * @param uriStoredInCatFile
224: * @return
225: */
226: @Override
227: public boolean isProjectProtocol(URI uriStoredInCatFile) {
228: return getDefaultProjectCatalogSupport().isProjectProtocol(
229: uriStoredInCatFile);
230: }
231:
232: /**
233: * delegates the method call to the default project catalog support
234: * @see org.netbeans.modules.xml.catalogsupport.ProjectCatalogSupport
235: * @param uriToBeResolved
236: * @return
237: */
238: @Override
239: public FileObject resolveProjectProtocol(URI uriToBeResolved) {
240: return getDefaultProjectCatalogSupport()
241: .resolveProjectProtocol(uriToBeResolved);
242: }
243:
244: /**
245: * This generates the catalog entry relative to the source file or relative
246: * to the project. @see #createCatalogEntry(FileObject) for more details on
247: * project relative catalog entry.
248: * @param source. If non null, creates a source relative entry. If null,
249: * creates a project relative entry.
250: * @param target target file object to which the reference is created.
251: * @return uri reference of the entry created.
252: * @throws org.netbeans.modules.xml.xam.locator.CatalogModelException
253: * @throws java.io.IOException
254: */
255: @Override
256: public URI createCatalogEntry(FileObject source, FileObject target)
257: throws CatalogModelException, IOException {
258: if (source == null) {
259: return createCatalogEntry(target);
260: } else {
261: return getDefaultProjectCatalogSupport()
262: .createCatalogEntry(source, target);
263: }
264: }
265:
266: /**
267: * removes the specified systemid uri entry
268: * @see org.netbeans.modules.xml.catalogsupport.ProjectCatalogSupport
269: * @param uri system id uri entry in the catalog.
270: * @return true if removed, false if it is not remove
271: * @throws java.io.IOException
272: */
273: @Override
274: public boolean removeCatalogEntry(URI uri) throws IOException {
275: return getDefaultProjectCatalogSupport()
276: .removeCatalogEntry(uri);
277: }
278:
279: /**
280: * creates a target entry into the catalog.xml. The target entry generated
281: * will be project relative either external or internal reference.
282: * @param target
283: * @return the systemId uri that was entered in the catalog for target.
284: * @throws org.netbeans.modules.xml.xam.locator.CatalogModelException
285: * @throws java.io.IOException
286: */
287: public URI createCatalogEntry(FileObject target)
288: throws CatalogModelException, IOException {
289: assert target != null;
290: CatalogWriteModel cwm = CatalogWriteModelFactory.getInstance()
291: .getCatalogWriteModelForProject(
292: this .mProject.getProjectDirectory());
293: assert cwm != null;
294: Project targetProject = FileOwnerQuery.getOwner(target);
295: URI systemId = null;
296: URI uriReference = null;
297:
298: systemId = generateSystemID(targetProject, target);
299: uriReference = generateURIReference(targetProject, target);
300:
301: if (systemId == null || uriReference == null) {
302: //TODO log info. why they are null.
303: return null;
304: }
305: cwm.addURI(systemId, uriReference);
306: return systemId;
307: }
308:
309: /**
310: * adds implicit catalog entry to the catalog support. An entry for the
311: * target with the namespace specified will be added to the catalog.xml and
312: * catalog.wsdl to resolve this implicit catalog entry.
313: *
314: * @param namespace
315: * @param target
316: * @param type
317: * @return
318: * @throws org.netbeans.modules.xml.xam.locator.CatalogModelException
319: * @throws java.io.IOException
320: */
321: public URI createImplicitCatalogEntry(String namespace,
322: FileObject target, CatalogWSDL.EntryType type)
323: throws CatalogModelException, IOException {
324: // update catalog.xml
325: URI systemId = createCatalogEntry(target);
326: // update catalog.wsdl
327: String location = systemId.toASCIIString();
328: Entry entry = null;
329: // create catalog wsdl entry object
330: if (EntryType.WSDL.equals(type)) {
331: entry = Entry.createWSDLEntry(namespace, location);
332: } else if (EntryType.XSD.equals(type)) {
333: entry = Entry.createXSDEntry(namespace, location);
334: } else {
335: entry = null;
336: }
337: // add the entry to catalog wsdl and save.
338: if (entry != null) {
339: CatalogWSDL catWSDL = CatalogWSDL
340: .loadCatalogWSDL(this .mProject);
341: catWSDL.addEntry(entry);
342: CatalogWSDL.saveCatalogWSDL(catWSDL, this .mProject);
343: } else {
344: //Log
345: sLogger
346: .fine("#### NO ENTRY WAS ADDED IN CatalogWSDL for namespace "
347: + namespace);
348: }
349: return systemId;
350: }
351:
352: /**
353: * removes implicit catalog entry from the catalog support. This only removes
354: * the catalog entry correpsonds to the namespace from the catalog.wsdl. It
355: * will not remove the entry in the catalog.xml corresponds to this namespace
356: * as that entry might be used by some other explicit refernce. To cleanup the
357: * unused catalog.xml entries users can use the project customizer.
358: *
359: * @param namespace
360: * @param type
361: * @return
362: * @throws org.netbeans.modules.xml.xam.locator.CatalogModelException
363: * @throws java.io.IOException
364: */
365: public boolean removeImplicitCatalogEntry(String namespace,
366: CatalogWSDL.EntryType type) throws CatalogModelException,
367: IOException {
368: CatalogWSDL catWSDL = CatalogWSDL
369: .loadCatalogWSDL(this .mProject);
370: Entry entry = catWSDL.getEntry(type, namespace, null);
371: boolean removed = catWSDL.removeEntry(entry);
372: CatalogWSDL.saveCatalogWSDL(catWSDL, this .mProject);
373: return removed;
374: }
375:
376: /**
377: * resolves the implicit reference to the file object using the implicit
378: * catalog support. It uses both the catalog.wsdl and the catalog.xml to
379: * resolve the reference. If the type is null, it returns the fileobject
380: * corresponding to the first entry with the namespace (wsdl or xsd).
381: *
382: * @param namespace namespace to lookup
383: * @param type wsdl or xsd file type correpsonding to the namespace
384: * @return fileobject correpsonding to the namespace or null
385: * @throws org.netbeans.modules.xml.xam.locator.CatalogModelException
386: * @throws java.io.IOException
387: */
388: public FileObject resolveImplicitReference(String namespace,
389: CatalogWSDL.EntryType type) throws CatalogModelException,
390: IOException {
391: FileObject refFO = null;
392: CatalogWSDL catWSDL = CatalogWSDL
393: .loadCatalogWSDL(this .mProject);
394: Entry entry = null;
395: if (type == null) {
396: List<Entry> entries = catWSDL.getEntries(namespace);
397: if (entries.size() > 0) {
398: entry = entries.get(0);
399: }
400: } else {
401: entry = catWSDL.getEntry(type, namespace, null);
402: }
403: if (entry != null) {
404: refFO = resolveImplicitReference(entry.getLocation());
405: }
406: return refFO;
407: }
408:
409: /**
410: * resolves the implicit reference to the file object using the implicit
411: * catalog support. It uses both the catalog.wsdl and the catalog.xml to
412: * resolve the reference.
413: *
414: * @param location location in the catalog entry
415: * @return fileobject correpsonding to the namespace or null.
416: * @throws org.netbeans.modules.xml.xam.locator.CatalogModelException
417: * @throws java.io.IOException
418: */
419: public FileObject resolveImplicitReference(String location)
420: throws CatalogModelException, IOException {
421: FileObject refFO = null;
422: if (location == null) {
423: return refFO;
424: }
425: try {
426: URI systemIdUri = new URI(location);
427: CatalogWriteModel cwm = CatalogWriteModelFactory
428: .getInstance().getCatalogWriteModelForProject(
429: this .mProject.getProjectDirectory());
430: URI uriReference = cwm.searchURI(systemIdUri);
431:
432: if (uriReference != null) {
433: refFO = resolveProjectProtocol(uriReference);
434: if (refFO == null) {
435: // it is not project protocol. so resolve it as project relative uri.
436: // uriReference.re
437: refFO = resolveRelativeReference(uriReference);
438: }
439: if (refFO == null) {
440: sLogger
441: .fine("Can not find file object for SystemId: "
442: + systemIdUri
443: + " URIReference: "
444: + uriReference + "in catalog.xml");
445: }
446: } else {
447: sLogger
448: .fine("Can not find the URI reference for SystemId"
449: + systemIdUri + "in catalog.xml");
450: }
451: } catch (URISyntaxException ex) {
452: sLogger.log(Level.FINE, ex.getMessage(), ex);
453: }
454: return refFO;
455: }
456:
457: /**
458: * checks if this instance supports the cross project references
459: * @return
460: */
461: public boolean supportsCrossProject() {
462: return this .mAntPrjHelper != null;
463: }
464:
465: /**
466: * returns the sub project references
467: * @return
468: */
469: public Set getProjectReferences() {
470: SubprojectProvider provider = (SubprojectProvider) this .mProject
471: .getLookup().lookup(SubprojectProvider.class);
472: return provider.getSubprojects();
473: }
474:
475: /**
476: * returns the file object in the same project represented by the relative
477: * uriReference from the project directory.
478: * @param uriReference
479: * @return
480: */
481: protected FileObject resolveRelativeReference(URI uriReference) {
482: File myPrjRoot = FileUtil.toFile(this .mProject
483: .getProjectDirectory());
484: File refFile = new File(myPrjRoot.toURI().resolve(uriReference));
485: FileObject refFO = FileUtil.toFileObject(FileUtil
486: .normalizeFile(refFile));
487: return refFO;
488: }
489:
490: /**
491: * generates the URI references w.r.t the target project for both internal
492: * or external file targets.
493: *
494: * @param targetProject
495: * @param target
496: * @return
497: */
498: protected URI generateURIReference(Project targetProject,
499: FileObject target) {
500: assert targetProject != null && target != null;
501:
502: if (this .mProject != targetProject) {
503: // external reference. generate uri reference with project protocol
504: if (!getProjectReferences().contains(targetProject)
505: && supportsCrossProject()) {
506: ProjectReferenceUtility.addProjectReference(
507: this .mPrjRefHelper, targetProject);
508: }
509: return constructProjectProtocol(target);
510: } else {
511: try {
512: // internal reference. generate the relative uri w.r.t. project root
513: String relativePath = FileUtil.getRelativePath(
514: this .mProject.getProjectDirectory(), target);
515: return new URI(relativePath);
516: } catch (URISyntaxException ex) {
517: //TODO: log the exception
518: sLogger.log(Level.FINE, ex.getMessage(), ex);
519: return null;
520: }
521: }
522: }
523:
524: /**
525: * generates the system id for the target file within or outside the target
526: * project w.r.t. the target project.
527: *
528: * @param targetProject
529: * @param target
530: * @return
531: */
532: protected URI generateSystemID(Project targetProject,
533: FileObject target) {
534: assert targetProject != null && target != null;
535: try {
536: if (this .mProject != targetProject) {
537:
538: // external reference. generate w.r.t. external project source root.
539: FileObject targetSourceFolder = getSourceFolder(
540: targetProject, target);
541: String projectName = getUsableProjectName(targetProject);
542: String relativePath = FileUtil.getRelativePath(
543: targetSourceFolder, target);
544: return new URI(projectName + "/" + relativePath);
545: } else {
546: // internal reference. generate w.r.t. source root
547: FileObject targetSourceFolder = getSourceFolder(target);
548: if (targetSourceFolder == null) {
549: throw new IllegalArgumentException(target.getPath()
550: + " is not in project source"); //NOI18N
551: }
552: String relativePath = FileUtil.getRelativePath(
553: targetSourceFolder, target);
554: return new URI(relativePath);
555: }
556: } catch (URISyntaxException ex) {
557: //TODO: log ex
558: sLogger.log(Level.FINE, ex.getMessage(), ex);
559: return null;
560: }
561: }
562:
563: /**
564: * usable project name
565: * @param project
566: * @return
567: */
568: private static String getUsableProjectName(Project project) {
569: return PropertyUtils.getUsablePropertyName(
570: ProjectUtils.getInformation(project).getName())
571: .replace('.', '_');
572: }
573:
574: /**
575: * return the source folder of the source file.
576: * @param source
577: * @return
578: */
579: private FileObject getSourceFolder(FileObject source) {
580: return getSourceFolder(this .mProject, source);
581: }
582:
583: /** supported sources from which a file reference can be resolved */
584: private static String[] sourceTypes = new String[] {
585: ProjectConstants.SOURCES_TYPE_XML,
586: ProjectConstants.SOURCES_TYPE_JAVA,
587: ProjectConstants.TYPE_DOC_ROOT,
588: ProjectConstants.TYPE_WEB_INF };
589:
590: /**
591: *
592: * @param project
593: * @param source
594: * @return
595: */
596: private static FileObject getSourceFolder(Project project,
597: FileObject source) {
598: Sources sources = ProjectUtils.getSources(project);
599: assert sources != null;
600: ArrayList<SourceGroup> sourceGroups = new ArrayList<SourceGroup>();
601: for (String type : sourceTypes) {
602: SourceGroup[] groups = sources.getSourceGroups(type);
603: if (groups != null) {
604: sourceGroups.addAll(Arrays.asList(groups));
605: }
606: }
607:
608: assert sourceGroups.size() > 0;
609: for (SourceGroup sourceGroup : sourceGroups) {
610: if (FileUtil
611: .isParentOf(sourceGroup.getRootFolder(), source)) {
612: return sourceGroup.getRootFolder();
613: }
614: }
615:
616: FileObject metaInf = project.getProjectDirectory()
617: .getFileObject("src/conf"); //NOI18N
618: if (metaInf != null) {
619: if (FileUtil.isParentOf(metaInf, source)) {
620: return metaInf;
621: }
622: }
623: return null;
624: }
625: }
|