/** * Copyright (c) 2006-2009 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are 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: * IBM - Initial API and implementation */ package org.eclipse.emf.exporter.html; import java.io.IOException; import java.io.OutputStream; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.StringTokenizer; import org.eclipse.emf.codegen.ecore.genmodel.GenPackage; import org.eclipse.emf.common.util.Diagnostic; import org.eclipse.emf.common.util.Monitor; import org.eclipse.emf.common.util.URI; import org.eclipse.emf.ecore.EAttribute; import org.eclipse.emf.ecore.EClass; import org.eclipse.emf.ecore.EClassifier; import org.eclipse.emf.ecore.EDataType; import org.eclipse.emf.ecore.EEnum; import org.eclipse.emf.ecore.EEnumLiteral; import org.eclipse.emf.ecore.EGenericType; import org.eclipse.emf.ecore.EModelElement; import org.eclipse.emf.ecore.ENamedElement; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.EOperation; import org.eclipse.emf.ecore.EPackage; import org.eclipse.emf.ecore.EParameter; import org.eclipse.emf.ecore.EReference; import org.eclipse.emf.ecore.EStructuralFeature; import org.eclipse.emf.ecore.ETypeParameter; import org.eclipse.emf.ecore.ETypedElement; import org.eclipse.emf.ecore.EcorePackage; import org.eclipse.emf.ecore.resource.URIConverter; import org.eclipse.emf.ecore.util.EcoreUtil; import org.eclipse.emf.exporter.ModelExporter; /** * <p>This example shows how the Export API and JET can be used to generate a * HTML documentation for Ecore packages.</p> * * <p>As any example, this code was <b>NOT</b> extensively tested and is not * necessary to cover all the possible nuances of Ecore models.</p> * * @since 2.2.0 */ public class HTMLExporter extends ModelExporter { public static String escape(String string) { return string == null ? "" : string.replace("&", "&").replace("<", "<").replace(">", ">").replace("\"", """); } private ModelExporter.ExportData exportData; private Map<EPackage, GenPackage> ePackageToGenPackage; private GenPackage currentGenPackage; private URI currentArtifactURI; private List<EStructuralFeature> defaultEStructuralFeatureDetails; @Override public String getID() { return "org.eclipse.emf.exporter.html"; } @Override protected String getDefaultArtifactLocation(EPackage ePackage) { return getDefaultArtifactFileName(ePackage) + ".html"; } @Override protected String doCheckEPackageArtifactLocation(String location, String packageName) { if (!location.endsWith(".html")) { return HTMLExporterPlugin.INSTANCE.getString("_UI_InvalidArtifactFileNameExtension_message"); } return super.doCheckEPackageArtifactLocation(location, packageName); } @Override protected Diagnostic doExport(Monitor monitor, ModelExporter.ExportData exportData) throws Exception { this.exportData = exportData; List<GenPackage> entries = new ArrayList<GenPackage>(exportData.genPackageToArtifactURI.keySet()); entries.addAll(exportData.referencedGenPackagesToArtifactURI.keySet()); ePackageToGenPackage = new HashMap<EPackage, GenPackage>(); for (GenPackage genPackage : entries) { ePackageToGenPackage.put(genPackage.getEcorePackage(), genPackage); } for (Map.Entry<GenPackage, URI> entry : exportData.genPackageToArtifactURI.entrySet()) { currentGenPackage = entry.getKey(); currentArtifactURI = entry.getValue(); String content = new PackageHTML().generate(this); save(content); } return Diagnostic.OK_INSTANCE; } public GenPackage getCurrentGenPackage() { return currentGenPackage; } public URI getPackageArtifacttURI(EPackage ePackage) { GenPackage eClassifierGenPackage = ePackageToGenPackage.get(ePackage); if (eClassifierGenPackage != null) { URI artifactURI = exportData.genPackageToArtifactURI.get(eClassifierGenPackage); if (artifactURI == null) { artifactURI = exportData.referencedGenPackagesToArtifactURI.get(eClassifierGenPackage); } if (artifactURI != null) { return artifactURI.deresolve(currentArtifactURI); } } return null; } public String computeClassifierLabel(EClassifier classifier) { StringBuilder label = new StringBuilder(); label.append("<a name=\""); label.append(classifier.getName()); label.append("\">"); label.append(classifier.getName()); label.append("</a>"); if (!classifier.getETypeParameters().isEmpty()) { label.append("<"); for (Iterator<ETypeParameter> i = classifier.getETypeParameters().iterator(); i.hasNext();) { ETypeParameter typeParameter = i.next(); label.append(computeTypeParameterLabel(typeParameter)); if (i.hasNext()) { label.append(", "); } } label.append(">"); } return label.toString(); } protected String computeTypeParameterLabel(ETypeParameter typeParameter) { StringBuilder label = new StringBuilder(); String name = typeParameter.getName(); EObject container = typeParameter.eContainer(); if (container instanceof EClassifier) { EClassifier classifier = (EClassifier)container; label.append("<a name=\""); label.append(classifier.getName()); label.append("@@"); label.append(name); label.append("\">"); label.append(name); label.append("</a>"); } else { label.append(name); } if (!typeParameter.getEBounds().isEmpty()) { label.append(" extends "); for (Iterator<EGenericType> j = typeParameter.getEBounds().iterator(); j.hasNext();) { EGenericType bound = j.next(); label.append(computeLabel(bound)); if (j.hasNext()) { label.append(" & "); } } } return label.toString(); } public String computeLabel(EClassifier classifier) { String name = classifier.getName().trim(); EPackage eClassifierEPackage = classifier.getEPackage(); String classifierType = classifier instanceof EClass ? "Class" : classifier instanceof EEnum ? "Enumeration" : "DataType"; if (getCurrentGenPackage().getEcorePackage() == eClassifierEPackage || getCurrentGenPackage().getEcorePackage().getNsURI().equals(eClassifierEPackage.getNsURI())) { return new StringBuffer() .append("<a href=\"#").append(name).append("\"") .append(" title=\"").append(classifierType).append(":").append(name) .append("\">") .append(name) .append("</a>") .toString(); } URI packageArtifactURI = getPackageArtifacttURI(eClassifierEPackage); if (packageArtifactURI != null) { return new StringBuffer() .append("<a href=\"").append(packageArtifactURI.toString()).append("#").append(name).append("\"") .append(" title=\"").append(classifierType).append(":").append(name) .append("\">") .append(name) .append("</a>") .toString(); } if (EcorePackage.eNS_URI.equals(eClassifierEPackage.getNsURI())) { return new StringBuffer() .append("<i><b>") .append(name) .append("</b></i>") .toString(); } return name; } /** * Returns the list of 'details', ie {@link EStructuralFeature}s, that should * presented for each {@link EClass}. * @return a list of {@link EStructuralFeature}s */ public List<EStructuralFeature> getDetails(EClass eClass) { List<EStructuralFeature> details = new ArrayList<EStructuralFeature>(); details.add(EcorePackage.Literals.ECLASS__ABSTRACT); details.add(EcorePackage.Literals.ECLASS__INTERFACE); return details; } /** * Returns the list of 'details', ie {@link EStructuralFeature}s, that should * presented for each {@link EDataType}. * @return a list of {@link EStructuralFeature}s */ public List<EStructuralFeature> getDetails(EDataType dataType) { List<EStructuralFeature> details = new ArrayList<EStructuralFeature>(); details.add(EcorePackage.Literals.ECLASSIFIER__INSTANCE_CLASS_NAME); details.add(EcorePackage.Literals.ECLASSIFIER__INSTANCE_TYPE_NAME); details.add(EcorePackage.Literals.EDATA_TYPE__SERIALIZABLE); return details; } protected String computeLabel(ENamedElement namedElement) { boolean addedHyperlink = false; StringBuilder label = new StringBuilder(); String titleType = namedElement.eClass().getName().substring(1); String name = namedElement.getName(); EObject container = namedElement.eContainer(); if (container instanceof EClassifier) { EClassifier classifier = (EClassifier)container; String classifierLabel = computeLabel(classifier); int index = classifierLabel.indexOf("href=\""); if (index > 0) { index = classifierLabel.indexOf('\"', index+"href=\"".length()); label.append(classifierLabel.substring(0, index)); if (namedElement instanceof ETypeParameter) { label.append("@@"); } else { label.append("@"); } label.append(name); } int currentPos = index; index = classifierLabel.indexOf("title=\"", currentPos); if (index > 0) { index += "title=\"".length(); label.append(classifierLabel.substring(currentPos, index)); label.append(titleType).append(':').append(classifier.getName()).append('.').append(name); currentPos = classifierLabel.indexOf('\"', index+1); } index = classifierLabel.indexOf('>', currentPos); if (index > 0) { addedHyperlink = true; label.append(classifierLabel.substring(currentPos, ++index)); } } label.append(name); if (addedHyperlink) { label.append("</a>"); } return label.toString(); } protected String computeLabel(EGenericType genericType) { EObject container = genericType.eContainer(); if (container == null || !container.eIsSet(genericType.eContainingFeature())) { return computeLabel(genericType.getERawType()); } else { StringBuilder label = new StringBuilder(); if (genericType.getEClassifier() != null) { label.append(computeLabel(genericType.getERawType())); if (!genericType.getETypeArguments().isEmpty()) { label.append("<"); for (Iterator<EGenericType> i = genericType.getETypeArguments().iterator(); i.hasNext();) { EGenericType typeArgument = i.next(); label.append(computeLabel(typeArgument)); if (i.hasNext()) { label.append(", "); } } label.append(">"); } } else { ETypeParameter typeParameter = genericType.getETypeParameter(); String name = typeParameter != null ? computeLabel(typeParameter) : "?"; label.append(name); if (genericType.getELowerBound() != null) { label.append(" super "); label.append(computeLabel(genericType.getELowerBound())); } else if (genericType.getEUpperBound() != null) { label.append(" extends "); label.append(computeLabel(genericType.getEUpperBound())); } } return label.toString(); } } public String computeTypedElementLabel(ETypedElement typedElement) { StringBuilder label = new StringBuilder(); EGenericType genericType = typedElement.getEGenericType(); if (genericType != null) { label.append(computeLabel(genericType)); if (typedElement.isMany()) { label.append("*"); } label.append(" "); } String name = typedElement.getName(); EObject container = typedElement.eContainer(); if (container instanceof EClassifier) { EClassifier classifier = (EClassifier)container; label.append("<a name=\""); label.append(classifier.getName()); label.append("@"); label.append(name); label.append("\">"); label.append(name); label.append("</a>"); } else { label.append(name); } return label.toString(); } /** * Returns the list of 'details', ie {@link EStructuralFeature}s, that should * presented for each {@link EAttribute}. * @return a list of {@link EStructuralFeature}s */ public List<EStructuralFeature> getDetails(EAttribute attribute) { List<EStructuralFeature> details = new ArrayList<EStructuralFeature>(getDefaultEStructuralFeatureDetails()); details.add(EcorePackage.Literals.EATTRIBUTE__ID); return details; } /** * Returns the list of 'details', ie {@link EStructuralFeature}s, that should * presented for each {@link EReference}. * @return a list of {@link EStructuralFeature}s */ public List<EStructuralFeature> getDetails(EReference reference) { List<EStructuralFeature> details = new ArrayList<EStructuralFeature>(getDefaultEStructuralFeatureDetails()); details.add(0,EcorePackage.Literals.EREFERENCE__CONTAINMENT); details.add(1,EcorePackage.Literals.EREFERENCE__CONTAINER); details.add(2,EcorePackage.Literals.EREFERENCE__RESOLVE_PROXIES); return details; } protected List<EStructuralFeature> getDefaultEStructuralFeatureDetails() { if (defaultEStructuralFeatureDetails == null) { defaultEStructuralFeatureDetails = new ArrayList<EStructuralFeature>(); defaultEStructuralFeatureDetails.add(EcorePackage.Literals.ETYPED_ELEMENT__LOWER_BOUND); defaultEStructuralFeatureDetails.add(EcorePackage.Literals.ETYPED_ELEMENT__UPPER_BOUND); defaultEStructuralFeatureDetails.add(EcorePackage.Literals.ESTRUCTURAL_FEATURE__CHANGEABLE); defaultEStructuralFeatureDetails.add(EcorePackage.Literals.ESTRUCTURAL_FEATURE__DEFAULT_VALUE); defaultEStructuralFeatureDetails.add(EcorePackage.Literals.ESTRUCTURAL_FEATURE__DERIVED); defaultEStructuralFeatureDetails.add(EcorePackage.Literals.ESTRUCTURAL_FEATURE__TRANSIENT); defaultEStructuralFeatureDetails.add(EcorePackage.Literals.ESTRUCTURAL_FEATURE__UNSETTABLE); defaultEStructuralFeatureDetails.add(EcorePackage.Literals.ESTRUCTURAL_FEATURE__VOLATILE); } return Collections.unmodifiableList(defaultEStructuralFeatureDetails); } public String computeEKeys(EReference reference) { if (!reference.getEKeys().isEmpty()) { StringBuilder label = new StringBuilder(); for (Iterator<EAttribute> i = reference.getEKeys().iterator(); i.hasNext();) { EAttribute attribute = i.next(); label.append(", ").append(computeLabel(attribute)); } return label.substring(", ".length()); } else { return ""; } } public String computeLabel(EOperation operation) { StringBuilder label = new StringBuilder(); if (!operation.getETypeParameters().isEmpty()) { label.append("<"); for (Iterator<ETypeParameter> i = operation.getETypeParameters().iterator(); i.hasNext(); ) { ETypeParameter typeParameter = i.next(); label.append(computeTypeParameterLabel(typeParameter)); if (i.hasNext()) { label.append(", "); } } label.append("> "); } if (operation.getEGenericType() == null) { label.append("void "); } label.append(computeTypedElementLabel(operation)); if (!operation.getEParameters().isEmpty()) { label.append("("); for (Iterator<EParameter> i = operation.getEParameters().iterator(); i.hasNext(); ) { EParameter parameter = i.next(); label.append(computeTypedElementLabel(parameter)); if (i.hasNext()) { label.append(", "); } } label.append(")"); } if (!operation.getEExceptions().isEmpty()) { label.append(" throws "); for (Iterator<EClassifier> i = operation.getEExceptions().iterator(); i.hasNext(); ) { EClassifier exception = i.next(); label.append(computeLabel(exception)); if (i.hasNext()) { label.append(", "); } } } return label.toString(); } /** * Returns the list of 'details', ie {@link EStructuralFeature}s, that should * presented for each {@link EOperation}. * @return a list of {@link EStructuralFeature}s */ public List<EStructuralFeature> getDetails(EOperation operation) { List<EStructuralFeature> details = new ArrayList<EStructuralFeature>(); details.add(EcorePackage.Literals.ETYPED_ELEMENT__LOWER_BOUND); details.add(EcorePackage.Literals.ETYPED_ELEMENT__UPPER_BOUND); return details; } public String computeLabel(EEnumLiteral enumLiteral) { StringBuilder label = new StringBuilder(); label.append(enumLiteral.getName()); return label.toString(); } /** * Returns the list of 'details', ie {@link EStructuralFeature}s, that should * presented for each {@link EEnumLiteral}. * @return a list of {@link EStructuralFeature}s */ public List<EStructuralFeature> getDetails(EEnumLiteral enumLiteral) { List<EStructuralFeature> details = new ArrayList<EStructuralFeature>(); details.add(EcorePackage.Literals.EENUM_LITERAL__LITERAL); details.add(EcorePackage.Literals.EENUM_LITERAL__VALUE); return details; } protected void save(String content) throws IOException { OutputStream outputStream = URIConverter.INSTANCE.createOutputStream(currentArtifactURI, null); outputStream.write(content.getBytes("UTF-8")); outputStream.close(); } /** * Returns a text that is suitable for displaying in HTML. This method analyzes the specified * <code>string</code>, checking whether it is already formated for HTML. If it is not, it replaces * line breaks by <br />. * @param string the string to be analyzed * @return a text that is suitable for displaying in HTML */ public String getLongText(String string) { if (string == null || string.length() == 0) { return ""; } // This is certainly not the most optimized way to do this... // if (string.contains("<p>") || string.contains("<br/>") || string.contains("<br />") || string.contains("<i>") || string.contains("<b>") || string.contains("<ul>") || string.contains("<ol>")) { return string; } // ...or this... // string = string.replace("\r\n", "\n").replace('\r', '\n'); StringBuilder sb = new StringBuilder("<p>"); for (StringTokenizer tokenizer = new StringTokenizer(string, "\n"); tokenizer.hasMoreElements();) { String line = tokenizer.nextElement().toString(); if (line != null) { sb.append(line).append("<br />\n"); } } return sb.append("</p>").toString(); } public String computeConstraints(EModelElement modelElement) { List<String> constraints = EcoreUtil.getConstraints(modelElement); if (!constraints.isEmpty()) { StringBuilder label = new StringBuilder(); for (String constraint : constraints) { label.append(", <tt>").append(constraint).append("</tt>"); } return label.substring(", ".length()); } else { return null; } } }