org.codehaus.enunciate.modules.c.CDeploymentModule Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of enunciate-c Show documentation
Show all versions of enunciate-c Show documentation
The Enunciate C module generates the C client code for accessing the Enunciate services.
The newest version!
/*
* Copyright 2006-2008 Web Cohesion
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.codehaus.enunciate.modules.c;
import freemarker.template.*;
import net.sf.jelly.apt.decorations.JavaDoc;
import net.sf.jelly.apt.freemarker.FreemarkerJavaDoc;
import org.apache.commons.digester.RuleSet;
import org.codehaus.enunciate.EnunciateException;
import org.codehaus.enunciate.contract.jaxrs.ResourceMethod;
import org.codehaus.enunciate.modules.FacetAware;
import org.codehaus.enunciate.template.freemarker.AccessorOverridesAnotherMethod;
import org.codehaus.enunciate.config.SchemaInfo;
import org.codehaus.enunciate.apt.EnunciateFreemarkerModel;
import org.codehaus.enunciate.contract.validation.Validator;
import org.codehaus.enunciate.contract.jaxb.TypeDefinition;
import org.codehaus.enunciate.contract.jaxb.ElementDeclaration;
import org.codehaus.enunciate.contract.jaxb.RootElementDeclaration;
import org.codehaus.enunciate.contract.jaxb.LocalElementDeclaration;
import org.codehaus.enunciate.main.ClientLibraryArtifact;
import org.codehaus.enunciate.main.NamedFileArtifact;
import org.codehaus.enunciate.main.ArtifactType;
import org.codehaus.enunciate.modules.FreemarkerDeploymentModule;
import org.codehaus.enunciate.modules.c.config.CRuleSet;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.PrintStream;
import java.net.URL;
import java.util.*;
import java.util.regex.Pattern;
import com.sun.mirror.declaration.ClassDeclaration;
import org.codehaus.enunciate.template.freemarker.FindRootElementMethod;
import org.codehaus.enunciate.template.freemarker.ReferencedNamespacesMethod;
/**
* C Module
*
* The C module generates C data structures and (de)serialization functions that can be used in conjunction with libxml2
* to (de)serialize the REST resources as they are represented as XML data.
*
* The order of the C deployment module is 0, as it doesn't depend on any artifacts exported by any other module.
*
*
* - configuration
*
*
* Configuration
*
* The C module is configured with the "c" element under the "modules" element of the enunciate configuration file. It supports the following
* attributes:
*
*
* - The "label" attribute is the label for the C API. This is the name by which the file will be identified (producing [label].c).
* By default the label is the same as the Enunciate project label.
* - The "forceEnable" attribute is used to force-enable the C module. By default, the C module is enabled only if REST endpoints are found in the project.
* - The "enumConstantNamePattern" attribute defines the format string for
* converting an enum constant name to a unique c-style constant name. The arguments passed to the format string are: (1) the project label (2) the namespace id
* of the type definition (3) the name of the type definition (4) the NOT decapitalized annotation-specified client name of the type declaration
* (5) the decapitalized annotation-specified client name of the type declaration (6) the NOT-decapitalized simple name of the type declaration
* (7) the decapitalized simple name of the type declaration (8) the package name (9) the annotation-specified client name of the enum contant (10) the simple name of the enum constant. All tokens will
* be "scrubbed" by replacing any non-word character with the "_" character. The default value for this pattern is "%1$S_%2$S_%3$S_%9$S".
* - The "typeDefinitionNamePattern" attribute defines the format string for
* converting an type definition name to a unique c-style name. The arguments passed to the format string are: (1) the project label (2) the namespace id
* of the type definition (3) the name of the type definition (4) the NOT decapitalized annotation-specified client name of the type declaration
* (5) the decapitalized annotation-specified client name of the type declaration (6) the NOT-decapitalized simple name of the type declaration
* (7) the decapitalized simple name of the type declaration (8) the package name. All tokens will be "scrubbed" by replacing any non-word character with the "_" character. The default value for this
* pattern is "%1$s_%2$s_%3$s".
* - The 'separateCommonCode' attribute tells Enunciate to keep the code that is common to all Enunciate-generated projects separate from the code that is
* generated specifically for this project. Default: true.
*
*
* The "facets" element
*
* The "facets" element is applicable to the C module to configure which facets are to be included/excluded from the C artifacts. For
* more information, see API Facets
*
*
* @author Ryan Heaton
* @docFileName module_c.html
*/
public class CDeploymentModule extends FreemarkerDeploymentModule implements FacetAware {
/**
* The pattern to scrub is any non-word character.
*/
private static final Pattern SCRUB_PATTERN = Pattern.compile("\\W");
private boolean forceEnable = false;
private String label = null;
private String typeDefinitionNamePattern = "%1$s_%2$s_%3$s";
private String enumConstantNamePattern = "%1$S_%2$S_%3$S_%9$S";
private boolean separateCommonCode = true;
private Set facetIncludes = new TreeSet();
private Set facetExcludes = new TreeSet();
/**
* @return "c"
*/
@Override
public String getName() {
return "c";
}
/**
* Scrub a C identifier (removing any illegal characters, etc.).
*
* @param identifier The identifier.
* @return The identifier.
*/
public static String scrubIdentifier(String identifier) {
return identifier == null ? null : SCRUB_PATTERN.matcher(identifier).replaceAll("_");
}
@Override
public void doFreemarkerGenerate() throws IOException, TemplateException, EnunciateException {
File genDir = getGenerateDir();
if (!enunciate.isUpToDateWithSources(genDir)) {
EnunciateFreemarkerModel model = getModel();
String label = getLabel() == null ? getEnunciate().getConfig() == null ? "enunciate" : getEnunciate().getConfig().getLabel() : getLabel();
NameForTypeDefinitionMethod nameForTypeDefinition = new NameForTypeDefinitionMethod(getTypeDefinitionNamePattern(), label, model.getNamespacesToPrefixes());
model.put("nameForTypeDefinition", nameForTypeDefinition);
model.put("nameForEnumConstant", new NameForEnumConstantMethod(getEnumConstantNamePattern(), label, model.getNamespacesToPrefixes()));
TreeMap conversions = new TreeMap();
for (SchemaInfo schemaInfo : model.getNamespacesToSchemas().values()) {
for (TypeDefinition typeDefinition : schemaInfo.getTypeDefinitions()) {
if (typeDefinition.isEnum()) {
conversions.put(typeDefinition.getQualifiedName(), "enum " + nameForTypeDefinition.calculateName(typeDefinition));
}
else {
conversions.put(typeDefinition.getQualifiedName(), "struct " + nameForTypeDefinition.calculateName(typeDefinition));
}
}
}
ClientClassnameForMethod classnameFor = new ClientClassnameForMethod(conversions);
model.put("classnameFor", classnameFor);
model.put("cFileName", getSourceFileName());
model.put("separateCommonCode", isSeparateCommonCode());
model.put("findRootElement", new FindRootElementMethod());
model.put("referencedNamespaces", new ReferencedNamespacesMethod());
model.put("prefix", new PrefixMethod());
model.put("xmlFunctionIdentifier", new XmlFunctionIdentifierMethod());
model.put("accessorOverridesAnother", new AccessorOverridesAnotherMethod());
debug("Generating the C data structures and (de)serialization functions...");
URL apiTemplate = getTemplateURL("api.fmt");
processTemplate(apiTemplate, model);
}
else {
info("Skipping C code generation because everything appears up-to-date.");
}
ClientLibraryArtifact artifactBundle = new ClientLibraryArtifact(getName(), "c.client.library", "C Client Library");
NamedFileArtifact sourceScript = new NamedFileArtifact(getName(), "c.client", new File(getGenerateDir(), getSourceFileName()));
sourceScript.setArtifactType(ArtifactType.sources);
sourceScript.setPublic(false);
String description = readResource("library_description.fmt"); //read in the description from file
artifactBundle.setDescription(description);
artifactBundle.addArtifact(sourceScript);
if (isSeparateCommonCode()) {
NamedFileArtifact commonSourceHeader = new NamedFileArtifact(getName(), "c.common.client", new File(getGenerateDir(), "enunciate-common.c"));
commonSourceHeader.setPublic(false);
commonSourceHeader.setArtifactType(ArtifactType.sources);
commonSourceHeader.setDescription("Common code needed for all projects.");
artifactBundle.addArtifact(commonSourceHeader);
}
getEnunciate().addArtifact(artifactBundle);
}
/**
* Reads a resource into string form.
*
* @param resource The resource to read.
* @return The string form of the resource.
*/
protected String readResource(String resource) throws IOException, EnunciateException {
HashMap model = new HashMap();
ResourceMethod exampleResource = getModelInternal().findExampleResourceMethod();
model.put("filename", getSourceFileName());
String label = getLabel() == null ? getEnunciate().getConfig() == null ? "enunciate" : getEnunciate().getConfig().getLabel() : getLabel();
NameForTypeDefinitionMethod nameForTypeDefinition = new NameForTypeDefinitionMethod(getTypeDefinitionNamePattern(), label, getModelInternal().getNamespacesToPrefixes());
if (exampleResource != null) {
if (exampleResource.getEntityParameter() != null && exampleResource.getEntityParameter().getXmlElement() != null) {
ElementDeclaration el = exampleResource.getEntityParameter().getXmlElement();
TypeDefinition typeDefinition = null;
if (el instanceof RootElementDeclaration) {
typeDefinition = getModelInternal().findTypeDefinition((RootElementDeclaration) el);
}
else if (el instanceof LocalElementDeclaration && ((LocalElementDeclaration) el).getElementTypeDeclaration() instanceof ClassDeclaration) {
typeDefinition = getModelInternal().findTypeDefinition((ClassDeclaration) ((LocalElementDeclaration) el).getElementTypeDeclaration());
}
if (typeDefinition != null) {
model.put("input_element_name", nameForTypeDefinition.calculateName(typeDefinition));
}
}
if (exampleResource.getRepresentationMetadata() != null && exampleResource.getRepresentationMetadata().getXmlElement() != null) {
ElementDeclaration el = exampleResource.getRepresentationMetadata().getXmlElement();
TypeDefinition typeDefinition = null;
if (el instanceof RootElementDeclaration) {
typeDefinition = getModelInternal().findTypeDefinition((RootElementDeclaration) el);
}
else if (el instanceof LocalElementDeclaration && ((LocalElementDeclaration) el).getElementTypeDeclaration() instanceof ClassDeclaration) {
typeDefinition = getModelInternal().findTypeDefinition((ClassDeclaration) ((LocalElementDeclaration) el).getElementTypeDeclaration());
}
if (typeDefinition != null) {
model.put("output_element_name", nameForTypeDefinition.calculateName(typeDefinition));
}
}
}
URL res = CDeploymentModule.class.getResource(resource);
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
PrintStream out = new PrintStream(bytes);
try {
processTemplate(res, model, out);
out.flush();
bytes.flush();
return bytes.toString("utf-8");
}
catch (TemplateException e) {
throw new EnunciateException(e);
}
}
/**
* The name of the generated Ruby source file.
*
* @return The name of the generated Ruby source file.
*/
protected String getSourceFileName() {
String label = getLabel();
if (label == null) {
label = getEnunciate().getConfig() == null ? "enunciate" : getEnunciate().getConfig().getLabel();
}
return label + ".c";
}
@Override
protected ObjectWrapper getObjectWrapper() {
return new DefaultObjectWrapper() {
@Override
public TemplateModel wrap(Object obj) throws TemplateModelException {
if (obj instanceof JavaDoc) {
return new FreemarkerJavaDoc((JavaDoc) obj);
}
return super.wrap(obj);
}
};
}
/**
* Get a template URL for the template of the given name.
*
* @param template The specified template.
* @return The URL to the specified template.
*/
protected URL getTemplateURL(String template) {
return CDeploymentModule.class.getResource(template);
}
/**
* The label for the C API.
*
* @return The label for the C API.
*/
public String getLabel() {
return label;
}
/**
* The label for the C API.
*
* @param label The label for the C API.
*/
public void setLabel(String label) {
this.label = label;
}
/**
* The pattern for converting a type definition to a unique C-style type name.
*
* @return The pattern for converting a type definition to a unique C-style type name.
*/
public String getTypeDefinitionNamePattern() {
return typeDefinitionNamePattern;
}
/**
* The pattern for converting a type definition to a unique C-style type name.
*
* @param typeDefinitionNamePattern The pattern for converting a type definition to a unique C-style type name.
*/
public void setTypeDefinitionNamePattern(String typeDefinitionNamePattern) {
this.typeDefinitionNamePattern = typeDefinitionNamePattern;
}
/**
* The pattern for converting an enum constant to a unique C-style type name.
*
* @return The pattern for converting an enum constant to a unique C-style type name.
*/
public String getEnumConstantNamePattern() {
return enumConstantNamePattern;
}
/**
* The pattern for converting an enum constant to a unique C-style type name.
*
* @param enumConstantNamePattern The pattern for converting an enum constant to a unique C-style type name.
*/
public void setEnumConstantNamePattern(String enumConstantNamePattern) {
this.enumConstantNamePattern = enumConstantNamePattern;
}
/**
* Whether to require this module (force-enable it).
*
* @return Whether to require this module (force-enable it).
*/
public boolean isForceEnable() {
return forceEnable;
}
/**
* Whether to require this module (force-enable it).
*
* @param forceEnable Whether to require this module (force-enable it).
*/
public void setForceEnable(boolean forceEnable) {
this.forceEnable = forceEnable;
}
/**
* Whether to separate the common code from the project-specific code.
*
* @return Whether to separate the common code from the project-specific code.
*/
public boolean isSeparateCommonCode() {
return separateCommonCode;
}
/**
* Whether to separate the common code from the project-specific code.
*
* @param separateCommonCode Whether to separate the common code from the project-specific code.
*/
public void setSeparateCommonCode(boolean separateCommonCode) {
this.separateCommonCode = separateCommonCode;
}
@Override
public RuleSet getConfigurationRules() {
return new CRuleSet();
}
@Override
public Validator getValidator() {
return new CValidator();
}
/**
* The set of facets to include.
*
* @return The set of facets to include.
*/
public Set getFacetIncludes() {
return facetIncludes;
}
/**
* Add a facet include.
*
* @param name The name.
*/
public void addFacetInclude(String name) {
if (name != null) {
this.facetIncludes.add(name);
}
}
/**
* The set of facets to exclude.
*
* @return The set of facets to exclude.
*/
public Set getFacetExcludes() {
return facetExcludes;
}
/**
* Add a facet exclude.
*
* @param name The name.
*/
public void addFacetExclude(String name) {
if (name != null) {
this.facetExcludes.add(name);
}
}
// Inherited.
@Override
public boolean isDisabled() {
if (isForceEnable()) {
debug("C module is force-enabled via the 'require' attribute in the configuration.");
return false;
}
else if (super.isDisabled()) {
return true;
}
else if (getModelInternal() != null && getModelInternal().getNamespacesToSchemas().isEmpty()) {
debug("C module is disabled because there are no schema types.");
return true;
}
else if (getModelInternal() != null && getModelInternal().getRootResources().isEmpty()) {
debug("C module is disabled because there are no REST resources.");
return true;
}
return false;
}
}