2
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
4
* Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
6
* The contents of this file are subject to the terms of either the GNU
7
* General Public License Version 2 only ("GPL") or the Common
8
* Development and Distribution License("CDDL") (collectively, the
9
* "License"). You may not use this file except in compliance with the
10
* License. You can obtain a copy of the License at
11
* http://www.netbeans.org/cddl-gplv2.html
12
* or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
13
* specific language governing permissions and limitations under the
14
* License. When distributing the software, include this License Header
15
* Notice in each file and include the License file at
16
* nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
17
* particular file as subject to the "Classpath" exception as provided
18
* by Sun in the GPL Version 2 section of the License file that
19
* accompanied this code. If applicable, add the following below the
20
* License Header, with the fields enclosed by brackets [] replaced by
21
* your own identifying information:
22
* "Portions Copyrighted [year] [name of copyright owner]"
26
* The Original Software is NetBeans. The Initial Developer of the Original
27
* Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
28
* Microsystems, Inc. All Rights Reserved.
30
* If you wish your version of this file to be governed by only the CDDL
31
* or only the GPL Version 2, indicate your decision by adding
32
* "[Contributor] elects to include this software in this distribution
33
* under the [CDDL or GPL Version 2] license." If you do not indicate a
34
* single choice of license, a recipient has the option to distribute
35
* your version of this file under either the CDDL, the GPL Version 2 or
36
* to extend the choice of license to its licensees as provided above.
37
* However, if you add GPL Version 2 code and therefore, elected the GPL
38
* Version 2 license, then the option applies only if the new code is
39
* made subject to such option by the copyright holder.
43
* CatalogModelImpl.java
45
* Created on March 29, 2006, 6:03 PM
47
* To change this template, choose Tools | Template Manager
48
* and open the template in the editor.
51
package org.netbeans.modules.xml.retriever.catalog.impl;
54
import java.io.FileInputStream;
55
import java.io.FileNotFoundException;
56
import java.io.IOException;
57
import java.io.Reader;
58
import java.net.MalformedURLException;
60
import java.net.URISyntaxException;
61
import java.util.ArrayList;
62
import java.util.Collection;
63
import java.util.List;
64
import java.util.logging.Level;
65
import java.util.logging.Logger;
66
import javax.swing.text.Document;
67
import javax.xml.parsers.DocumentBuilderFactory;
68
import javax.xml.parsers.ParserConfigurationException;
69
import org.apache.xml.resolver.Catalog;
70
import org.apache.xml.resolver.CatalogManager;
71
import org.apache.xml.resolver.helpers.Debug;
72
import org.apache.xml.resolver.tools.CatalogResolver;
73
import org.netbeans.api.project.FileOwnerQuery;
74
import org.netbeans.api.project.Project;
75
import org.netbeans.editor.BaseDocument;
76
import org.netbeans.modules.xml.retriever.XMLCatalogProvider;
77
import org.netbeans.modules.xml.retriever.catalog.ProjectCatalogSupport;
78
import org.netbeans.modules.xml.retriever.catalog.Utilities;
79
import org.netbeans.modules.xml.xam.locator.CatalogModel;
80
import org.netbeans.modules.xml.xam.locator.CatalogModelException;
81
import org.netbeans.modules.xml.retriever.catalog.CatalogWriteModel;
82
import org.netbeans.modules.xml.retriever.catalog.CatalogWriteModelFactory;
83
import org.netbeans.modules.xml.retriever.catalog.ProjectCatalogSupport;
84
import org.netbeans.modules.xml.retriever.impl.Util;
85
import org.netbeans.modules.xml.xam.ModelSource;
86
import org.netbeans.spi.xml.cookies.DataObjectAdapters;
87
import org.openide.cookies.EditorCookie;
88
import org.openide.filesystems.FileObject;
89
import org.openide.filesystems.FileUtil;
90
import org.openide.loaders.DataObject;
91
import org.openide.loaders.DataObjectNotFoundException;
92
import org.openide.util.Exceptions;
93
import org.openide.util.Lookup;
94
import org.w3c.dom.DOMImplementation;
95
import org.w3c.dom.ls.DOMImplementationLS;
96
import org.w3c.dom.ls.LSInput;
97
import org.xml.sax.InputSource;
98
import org.xml.sax.SAXException;
104
public class CatalogModelImpl implements CatalogModel {
105
protected FileObject catalogFileObject = null;
106
private static Logger logger = Logger.getLogger(CatalogModelImpl.class.getName());
107
/** Creates a new instance of CatalogModelImpl */
108
public CatalogModelImpl(Project myProject) throws IOException{
109
assert(myProject != null);
110
this.catalogFileObject = Util.getProjectCatalogFileObject(myProject, false);
114
/** Creates a new instance of CatalogModelImpl */
115
public CatalogModelImpl(FileObject catalogFileObject) throws IOException{
116
assert(catalogFileObject != null);
117
this.catalogFileObject = catalogFileObject;
121
public CatalogModelImpl(){
126
* This constructor is for unit testing purpose only
128
public CatalogModelImpl(File tempFolder) throws IOException{
129
tempFolder = FileUtil.normalizeFile(tempFolder);
130
FileObject fo = FileUtil.toFileObject(tempFolder);
131
String fileName = CatalogWriteModel.PUBLIC_CATALOG_FILE_NAME+
132
CatalogWriteModel.CATALOG_FILE_EXTENSION;
133
this.catalogFileObject = FileUtil.createData(fo, fileName);
136
private boolean doFetch = true;
137
private boolean fetchSynchronous = false;
138
public synchronized ModelSource getModelSourceSynchronous(URI locationURI,
139
ModelSource modelSourceOfSourceDocument, boolean fetch) throws CatalogModelException {
140
ModelSource ms = null;
142
fetchSynchronous = true;
144
ms = getModelSource(locationURI, modelSourceOfSourceDocument);
145
} catch (CatalogModelException ex) {
150
fetchSynchronous = false;
156
public synchronized ModelSource getModelSource(URI locationURI,
157
ModelSource modelSourceOfSourceDocument) throws CatalogModelException {
158
logger.entering("CatalogModelImpl", "getModelSource", locationURI);
159
Exception exn = null;
160
ModelSource result = null;
161
//selects the correct cataog for use.
162
useSuitableCatalogFile(modelSourceOfSourceDocument);
163
if(isOrphan() && isLocalFile(locationURI)) {
164
return tryOrphanResolution(locationURI, modelSourceOfSourceDocument);
166
File absResourceFile = null;
167
FileObject fob = null;
168
if(modelSourceOfSourceDocument != null)
169
fob = (FileObject) modelSourceOfSourceDocument.getLookup().lookup(FileObject.class);
171
//try to resolve using project wide catalog
172
absResourceFile = resolveUsingCatalog(locationURI, fob);
173
} catch (IOException ex) {
175
} catch(CatalogModelException ex){
178
if( (absResourceFile == null) || (exn != null) ){
179
//means there was no entry found in catalog or relative path resolution
180
if(fetchSynchronous) { //request from CC, no one else should lookup system wide catalog
181
//check in the system wide catalog (Runtime tab), if entry found, return that
182
ModelSource rms = getModelSourceFromSystemWideCatalog(locationURI, modelSourceOfSourceDocument);
189
//we did not get any matching entry by conventional way..So try retrieve and cache
190
absResourceFile = retrieveCacheAndLookup(locationURI, fob);
192
} catch (IOException ex) {
193
throw new CatalogModelException(ex);
196
if(absResourceFile != null){
197
logger.finer("Found abs file res:"+absResourceFile);
198
File normalizedFile = org.openide.filesystems.FileUtil.normalizeFile(absResourceFile);
199
FileObject thisFileObj = org.openide.filesystems.FileUtil.toFileObject(normalizedFile);
200
boolean editable = isEditable(absResourceFile);
201
result = createModelSource(thisFileObj, editable);
202
}else if(exn!= null) {
203
throw new CatalogModelException(exn);
205
logger.exiting("CatalogModelImpl", "getModelSource", result);
209
private void useSuitableCatalogFile(ModelSource modelSourceOfSourceDocument) {
210
// if the modelSource's project has XMLCatalogProvider then use that to
211
// see which catalog file to use for this modelSource
212
if(modelSourceOfSourceDocument != null){
213
FileObject msfo = (FileObject) modelSourceOfSourceDocument.getLookup().
214
lookup(FileObject.class);
217
Project prj = FileOwnerQuery.getOwner(msfo);
220
XMLCatalogProvider catPovider = (XMLCatalogProvider) prj.getLookup().
221
lookup(XMLCatalogProvider.class);
222
if(catPovider == null)
224
URI caturi = catPovider.getCatalog(msfo);
227
URI prjuri = FileUtil.toFile(prj.getProjectDirectory()).toURI();
228
URI catFileURI = prjuri.resolve(caturi);
229
if(catFileURI == null)
231
File catFile = new File(catFileURI);
232
if(!catFile.isFile()){
234
catFile.createNewFile();
235
} catch (IOException ex) {
239
FileObject catFO = FileUtil.toFileObject(FileUtil.normalizeFile(catFile));
242
//assign new catalog file that needs to be used for resolution
243
this.catalogFileObject = catFO;
248
public ModelSource getModelSource(URI locationURI) throws CatalogModelException{
250
//the originating file does not belong to a project so dont use catalog lookup
251
//just use file resolution instead
252
return tryOrphanResolution(locationURI, null);
254
//just look in to the project catalog
255
return getModelSource(locationURI, null);
260
* This method must be overridden by the Unit testcase to return a special
261
* Document object for a FileObject.
263
private Document getDocument(FileObject modelSourceFileObject) throws CatalogModelException{
264
Document result = null;
266
DataObject dObject = DataObject.find(modelSourceFileObject);
267
EditorCookie ec = (EditorCookie)dObject.getCookie(EditorCookie.class);
268
Document doc = ec.openDocument();
269
assert(doc instanceof BaseDocument);
271
} catch (Exception dObjEx) {
272
throw new CatalogModelException(dObjEx);
279
* This method could be overridden by the Unit testcase to return a special
280
* ModelSource object for a FileObject with custom impl of classes added to the lookup.
281
* This is optional if both getDocument(FO) and createCatalogModel(FO) are overridden.
283
protected ModelSource createModelSource(final FileObject thisFileObj, boolean editable) throws CatalogModelException{
284
final ModelSource ms = Utilities.getModelSource(thisFileObj,editable);
289
protected CatalogModel createCatalogModel(FileObject fo) throws CatalogModelException{
290
return new CatalogModelFactoryImpl().getCatalogModel(fo);
294
private ModelSource tryOrphanResolution(URI locationURI, ModelSource modelSource){
295
logger.entering("CatalogModelImpl", "getModelSource", locationURI);
296
if(catalogFileObject == null){
298
if(locationURI.isAbsolute()){
299
//may be a local file URI so try creating a file
300
File file = new File(locationURI);
302
file = FileUtil.normalizeFile(file);
303
FileObject fo = FileUtil.toFileObject(file);
304
return createModelSource(fo, isEditable(file));
307
//a relative URI, try resolving relative
308
if(modelSource != null){
309
//source is needed for resolution
310
FileObject fo = (FileObject) modelSource.getLookup().lookup(FileObject.class);
311
File file = resolveRelativeURI(locationURI, fo);
313
file = FileUtil.normalizeFile(file);
314
FileObject fobj = FileUtil.toFileObject(file);
315
return createModelSource(fobj, isEditable(file));
319
}catch (Exception e){
326
private boolean isOrphan(){
327
if(catalogFileObject == null)
332
private boolean isLocalFile(URI locationURI) {
333
if(locationURI.isAbsolute() &&
334
locationURI.getScheme() != null &&
335
"file".equals(locationURI.getScheme()))
343
protected File resolveUsingCatalog(URI locationURI, FileObject sourceFileObject
344
) throws CatalogModelException, IOException {
345
logger.entering("CatalogModelImpl", "resolveUsingCatalog", locationURI);
346
if(locationURI == null)
349
result = resolveUsingPublicCatalog(locationURI);
352
if(sourceFileObject != null){
353
result = resolveRelativeURI(locationURI, sourceFileObject);
357
if( (locationURI.isAbsolute()) && locationURI.getScheme().equalsIgnoreCase("file")){
359
result = new File(locationURI);
361
logger.exiting("CatalogModelImpl", "resolveUsingCatalog",result);
364
throw new FileNotFoundException(locationURI.toString()+": is absolute but "+result.getAbsolutePath()+" Not Found.");
366
throw new CatalogModelException(locationURI.toString()+" : Entry is not a relative or absolute and catalog entry not found");
370
private File retrieveCacheAndLookup(URI locationURI, FileObject sourceFileObject) throws IOException, CatalogModelException{
372
if((locationURI.isAbsolute()) && locationURI.getScheme().toLowerCase().
373
startsWith("http") && !CatalogFileWrapperDOMImpl.TEST_ENVIRONMENT){
374
// for all http and https absolute URI, just attempt downloading the
375
// file using the retriever API and store in the private cache.
376
//do not attempt this for a test environment.
379
res = Util.retrieveAndCache(locationURI, sourceFileObject,!fetchSynchronous);
380
}catch (Exception e){//ignore all exceptions
383
//now attempt onec more
384
result = resolveUsingPublicCatalog(locationURI);
392
protected File resolveUsingPublicCatalog(URI locationURI) throws IOException, CatalogModelException{
394
if(catalogFileObject != null){
395
//look up in the catalog
396
File publicCatalogFile = FileUtil.toFile(catalogFileObject);
397
if(publicCatalogFile.isFile()){
398
//return if the file content is empty or just start and end tags
399
if(publicCatalogFile.length() < 20)
401
URI strRes = resolveUsingApacheCatalog(publicCatalogFile, locationURI.toString());
403
if(strRes.isAbsolute()){
404
if(strRes.getScheme().equalsIgnoreCase("file")){
405
result = new File(strRes);
407
logger.exiting("CatalogModelImpl", "resolveUsingCatalog",result);
410
throw new FileNotFoundException(result.getAbsolutePath()+" Not Found.");
412
File res = resolveProjectProtocol(strRes);
415
throw new CatalogModelException("Catalog contains non-file URI. Catalog Maps URI to a local file only.");
425
protected File resolveRelativeURI(URI locationURI, FileObject sourceFileObject) throws CatalogModelException, FileNotFoundException{
427
if(!locationURI.isAbsolute()){
428
//this might be a relative file location
429
if(sourceFileObject == null)
430
throw new CatalogModelException(locationURI.toString()+" : Entry is relative but base file now known. Pass base file to the factory");
431
File sourceFile = FileUtil.toFile(sourceFileObject);
433
//In case of layer.xml defined sourceFileObject, FileUtil.toFile returns null.
435
if (sourceFile != null) {
436
URI sourceFileObjectURI = sourceFile.toURI();
437
URI resultURI = sourceFileObjectURI.resolve(locationURI);
439
result = new File(resultURI);
440
} catch(Exception e){
441
throw new CatalogModelException(locationURI.toString()+" : Entry is relative but resolved entry is not absolute");
444
logger.exiting("CatalogModelImpl", "resolveUsingCatalog",result);
447
throw new FileNotFoundException(result.getAbsolutePath()+" Not Found.");
454
protected URI resolveUsingApacheCatalog(File catalogFile, String locationURI) throws IOException, CatalogModelException{
455
List<File> catalogFileList = new ArrayList<File>();
456
catalogFileList.add(catalogFile);
457
return resolveUsingApacheCatalog(catalogFileList, locationURI);
461
CatalogResolver catalogResolver;
462
Catalog apacheCatalogResolverObj;
463
protected URI resolveUsingApacheCatalog(List<File> catalogFileList, String locationURI) throws CatalogModelException, IOException {
464
if((logger.getLevel() != null) && (logger.getLevel().intValue() <= Level.FINER.intValue())){
465
Debug debug = CatalogManager.getStaticManager().debug;
466
debug.setDebug(logger.getLevel().intValue());
469
CatalogManager manager = new CatalogManager(null);
470
manager.setUseStaticCatalog(false);
471
manager.setPreferPublic(false);
472
catalogResolver = new CatalogResolver(manager);
473
apacheCatalogResolverObj = catalogResolver.getCatalog();
474
for(File catFile : catalogFileList){
475
apacheCatalogResolverObj.parseCatalog(catFile.toURL());
478
String result = null;
480
result = apacheCatalogResolverObj.resolveSystem(locationURI);
481
} catch (Exception ex) {
488
//This is a workaround for a bug in resolver module on windows.
489
//the String returned by resolver is not an URI style
490
result = Utilities.normalizeURI(result);
491
URI uri = new URI(result);
493
if(uri.getScheme().equalsIgnoreCase("file")){
494
StringBuffer resBuff = new StringBuffer(result);
495
result = resBuff.insert("file:".length(), "/").toString();
498
} catch (URISyntaxException ex) {
502
if(result.length() > 0 ){
504
URI res = new URI(result);
506
} catch (URISyntaxException ex) {
513
// long lastModTime = 0;
514
// protected boolean reparseRequired(List<File> catalogFileList){
515
// /* if((apacheCatalogResolverObj == null) || (lastModTime == 0)){
516
// //then parse always
517
// lastModTime = catalogFileList.get(0).lastModified(); //bother only public catalog for now
518
// //System.out.println("Parsing First time: "+lastModTime);
521
// if((apacheCatalogResolverObj != null) && (lastModTime != 0)){
522
// if(lastModTime < catalogFileList.get(0).lastModified()){
523
// //System.out.println("Parsing time diff Old: "+lastModTime+" New:"+catalogFileList.get(0).lastModified());
524
// lastModTime = catalogFileList.get(0).lastModified();
527
// //System.out.println("NOT Parsing time diff Old: "+lastModTime);
531
// //System.out.println("Parsing Otherwise: "+lastModTime);*/
536
boolean isEditable(File absResourceFile) {
541
public InputSource resolveEntity(String publicId, String systemId) throws SAXException, IOException {
543
return getInputSource(new URI(systemId));
544
} catch (CatalogModelException ex) {
545
throw new IOException(ex.getMessage());
546
} catch (URISyntaxException e){
547
throw new IOException("SystemID not a URL");
552
private InputSource getInputSource(URI locationURI) throws CatalogModelException, IOException {
553
logger.entering("CatalogModelImpl", "getInputSource", locationURI);
554
File absResourceFile = resolveUsingCatalog(locationURI, null);
555
logger.finer("Found abs file res:"+absResourceFile);
556
InputSource result = new InputSource(new FileInputStream(absResourceFile));
557
result.setSystemId(locationURI.toString());
558
logger.exiting("CatalogModelImpl", "getInputSource", result);
563
public LSInput resolveResource(String type, String namespaceURI, String publicId, String systemId, String baseURIStr) {
564
//check for sanity of the systemID
565
if((systemId == null) || (systemId.trim().length() <=0 ))
567
URI systemIdURI = null;
569
systemIdURI = new URI(systemId);
570
} catch (URISyntaxException ex) {
573
FileObject baseFO = null;
574
//get the resolver object
575
CatalogModel depRez = null;
577
baseFO = getFileObject(baseURIStr);
578
depRez = getResolver(baseFO);
579
} catch (CatalogModelException ex) {
581
} catch (IOException ex) {
586
ModelSource baseMS = null;
588
baseMS = createModelSource(baseFO, false);
589
} catch (CatalogModelException ex) {
591
//get the model source from it
592
ModelSource resultMS = null;
594
resultMS = depRez.getModelSource(systemIdURI, baseMS);
595
} catch (CatalogModelException ex) {
601
FileObject resultFob = (FileObject) resultMS.getLookup().lookup(FileObject.class);
602
if(resultFob == null)
605
File resultFile = FileUtil.toFile(resultFob);
606
if(resultFile == null)
608
//get URI out of file
609
URI resultURI = resultFile.toURI();
610
//create LSInput object
611
DOMImplementation domImpl = null;
613
domImpl = DocumentBuilderFactory.newInstance().newDocumentBuilder().getDOMImplementation();
614
} catch (ParserConfigurationException ex) {
617
DOMImplementationLS dols = (DOMImplementationLS) domImpl.getFeature("LS","3.0");
618
LSInput lsi = dols.createLSInput();
619
Reader is = getFileStreamFromDocument(resultFile);
621
lsi.setCharacterStream(is);
622
lsi.setSystemId(resultURI.toString());
627
private FileObject getFileObject(String baseURIStr) throws IOException{
628
if(baseURIStr == null)
632
baseURI = new URI(baseURIStr);
633
} catch (URISyntaxException ex) {
634
IOException ioe = new IOException();
638
if(baseURI.isAbsolute()){
639
if(baseURI.getScheme().equalsIgnoreCase("file")){ //NOI18N
640
File baseFile = null;
642
baseFile = new File(baseURI);
643
} catch(Exception e){
644
IOException ioe = new IOException();
648
baseFile = FileUtil.normalizeFile(baseFile);
649
FileObject baseFileObject = null;
651
baseFileObject = FileUtil.toFileObject(baseFile);
653
IOException ioe = new IOException();
657
return baseFileObject;
664
private CatalogModel getResolver(FileObject baseFileObject) throws CatalogModelException{
665
if(baseFileObject != null && FileOwnerQuery.getOwner(baseFileObject) != null) {
666
return CatalogWriteModelFactory.getInstance().getCatalogWriteModelForProject(baseFileObject);
672
private Reader getFileStreamFromDocument(File resultFile) {
673
FileObject fo = FileUtil.toFileObject(FileUtil.normalizeFile(resultFile));
675
DataObject dobj = null;
677
dobj = DataObject.find(fo);
678
} catch (DataObjectNotFoundException ex) {
681
if(dobj.isValid() && dobj.isModified()){
682
// DataObjectAdapters does not implement getByteStream
683
// so calling this here will effectively return null
684
return DataObjectAdapters.inputSource(dobj).getCharacterStream();
686
//return null so that the validator will use normal file path to access doc
694
protected File resolveProjectProtocol(URI strRes) {
696
Project prj = FileOwnerQuery.getOwner(this.catalogFileObject);
698
ProjectCatalogSupport pcs = (ProjectCatalogSupport) prj.getLookup().lookup(ProjectCatalogSupport.class);
699
if(pcs.isProjectProtocol(strRes)){
700
FileObject resFO = pcs.resolveProjectProtocol(strRes);
702
return FileUtil.toFile(resFO);
710
private ModelSource getModelSourceFromSystemWideCatalog(URI locationURI,
711
ModelSource modelSourceOfSourceDocument) {
712
if( locationURI == null)
716
Lookup.Template templ = new Lookup.Template(CatalogModel.class);
717
Lookup.Result res = Lookup.getDefault().lookup(templ);
718
Collection impls = res.allInstances();
719
for(Object obj : impls){
720
CatalogModel cm = (CatalogModel) obj;
721
return cm.getModelSource(locationURI,
722
modelSourceOfSourceDocument);
724
} catch (CatalogModelException ex) {