blob: 7e723091f5d591811ccfd3676e6d0e04933a0686 [file] [log] [blame]
/*
* 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.
*/
package org.apache.felix.ipojo.manipulator;
import org.apache.felix.ipojo.manipulation.Manipulator;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
/**
* A {@code ManipulationEngine} is responsible to drive the component's
* classes manipulation.
*
* @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
*/
public class ManipulationEngine {
/**
* The classloader given to the manipulator to load classes.
*/
private final ClassLoader m_classLoader;
/**
* List of component types.
*/
private List<ManipulationUnit> m_manipulationUnits = new ArrayList<ManipulationUnit>();
/**
* Error reporting.
*/
private Reporter m_reporter;
/**
* Bytecode store.
*/
private ResourceStore m_store;
/**
* The visitor handling output result.
*/
private ManipulationVisitor m_manipulationVisitor;
public ManipulationEngine(ClassLoader classLoader) {
m_classLoader = classLoader;
}
/**
* Add information related to a discovered component that will be manipulated.
* @param component additional component
*/
public void addManipulationUnit(ManipulationUnit component) {
m_manipulationUnits.add(component);
}
public void setManipulationVisitor(ManipulationVisitor manipulationVisitor) {
m_manipulationVisitor = manipulationVisitor;
}
/**
* @param reporter Feedback reporter.
*/
public void setReporter(Reporter reporter) {
m_reporter = reporter;
}
/**
* Provides the bytecode store that allows to retrieve bytecode of the
* component's related resources (inner classes for example).
* @param store Helps to locate bytecode for classes.
*/
public void setResourceStore(ResourceStore store) {
m_store = store;
}
/**
* Manipulates classes of all the given component's.
*/
public void generate() {
// Iterates over the list of discovered components
// Note that this list includes components from metadata.xml AND from annotations
for (ManipulationUnit info : m_manipulationUnits) {
byte[] bytecode;
try {
bytecode = m_store.read(info.getResourcePath());
} catch (IOException e) {
m_reporter.error("Cannot find bytecode for class '" + info.getClassName() + "': no bytecode found.");
return;
}
// Is the visitor interested in this component ?
ManipulationResultVisitor result = m_manipulationVisitor.visitManipulationResult(info.getComponentMetadata());
if (result != null) {
// Should always be the case
// Manipulation preparation
Manipulator manipulator = new Manipulator(m_classLoader);
try {
manipulator.prepare(bytecode);
} catch (IOException e) {
m_reporter.error("Cannot analyze the class " + info.getClassName() + " : " + e.getMessage());
return;
}
// Inner class preparation
for (String inner : manipulator.getInnerClasses()) {
// Get the bytecode and start manipulation
String resourcePath = inner + ".class";
String outerClassInternalName = info.getClassName().replace('.', '/');
byte[] innerClassBytecode;
try {
innerClassBytecode = m_store.read(resourcePath);
manipulator.prepareInnerClass(inner, innerClassBytecode);
} catch (IOException e) {
m_reporter.error("Cannot find or analyze inner class '" + resourcePath + "'");
return;
}
}
// Now manipulate the classes.
try {
byte[] out = manipulator.manipulate(bytecode);
// Call the visitor
result.visitManipulatedResource(info.getResourcePath(), out);
} catch (IOException e) {
m_reporter.error("Cannot manipulate the class " + info.getClassName() + " : " + e.getMessage());
return;
}
// Visit inner classes
for (String inner : manipulator.getInnerClasses()) {
// Get the bytecode and start manipulation
String resourcePath = inner + ".class";
String outerClassInternalName = info.getClassName().replace('.', '/');
byte[] innerClassBytecode;
try {
innerClassBytecode = m_store.read(resourcePath);
} catch (IOException e) {
m_reporter.error("Cannot find inner class '" + resourcePath + "'");
return;
}
// Manipulate inner class
// Notice that (for performance reason) re-use the class version information
// discovered in the main class instead of re-parsing the inner class to find
// its own class version
try {
byte[] manipulated = manipulator.manipulateInnerClass(inner, innerClassBytecode);
// Propagate manipulated resource
result.visitManipulatedResource(resourcePath, manipulated);
} catch (IOException e) {
m_reporter.error("Cannot manipulate inner class '" + resourcePath + "'");
return;
}
}
// Compute manipulation metadata
result.visitClassStructure(manipulator.getManipulationMetadata());
// All resources have been manipulated for this component
result.visitEnd();
}
}
}
}