/* * The contents of this file are subject to the terms of the Common Development * and Distribution License (the License). You may not use this file except in * compliance with the License. * * You can obtain a copy of the License at http://www.netbeans.org/cddl.html * or http://www.netbeans.org/cddl.txt. * * When distributing Covered Code, include this CDDL Header Notice in each file * and include the License file at http://www.netbeans.org/cddl.txt. * If applicable, add the following below the CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * * The Original Software is NetBeans. The Initial Developer of the Original * Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun * Microsystems, Inc. All Rights Reserved. */ package org.netbeans.modules.gwt4nb; import java.awt.Image; import java.beans.PropertyChangeListener; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.io.PrintWriter; import java.net.URLDecoder; import java.util.ArrayList; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Set; import java.util.jar.JarEntry; import java.util.jar.JarInputStream; import java.util.logging.Level; import java.util.logging.Logger; import javax.swing.JOptionPane; import org.netbeans.api.java.classpath.ClassPath; import org.netbeans.api.java.project.JavaProjectConstants; import org.netbeans.api.project.Project; import org.netbeans.api.project.ProjectUtils; import org.netbeans.api.project.SourceGroup; import org.netbeans.api.project.ui.OpenProjects; import org.netbeans.modules.xml.catalog.spi.CatalogDescriptor; import org.netbeans.modules.xml.catalog.spi.CatalogListener; import org.netbeans.modules.xml.catalog.spi.CatalogReader; import org.openide.util.Exceptions; import org.openide.util.ImageUtilities; import org.openide.util.NbBundle; import org.xml.sax.EntityResolver; import org.xml.sax.InputSource; import org.xml.sax.SAXException; /** * Register Schema files to runtime tab. See * http://blogs.sun.com/vivek/entry/schema_based_code_completion_tutorial */ public class XSDCatalog implements CatalogReader, CatalogDescriptor, EntityResolver { private static final String UI_PUBLIC_ID = "urn:import:com.google.gwt.user.client.ui"; // NOI18N private static final String UI_URL = "nbres:/org/netbeans/modules/gwt4nb/resources/" + // NOI18N "com.google.gwt.user.client.ui.xsd"; // NOI18N private static final String UI_BINDER_PUBLIC_ID = "urn:ui:com.google.gwt.uibinder"; // NOI18N private static final String UI_BINDER_URL = "nbres:/org/netbeans/modules/gwt4nb/resources/" + // NOI18N "UiBinder.xsd"; // NOI18N private static final String XHTML_SYSTEM_ID = "http://dl.google.com/gwt/DTD/xhtml.ent"; // NOI18N private static final String XHTML_URL = "nbres:/org/netbeans/modules/gwt4nb/resources/xhtml.ent"; // NOI18N private static final String UI_IMPORT_PACKAGE = "urn:import:"; // NOI18N @Override public Iterator<String> getPublicIDs() { List<String> list = new ArrayList<String>(); list.add(UI_PUBLIC_ID); list.add(UI_BINDER_PUBLIC_ID); return list.listIterator(); } @Override public void refresh() { } @Override public String getSystemID(String publicId) { if (publicId.equals(UI_PUBLIC_ID)) { return UI_URL; } else if (publicId.equals(UI_BINDER_PUBLIC_ID)) { return UI_BINDER_URL; } else { return null; } } @Override public String resolveURI(String name) { return null; } @Override public String resolvePublic(String string) { return null; } @Override public void addCatalogListener(CatalogListener catalogListener) { } @Override public void removeCatalogListener(CatalogListener catalogListener) { } @Override public Image getIcon(int i) { return ImageUtilities.loadImage( "org/netbeans/modules/gwt4nb/gwticon.png"); // NOI18N } @Override public String getDisplayName() { return NbBundle.getMessage(XSDCatalog.class, "GWTCat"); // NOI18N } @Override public String getShortDescription() { return NbBundle.getMessage(XSDCatalog.class, "XMLCat"); // NOI18N } @Override public void addPropertyChangeListener(PropertyChangeListener pcl) { } @Override public void removePropertyChangeListener(PropertyChangeListener pcl) { } @Override public InputSource resolveEntity(String publicId, String systemId) throws SAXException, IOException { if (UI_PUBLIC_ID.equals(publicId)) { return new org.xml.sax.InputSource(UI_URL); } if (systemId != null && systemId.contains(UI_PUBLIC_ID)) { return new org.xml.sax.InputSource(UI_URL); } if (systemId != null && systemId.contains(UI_BINDER_PUBLIC_ID)) { return new org.xml.sax.InputSource(UI_BINDER_URL); } if (XHTML_SYSTEM_ID.equals(systemId)) { return new org.xml.sax.InputSource(XHTML_URL); } if (systemId != null && systemId.contains(UI_IMPORT_PACKAGE)) { File f = createXSD(systemId); if (f != null) { return new org.xml.sax.InputSource("file:" + f.getPath()); } } return null; } private static File createXSD(String originalPackagePath, List<String> elements) throws FileNotFoundException, IOException { File xsd = File.createTempFile("package", "xsd"); Logger.getLogger(XSDCatalog.class.getName()).log(Level.INFO, "Schema based code-completion created: {0}", xsd.getPath()); xsd.deleteOnExit(); PrintWriter pw = new PrintWriter(xsd); try { pw.println("<?xml version=\"1.0\"?>"); pw.println("<xs:schema"); pw.println(" xmlns:xs=\"http://www.w3.org/2001/XMLSchema\""); pw.println(" targetNamespace=\"urn:import:" + originalPackagePath + "\""); pw.println(" xmlns=\"urn:import:" + originalPackagePath + "\""); pw.println(" elementFormDefault=\"qualified\">"); pw.println(" <xs:complexType\n" + " name=\"ElementContentType\">\n" + "\n" + " <xs:choice\n" + " minOccurs=\"0\"\n" + " maxOccurs=\"unbounded\">\n" + " <xs:any\n" + " processContents=\"lax\" />\n" + " </xs:choice>\n" + "\n" + " <xs:anyAttribute\n" + " processContents=\"lax\" />\n" + " </xs:complexType>\n" + ""); for (String element : elements) { pw.println(" <xs:element"); pw.println(" name=\"" + element + "\""); pw.println(" type=\"ElementContentType\" />"); } pw.println("</xs:schema>"); } finally { pw.close(); } return xsd; } /** * Create a XSD schema from package */ private File createXSD(String packagePath) { packagePath = packagePath.split(":")[2].split("\\?")[0]; String originalPackagePath = packagePath; packagePath = packagePath.replaceAll("\\.", "\\/"); for (Project p : OpenProjects.getDefault().getOpenProjects()) { for (SourceGroup sg : ProjectUtils.getSources(p).getSourceGroups(JavaProjectConstants.SOURCES_TYPE_JAVA)) { String path = sg.getRootFolder().getPath() + "/" + packagePath; File folder = new File(path); if (folder.exists() && folder.isDirectory()) { try { ArrayList<String> elements = new ArrayList<String>(); for (File f : folder.listFiles()) { if (f.isFile() && f.getName().toLowerCase().endsWith(".java")) { elements.add(f.getName().split("\\.")[0]); } } return createXSD(originalPackagePath, elements); } catch (IOException ex) { Exceptions.printStackTrace(ex); } } } } ClassPath cp = ClassPath.getClassPath(OpenProjects.getDefault().getMainProject().getProjectDirectory(), ClassPath.COMPILE); for (ClassPath.Entry entr : cp.entries()) { try { if (entr.getURL().getFile().length() < 9) { continue; } String fname = URLDecoder.decode(entr.getURL().getFile().substring(6, entr.getURL().getFile().length() - 2)); File f = new File(fname); if (!f.exists()) { continue; } FileInputStream fis = new FileInputStream(fname); Set<String> classFound; try { classFound = getClasses(packagePath, fis); } finally { fis.close(); } ArrayList<String> elements = new ArrayList<String>(); if (classFound != null && !classFound.isEmpty()) { for (String clazz : classFound) { String[] splited = clazz.split("\\."); elements.add(splited[splited.length - 1]); } return createXSD(originalPackagePath, elements); } } catch (Exception ex) { Exceptions.printStackTrace(ex); } } return null; } public static Set<String> getClasses(String packageName, InputStream is) throws ClassNotFoundException, FileNotFoundException, IOException { HashSet<String> set = new HashSet<String>(); JarInputStream jarFile = new JarInputStream( is); JarEntry jarEntry; int level = packageName.split("/").length; while (true) { jarEntry = jarFile.getNextJarEntry(); if (jarEntry == null) { break; } String entryName = jarEntry.getName(); if ((entryName.startsWith(packageName)) && (entryName.endsWith(".class")) && !entryName.contains("$")) { if (entryName.split("/")[level].endsWith(".class")) { set.add((entryName. replaceAll("/", "\\."). substring(0, entryName.length() - 6))); } } } return set; } }