/**
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
/* @version $Rev: 452643 $ $Date: 2006-10-03 22:54:11 +0100 (Tue, 03 Oct 2006) $ */
package org.apache.tuscany.sca.cpp.tools.services;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.apache.tuscany.sca.cpp.tools.common.Headers;
import org.apache.tuscany.sca.cpp.tools.common.Signature;
import org.apache.tuscany.sca.cpp.tools.common.Utils;
import org.w3c.dom.Node;
/**
* This class will do the required processing for the <component>element of a
* sca composite file.
*/
public class ComponentDomNodeHandler extends GenericDomNodeHandler {
/**
* This method will do the "normal" processing and then trigger a call to
* processComponentNode.
*
* @param node
* the node being processed
* @param contextXPath
* the XPath to the node
* @param handlers
* the map pf element names to DomNodeHandlers
* @param parameters
* a map of XPaths to parameters values found so far
*/
public void handleNode(Node node, String contextXPath, Map handlers,
Map parameters) {
// Pick up attrs and the interface.cpp child elements
super.handleNode(node, contextXPath, handlers, parameters);
try {
//OK now go and create the wrapper and proxy for the service
processComponentNode(contextXPath, parameters);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* This method basically moved from the text names of things to operating on
* the actual Files. It will also verify or work out the correct class name
* for the implmentation and complain if this does match at least one
* potential service method in the class.
*
* @param contextXPath
* used to pull the correct values from the parameters map (as
* there can be multiple implementation.cpp elelements in there).
* @param parameters
* a map of XPath keys to attribute values
* @throws Exception
*/
private void processComponentNode(String contextXPath, Map parameters)
throws Exception {
String implHeader = (String) parameters.get(contextXPath
+ "/implementation.cpp/@header");
String implClass = (String) parameters.get(contextXPath
+ "/implementation.cpp/@class");
File compositeOrFragmentFile = (File) parameters
.get("compositeOrFragmentFile");
File implHeaderFile = null;
if (null != compositeOrFragmentFile) {
File dir = compositeOrFragmentFile.getParentFile();
implHeaderFile = new File(dir, implHeader);
} else {
throw new InternalError(
"Internal error: composite or fragment file not present in internal parameters");
}
try {
String resolvedImplClassName = getClassName(implHeaderFile,
implClass);
// Check or retrieve the impl Class name.
if (null == resolvedImplClassName) {
try {
//A class attribute was set but there were no methods of
// the
// class in the header
System.out
.println("Classname given ("
+ implClass
+ ") does not match any header file method's classes in file: "
+ implHeaderFile.getCanonicalPath());
} catch (IOException e) {
System.out
.println("Classname given ("
+ implClass
+ ") does not match any header file method's classes in file: "
+ implHeaderFile.getAbsolutePath());
}
return;
} else {
File target = (File) parameters.get("targetFile");
// go into the .componentType file and generate the cpp
processComponentTypeFile(implHeaderFile, target,
resolvedImplClassName);
}
} catch (Exception e) {
String compName = (String) parameters
.get("/compositeFragment/component/@name");
Utils
.screenMessage("Problem interpreting header or class attributes in "
+ compName
+ " component, in "
+ compositeOrFragmentFile.getPath() + " file");
System.exit(-2);
}
}
/**
* The purpose of this method is to move from the DOM parameters to dealing
* with the actual Files involved. It is from this method that we kick off
* the processing of the componentType file.
*
* @param header
* the implementation header
* @param target
* the directory for the output
* @param implClass
* @throws Exception
*/
private void processComponentTypeFile(File header, File target,
String implClass) throws Exception {
// The componentType files should be in the same dir as the Impl
// header...
if (header == null || target == null) {
return;
}
File componentTypeDirectory = header.getParentFile();
String headerFileName = header.getName();
String componentTypeName = headerFileName.substring(0, headerFileName
.lastIndexOf("."));
File componentTypeFile = new File(componentTypeDirectory,
componentTypeName + ".componentType");
ComponentTypeFileHandler ctParser = new ComponentTypeFileHandler();
// The implClass is used in the generated wrapper code so we need to
// store
// it so we can tunnel through the standard actOnFile signature.
int namespaceEnd = -1;
if (null != implClass) {
namespaceEnd = implClass.lastIndexOf("::");
}
String namespace = null;
if (-1 != namespaceEnd) {
namespace = implClass.substring(0, namespaceEnd);
ctParser.setParameter("implNamespace", namespace);
implClass = implClass.substring(namespaceEnd + 2);
}
if (implClass != null) {
ctParser.setParameter("implClass", implClass);
}
try {
ctParser.handleComponentTypeFile(componentTypeFile, target);
} catch (Exception e) {
Utils
.screenMessage("There has been a problem parsing the componentType file: "
+ componentTypeFile.getCanonicalPath());
Utils.screenMessage(" the reported errors is "
+ e.getLocalizedMessage());
Utils.screenMessage(" and the java exception stack is below.");
e.printStackTrace();
throw e;
}
}
/**
* The resolve and check the classname of the service. If we have an
* implementation class name we have to check that there is: at least one
* (non-private, non constructor or finalizer) method of that class in the
* header If there is no implementation class then we will return the class
* of the first non-private/constructor/finalizer method we find.
*
* @param header
* @param implementationCppClass
* @return
* @throws Exception
*/
private String getClassName(File header, String implementationCppClass)
throws Exception {
String methodClassName = null;
List methods = null;
if (null == header) {
return null;
}
Utils.postEvent(Utils.DEPLOYMENT_ARTEFACT_ENCOUNTERED, header
.getAbsolutePath());
Utils.postEvent(Utils.EVENT_TYPE_FILE_PARSED,
"Scagen processing C++ implementation header "
+ header.getAbsolutePath());
try {
Headers headers = new Headers();
headers.actOnFile(header, null, 1);
methods = headers.getAllMethods();
} catch (FileNotFoundException fnfe) {
String path;
try {
path = header.getCanonicalPath();
} catch (IOException e1) {
path = header.getPath();
}
Utils.screenMessage("The header file: " + path
+ " referenced cannot be found.");
throw fnfe;
} catch (Exception e) {
String path = header.getPath();
Utils.screenMessage("The header file: " + path
+ " referenced is not valid. Reason given is "
+ e.getLocalizedMessage());
throw e;
}
// We need at least some methods
if (null == methods) {
return null;
}
// We need at least one service method of to do anything
methods = trimMethodsOfPrivatesConstructorsAndDestrutors(methods);
if (null == methods || methods.size() == 0) {
return null;
}
// If the user specifies an implementation class then we need at
// least one service method of that class
if (implementationCppClass != null) {
methods = filterMethodsToOneClass(methods, implementationCppClass);
if (null == methods || methods.size() == 0) {
return null;
} else {
// There was at least one method of the correct type
return implementationCppClass;
}
} else {
// Implementation class is null so we return the fully qualified classname of the
// first service method
Signature s = (Signature) methods.get(0);
String className = s.getTrimClassName();
String namespace = s.getNamespace();
if( namespace != null && namespace.length() > 0)
{
className = namespace + "::" + className;
}
return className;
}
}
/**
* Filter the mthods supplied to only ones fo the supplied class.
*
* @param methods
* the list of methods
* @param implementationCppClass
* the class we wish
* @return a list of methods of the correct class
*/
private List filterMethodsToOneClass(List methods,
String implementationCppClass) {
if (null == methods) {
return null;
}
if (null == implementationCppClass
|| implementationCppClass.length() == 0) {
return null;
}
for (Iterator iter = methods.listIterator(); iter.hasNext();) {
Signature method = (Signature) iter.next();
String className = method.getTrimClassName();
String namespace = method.getNamespace();
if (namespace != null && namespace.length() > 0) {
className = namespace + "::" + className;
}
if (!implementationCppClass.equals(className)) {
iter.remove();
}
}
return methods;
}
/**
* This method removes contructor and destructor methods from the list.
*
* @param methods
* the list of methods
* @return
*/
private List trimMethodsOfPrivatesConstructorsAndDestrutors(List methods) {
if (null == methods) {
return null;
}
for (Iterator iter = methods.listIterator(); iter.hasNext();) {
Signature method = (Signature) iter.next();
if (method.isConstructor() || method.isDestructor()) {
iter.remove();
}
}
return methods;
}
}