1 /******************************************************************************* 2 * Copyright (c) 2009, 2013 IBM Corporation and others. 3 * 4 * This program and the accompanying materials 5 * are made available under the terms of the Eclipse Public License 2.0 6 * which accompanies this distribution, and is available at 7 * https://www.eclipse.org/legal/epl-2.0/ 8 * 9 * SPDX-License-Identifier: EPL-2.0 10 * 11 * Contributors: 12 * IBM Corporation - initial API and implementation 13 *******************************************************************************/ 14 package org.eclipse.pde.api.tools.internal; 15 16 import java.util.HashSet; 17 import java.util.Set; 18 import java.util.Stack; 19 20 import org.eclipse.core.runtime.CoreException; 21 import org.eclipse.pde.api.tools.internal.provisional.ApiDescriptionVisitor; 22 import org.eclipse.pde.api.tools.internal.provisional.IApiAnnotations; 23 import org.eclipse.pde.api.tools.internal.provisional.IApiDescription; 24 import org.eclipse.pde.api.tools.internal.provisional.descriptors.IElementDescriptor; 25 import org.eclipse.pde.api.tools.internal.provisional.descriptors.IFieldDescriptor; 26 import org.eclipse.pde.api.tools.internal.provisional.descriptors.IMethodDescriptor; 27 import org.eclipse.pde.api.tools.internal.provisional.descriptors.IPackageDescriptor; 28 import org.eclipse.pde.api.tools.internal.provisional.descriptors.IReferenceTypeDescriptor; 29 import org.eclipse.pde.api.tools.internal.provisional.model.IApiComponent; 30 import org.eclipse.pde.api.tools.internal.util.Signatures; 31 import org.eclipse.pde.api.tools.internal.util.Util; 32 import org.w3c.dom.Document; 33 import org.w3c.dom.Element; 34 35 /** 36 * {@link IApiDescription} visitor that generates XML for the given 37 * {@link IApiComponent}. 38 * 39 * @since 1.0.0 40 */ 41 public class ApiDescriptionXmlCreator extends ApiDescriptionVisitor { 42 43 /** 44 * Component element 45 */ 46 private Element fComponent; 47 48 /** 49 * XML doc being generated 50 */ 51 private Document fDoc; 52 53 /** 54 * Current package node being created 55 */ 56 private Element fPackage; 57 58 /** 59 * Visibility modifiers for package being visited 60 */ 61 private int fPackageVisibility; 62 63 /** 64 * The stack of current type node being visited 65 */ 66 private Stack<Element> fTypeStack; 67 68 /** 69 * Set of package names already visited (to avoid re-visiting same package) 70 */ 71 private Set<String> fVisitedPackages; 72 73 /** 74 * Constructs a new visitor for the given component. 75 * 76 * @param component API component 77 * @throws CoreException if unable to construct the visitor 78 */ ApiDescriptionXmlCreator(IApiComponent component)79 public ApiDescriptionXmlCreator(IApiComponent component) throws CoreException { 80 this(component.getName(), component.getSymbolicName()); 81 } 82 83 /** 84 * Constructs a new visitor for the given component. 85 * 86 * @param componentName the given component name 87 * @param componentId the given component id 88 * 89 * @throws CoreException if unable to construct the visitor 90 */ ApiDescriptionXmlCreator(String componentName, String componentId)91 public ApiDescriptionXmlCreator(String componentName, String componentId) throws CoreException { 92 fDoc = Util.newDocument(); 93 fComponent = fDoc.createElement(IApiXmlConstants.ELEMENT_COMPONENT); 94 fComponent.setAttribute(IApiXmlConstants.ATTR_NAME, componentName); 95 fComponent.setAttribute(IApiXmlConstants.ATTR_VERSION, IApiXmlConstants.API_DESCRIPTION_CURRENT_VERSION); 96 fDoc.appendChild(fComponent); 97 Element plugin = fDoc.createElement(IApiXmlConstants.ELEMENT_PLUGIN); 98 plugin.setAttribute(IApiXmlConstants.ATTR_ID, componentId); 99 fComponent.appendChild(plugin); 100 fVisitedPackages = new HashSet<>(); 101 fTypeStack = new Stack<>(); 102 } 103 104 /** 105 * Annotates the attribute set of the specified {@link Element} 106 * 107 * @param componentContext component context to which the API applies, or 108 * <code>null</code> 109 * @param description the description to annotate from 110 * @param element the element to annotate 111 */ annotateElementAttributes(IApiAnnotations description, Element element)112 private void annotateElementAttributes(IApiAnnotations description, Element element) { 113 element.setAttribute(IApiXmlConstants.ATTR_RESTRICTIONS, Integer.toString(description.getRestrictions())); 114 int visibility = description.getVisibility(); 115 if (visibility != fPackageVisibility) { 116 element.setAttribute(IApiXmlConstants.ATTR_VISIBILITY, Integer.toString(visibility)); 117 } 118 } 119 120 @Override endVisitElement(IElementDescriptor element, IApiAnnotations description)121 public void endVisitElement(IElementDescriptor element, IApiAnnotations description) { 122 switch (element.getElementType()) { 123 case IElementDescriptor.PACKAGE: { 124 // A null package indicates there was an override for the 125 // package in a different context. 126 // Package rules are stored in the manifest, not the API 127 // description file. 128 // No need to add empty packages. 129 if (fPackage != null && fPackage.hasChildNodes()) { 130 fComponent.appendChild(fPackage); 131 } 132 fPackage = null; 133 break; 134 } 135 case IElementDescriptor.TYPE: { 136 fTypeStack.pop(); 137 break; 138 } 139 default: 140 break; 141 } 142 } 143 144 /** 145 * Returns the settings as a UTF-8 string containing XML. 146 * 147 * @return XML 148 * @throws CoreException if something goes wrong 149 */ getXML()150 public String getXML() throws CoreException { 151 return Util.serializeDocument(fDoc); 152 } 153 154 @Override visitElement(IElementDescriptor element, IApiAnnotations description)155 public boolean visitElement(IElementDescriptor element, IApiAnnotations description) { 156 switch (element.getElementType()) { 157 case IElementDescriptor.PACKAGE: { 158 IPackageDescriptor pkg = (IPackageDescriptor) element; 159 String pkgName = pkg.getName(); 160 if (fVisitedPackages.add(pkgName)) { 161 fPackage = fDoc.createElement(IApiXmlConstants.ELEMENT_PACKAGE); 162 fPackage.setAttribute(IApiXmlConstants.ATTR_NAME, pkgName); 163 // package visibility settings are stored in MANIFEST.MF, so 164 // omit them here. 165 // still keep track of the visibility to know if children 166 // should override 167 fPackageVisibility = description.getVisibility(); 168 fPackage.setAttribute(IApiXmlConstants.ATTR_VISIBILITY, Integer.toString(fPackageVisibility)); 169 fVisitedPackages.add(pkgName); 170 } 171 break; 172 } 173 case IElementDescriptor.TYPE: { 174 IReferenceTypeDescriptor typeDesc = (IReferenceTypeDescriptor) element; 175 fTypeStack.push(fDoc.createElement(IApiXmlConstants.ELEMENT_TYPE)); 176 Element type = fTypeStack.peek(); 177 annotateElementAttributes(description, type); 178 fPackage.appendChild(type); 179 type.setAttribute(IApiXmlConstants.ATTR_NAME, Signatures.getSimpleTypeName(typeDesc.getQualifiedName())); 180 break; 181 } 182 case IElementDescriptor.METHOD: { 183 IMethodDescriptor desc = (IMethodDescriptor) element; 184 Element method = fDoc.createElement(IApiXmlConstants.ELEMENT_METHOD); 185 Element type = fTypeStack.peek(); 186 // add standard attributes 187 annotateElementAttributes(description, method); 188 if (method.hasAttributes()) { 189 type.appendChild(method); 190 // add specific method attributes 191 method.setAttribute(IApiXmlConstants.ATTR_SIGNATURE, desc.getSignature()); 192 method.setAttribute(IApiXmlConstants.ATTR_NAME, desc.getName()); 193 } 194 break; 195 } 196 case IElementDescriptor.FIELD: { 197 IFieldDescriptor desc = (IFieldDescriptor) element; 198 Element field = fDoc.createElement(IApiXmlConstants.ELEMENT_FIELD); 199 Element type = fTypeStack.peek(); 200 annotateElementAttributes(description, field); 201 if (field.hasAttributes()) { 202 type.appendChild(field); 203 // add standard attributes 204 // add specific field attributes 205 field.setAttribute(IApiXmlConstants.ATTR_NAME, desc.getName()); 206 } 207 break; 208 } 209 default: 210 break; 211 } 212 return true; 213 } 214 } 215