/******************************************************************************* * Copyright (c) 2007 Red Hat, Inc. * Distributed under license by Red Hat, Inc. All rights reserved. * This program is made available under the terms of the * Eclipse Public License v1.0 which accompanies this distribution, * and is available at http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Red Hat, Inc. - initial API and implementation ******************************************************************************/ package org.jboss.tools.seam.internal.core.scanner.xml; import java.util.HashSet; import java.util.Map; import java.util.Set; import java.util.StringTokenizer; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.IPath; import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jdt.core.IType; import org.eclipse.jdt.core.JavaModelException; import org.eclipse.jdt.internal.core.ModelUpdater; import org.jboss.tools.common.meta.XAttribute; import org.jboss.tools.common.meta.XModelEntity; import org.jboss.tools.common.model.XModel; import org.jboss.tools.common.model.XModelException; import org.jboss.tools.common.model.XModelObject; import org.jboss.tools.common.model.filesystems.impl.FolderImpl; import org.jboss.tools.common.model.plugin.ModelPlugin; import org.jboss.tools.common.model.project.ext.impl.ValueInfo; import org.jboss.tools.common.model.util.EclipseJavaUtil; import org.jboss.tools.common.model.util.EclipseResourceUtil; import org.jboss.tools.common.model.util.NamespaceMapping; import org.jboss.tools.jst.web.model.helpers.InnerModelHelper; import org.jboss.tools.jst.web.model.project.ext.store.XMLValueInfo; import org.jboss.tools.seam.core.ISeamNamespace; import org.jboss.tools.seam.core.ISeamProject; import org.jboss.tools.seam.core.ISeamXmlComponentDeclaration; import org.jboss.tools.seam.core.SeamCorePlugin; import org.jboss.tools.seam.core.event.ISeamValue; import org.jboss.tools.seam.core.event.ISeamValueString; import org.jboss.tools.seam.internal.core.SeamImport; import org.jboss.tools.seam.internal.core.SeamProperty; import org.jboss.tools.seam.internal.core.SeamValueList; import org.jboss.tools.seam.internal.core.SeamValueMap; import org.jboss.tools.seam.internal.core.SeamValueMapEntry; import org.jboss.tools.seam.internal.core.SeamValueString; import org.jboss.tools.seam.internal.core.SeamXmlComponentDeclaration; import org.jboss.tools.seam.internal.core.SeamXmlFactory; import org.jboss.tools.seam.internal.core.scanner.IFileScanner; import org.jboss.tools.seam.internal.core.scanner.LoadedDeclarations; import org.jboss.tools.seam.internal.core.scanner.ScannerException; /** * @author Viacheslav Kabanovich */ public class XMLScanner implements IFileScanner { private XModel model; private XModelObject o; public XMLScanner() {} /** * Returns true if file is probable component source - * has components.xml name or *.component.xml mask. * @param resource * @return */ public boolean isRelevant(IFile resource) { return resource.getName().equals("components.xml") || resource.getName().endsWith(".component.xml"); } /** * This method should be called only if isRelevant returns true; * Makes simple check if this java file contains annotation Name. * @param resource * @return */ public boolean isLikelyComponentSource(IFile f) { cleanState(); boolean isComponentSource = false; if(f.isSynchronized(IFile.DEPTH_ZERO) && f.exists()) { model = InnerModelHelper.createXModel(f.getProject()); if(model != null) { o = EclipseResourceUtil.getObjectByResource(model, f); if(o != null) { isComponentSource = o.getModelEntity().getName().startsWith("FileSeamComponent"); //$NON-NLS-1$ } } } if(!isComponentSource) { cleanState(); } return isComponentSource; } private void cleanState() { model = null; o = null; } /** * Returns list of components * @param f * @return * @throws ScannerException */ public LoadedDeclarations parse(IFile f, ISeamProject sp) throws ScannerException { model = InnerModelHelper.createXModel(f.getProject()); o = EclipseResourceUtil.getObjectByResource(model, f); return parse(o, f.getFullPath(), sp); } static Set<String> COMMON_ATTRIBUTES = new HashSet<String>(); static Set<String> INTERNAL_ATTRIBUTES = new HashSet<String>(); static { COMMON_ATTRIBUTES.add(ISeamXmlComponentDeclaration.NAME); COMMON_ATTRIBUTES.add(ISeamXmlComponentDeclaration.CLASS); COMMON_ATTRIBUTES.add(ISeamXmlComponentDeclaration.SCOPE); COMMON_ATTRIBUTES.add(ISeamXmlComponentDeclaration.PRECEDENCE); COMMON_ATTRIBUTES.add(ISeamXmlComponentDeclaration.INSTALLED); COMMON_ATTRIBUTES.add(ISeamXmlComponentDeclaration.AUTO_CREATE); COMMON_ATTRIBUTES.add(ISeamXmlComponentDeclaration.JNDI_NAME); COMMON_ATTRIBUTES.add(ISeamXmlComponentDeclaration.STARTUP); COMMON_ATTRIBUTES.add(ISeamXmlComponentDeclaration.STARTUP_DEPENDS); INTERNAL_ATTRIBUTES.add("NAME"); //$NON-NLS-1$ INTERNAL_ATTRIBUTES.add("EXTENSION"); //$NON-NLS-1$ INTERNAL_ATTRIBUTES.add("#comment"); //$NON-NLS-1$ } public LoadedDeclarations parse(XModelObject o, IPath source, ISeamProject sp) { NamespaceMapping nm = NamespaceMapping.load(o); if(o.getParent() instanceof FolderImpl) { IFile f = ResourcesPlugin.getWorkspace().getRoot().getFile(source); if(f != null && f.exists()) { try { ((FolderImpl)o.getParent()).updateChildFile(o, f.getLocation().toFile()); } catch (XModelException e) { ModelPlugin.getPluginLog().logError(e); } if(o.getParent() == null) { boolean b = isLikelyComponentSource(f); if(!b) return null; o = EclipseResourceUtil.getObjectByResource(o.getModel(), f); if(o == null) return null; } } } LoadedDeclarations ds = new LoadedDeclarations(); String fileEntity = o.getModelEntity().getName(); if(fileEntity.startsWith("FileSeamComponent") && !fileEntity.startsWith("FileSeamComponents")) { //$NON-NLS-1$ parseComponent(o, source, nm, sp, ds); return ds; } XModelObject[] os = o.getChildren(); for (int i = 0; i < os.length; i++) { XModelEntity componentEntity = os[i].getModelEntity(); if(componentEntity.getAttribute("class") != null) { //$NON-NLS-1$ parseComponent(os[i], source, nm, sp, ds); } else if(os[i].getModelEntity().getName().startsWith("SeamFactory")) { //$NON-NLS-1$ SeamXmlFactory factory = new SeamXmlFactory(); factory.setId(os[i]); factory.setSourcePath(source); factory.setName(new XMLValueInfo(os[i], ISeamXmlComponentDeclaration.NAME)); factory.setScope(new XMLValueInfo(os[i], ISeamXmlComponentDeclaration.SCOPE)); factory.setValue(new XMLValueInfo(os[i], "value")); //$NON-NLS-1$ factory.setMethod(new XMLValueInfo(os[i], "method")); //$NON-NLS-1$ ds.getFactories().add(factory); } else if(os[i].getModelEntity().getName().startsWith("SeamImport")) { //$NON-NLS-1$ String v = os[i].getAttributeValue("import"); if(v != null && v.length() > 0) { SeamImport s = new SeamImport(); s.setSeamPackage(v); ds.getImports().add(s); } } } return ds; } private void parseComponent(XModelObject c, IPath source, NamespaceMapping nm, ISeamProject sp, LoadedDeclarations ds) { SeamXmlComponentDeclaration component = new SeamXmlComponentDeclaration(); component.setSourcePath(source); component.setId(c); component.setName(new XMLValueInfo(c, getComponentAttribute(c))); if(isClassAttributeSet(c)) { component.setClassName(new XMLValueInfo(c, ISeamXmlComponentDeclaration.CLASS)); } else if(c.getModelEntity().getName().equals("FileSeamComponent12")) { //$NON-NLS-1$ component.setClassName(getImpliedClassName(c, source)); } else { String className = getDefaultClassName(c, nm, sp); if(className != null) { component.setClassName(className); component.setClassNameGuessed(true); } } component.setScope(new XMLValueInfo(c, ISeamXmlComponentDeclaration.SCOPE)); component.setPrecedence(new XMLValueInfo(c, ISeamXmlComponentDeclaration.PRECEDENCE)); component.setInstalled(new XMLValueInfo(c, ISeamXmlComponentDeclaration.INSTALLED)); component.setAutoCreate(new XMLValueInfo(c, ISeamXmlComponentDeclaration.AUTO_CREATE)); component.setJndiName(new XMLValueInfo(c, ISeamXmlComponentDeclaration.JNDI_NAME)); XAttribute[] attributes = c.getModelEntity().getAttributes(); for (int ia = 0; ia < attributes.length; ia++) { XAttribute a = attributes[ia]; String xml = a.getXMLName(); if(xml == null || xml.length() == 0 || "#comment".equals(xml)) continue; //$NON-NLS-1$ if(COMMON_ATTRIBUTES.contains(xml)) continue; if(INTERNAL_ATTRIBUTES.contains(xml)) continue; if(xml.indexOf(":") >= 0) continue; //$NON-NLS-1$ if(xml.startsWith("xmlns")) continue; //$NON-NLS-1$ SeamProperty p = new SeamProperty(); p.setId(xml); p.setName(new XMLValueInfo(c, "&" + a.getName())); //$NON-NLS-1$ p.setName(toCamelCase(xml, false)); SeamValueString v = new SeamValueString(); v.setId("value"); //$NON-NLS-1$ p.setValue(v); v.setValue(new XMLValueInfo(c, a.getName())); component.addProperty(p); } XModelObject[] properties = c.getChildren(); for (int j = 0; j < properties.length; j++) { XModelEntity entity = properties[j].getModelEntity(); SeamProperty p = new SeamProperty(); p.setId(properties[j]); p.setName(new XMLValueInfo(properties[j], "name")); //$NON-NLS-1$ String name = properties[j].getAttributeValue("name"); //$NON-NLS-1$ if(name == null) { SeamCorePlugin.getPluginLog().logWarning("Entity " + entity.getName() + " has no 'name' attribute"); continue; } String cname = toCamelCase(name, false); if(!cname.equals(name)) p.setName(cname); if(entity.getAttribute("value") != null) { //$NON-NLS-1$ //this is simple value; SeamValueString v = new SeamValueString(); v.setId(properties[j]); v.setValue(new XMLValueInfo(properties[j], "value")); //$NON-NLS-1$ p.setValue(v); } else { XModelObject[] entries = properties[j].getChildren(); if(entity.getChild("SeamListEntry") != null //$NON-NLS-1$ || "list".equals(entity.getProperty("childrenLoader"))) { //$NON-NLS-1$ //$NON-NLS-2$ //this is list value SeamValueList vl = new SeamValueList(); vl.setId(properties[j]); for (int k = 0; k < entries.length; k++) { SeamValueString v = new SeamValueString(); v.setId(entries[k]); v.setValue(new XMLValueInfo(entries[k], "value")); //$NON-NLS-1$ vl.addValue(v); } p.setValue(vl); //Sometimes there is an attribute for the same property SeamProperty pa = (SeamProperty)component.getProperty(cname); if(pa != null) { ISeamValue v = pa.getValue(); if(v instanceof ISeamValueString) { ISeamValueString s = (ISeamValueString)v; if(s.getValue() != null) { String ss = s.getValue().getValue(); if(ss != null && ss.length() > 0) { String[] qs = ss.split(","); for (int i = 0; i < qs.length; i++) { SeamValueString vi = new SeamValueString(); vi.setId(pa.getId()); ValueInfo info = new ValueInfo(); info.setValue(qs[i]); vi.setValue(info); vl.addValue(vi); } } } } } } else { //this is map value SeamValueMap vm = new SeamValueMap(); vm.setId(properties[j]); for (int k = 0; k < entries.length; k++) { SeamValueMapEntry e = new SeamValueMapEntry(); e.setId(entries[k]); SeamValueString key = new SeamValueString(); key.setId(entries[k]); key.setValue(new XMLValueInfo(entries[k], "key")); //$NON-NLS-1$ e.setKey(key); SeamValueString value = new SeamValueString(); value.setId(entries[k]); value.setValue(new XMLValueInfo(entries[k], "value")); //$NON-NLS-1$ e.setValue(value); vm.addEntry(e); } p.setValue(vm); } } component.addProperty(p); } ds.getComponents().add(component); } private String getComponentAttribute(XModelObject c) { if(c.getModelEntity().getAttribute("component-name") != null) { //$NON-NLS-1$ return "component-name"; //$NON-NLS-1$ } return ISeamXmlComponentDeclaration.NAME; } private boolean isClassAttributeSet(XModelObject c) { String value = c.getAttributeValue(ISeamXmlComponentDeclaration.CLASS); return value != null && value.length() > 0; } public static String getImpliedClassName(XModelObject c, IPath path) { if(EclipseResourceUtil.isJar(path.toString())) { String suffix = ".component"; //$NON-NLS-1$ String cn = c.getAttributeValue("name"); //$NON-NLS-1$ if(cn.endsWith(suffix)) cn = cn.substring(0, cn.length() - suffix.length()); XModelObject p = c.getParent(); while(p != null && p.getFileType() == XModelObject.FOLDER) { cn = p.getAttributeValue("name") + "." + cn; //$NON-NLS-1$ //$NON-NLS-2$ p = p.getParent(); } return cn; } else { IFile f = ResourcesPlugin.getWorkspace().getRoot().getFile(path); if(!f.exists()) return ""; //$NON-NLS-1$ IResource root = EclipseResourceUtil.getJavaSourceRoot(f.getProject()); if(!root.getLocation().isPrefixOf(f.getLocation())) return ""; //$NON-NLS-1$ String relative = f.getLocation().toString().substring(root.getLocation().toString().length()); String suffix = ".component.xml"; //$NON-NLS-1$ if(relative.endsWith(suffix)) { relative = relative.substring(0, relative.length() - suffix.length()); relative = relative.replace('\\', '/'); if(relative.startsWith("/")) relative = relative.substring(1); //$NON-NLS-1$ return relative.replace('/', '.'); } } return null; } /** * This is only limited to supported namespaces provided by seam. * @param c * @return */ public static String getDefaultClassName(XModelObject c, NamespaceMapping nm, ISeamProject sp) { String s = c.getModelEntity().getXMLSubPath(); int d = s.indexOf(':'); if(d < 0) return null; Map<String, Set<ISeamNamespace>> ns = sp == null ? null : sp.getNamespaces(); String namespace = s.substring(0, d); String tag = s.substring(d + 1); String packageName = "org.jboss.seam." + namespace; String name = toCamelCase(tag, true); if(nm != null) { String uri = nm.getURIForDefaultNamespace(namespace); if(uri != null && ns != null && !ns.isEmpty()) { Set<ISeamNamespace> set = ns.get(uri); if(set != null && !set.isEmpty()) for (ISeamNamespace n: set) { String pn = n.getPackage(); if(pn == null || pn.length() == 0) continue; String cn = pn + "." + name; packageName = pn; if(set.size() == 1) break; IJavaProject jp = EclipseResourceUtil.getJavaProject(sp.getProject()); IType type = null; try { type = EclipseJavaUtil.findType(jp, cn); } catch (JavaModelException e) { ModelPlugin.getPluginLog().logError(e); } if(type != null) { break; } } } } String className = packageName + "." + name; //$NON-NLS-1$ return className; } /** * Copied from org.jboss.seam.init.Initialization * @param hyphenated * @param initialUpper * @return */ private static String toCamelCase(String hyphenated, boolean initialUpper) { StringTokenizer tokens = new StringTokenizer(hyphenated, "-"); //$NON-NLS-1$ StringBuilder result = new StringBuilder( hyphenated.length() ); String firstToken = tokens.nextToken(); if (initialUpper) { result.append( Character.toUpperCase( firstToken.charAt(0) ) ) .append( firstToken.substring(1) ); } else { result.append(firstToken); } while ( tokens.hasMoreTokens() ) { String token = tokens.nextToken(); result.append( Character.toUpperCase( token.charAt(0) ) ) .append( token.substring(1) ); } return result.toString(); } }