package de.is24.deadcode4j.analyzer; import com.google.common.base.Optional; import com.google.common.collect.Iterables; import javax.annotation.Nonnull; import static com.google.common.base.Optional.absent; import static com.google.common.base.Optional.of; /** * Analyzes Spring XML files: * <ul> * <li>lists the <code>bean</code> classes being referenced</li> * <li>lists the classes being referenced by a <a href="http://docs.spring.io/autorepo/docs/spring-framework/3.2.x/javadoc-api/org/springframework/beans/factory/config/MethodInvokingFactoryBean.html"> * MethodInvokingFactoryBean</a>; note however that the <tt>staticMethod</tt> property is not supported, but the * more verbose approach using the <tt>targetClass</tt> and <tt>targetMethod</tt> properties</li> * <li>lists the <a href="http://cxf.apache.org/schemas/jaxws.xsd">CXF <code>endpoint</code></a> implementor classes * being referenced</li> * <li>lists the classes executed by Quartz via * <a href="http://docs.spring.io/spring/docs/3.0.x/reference/scheduling.html#scheduling-quartz-jobdetail">JobDetailBean</a></li> or * <a href="http://docs.spring.io/spring/docs/4.0.x/spring-framework-reference/html/scheduling.html#scheduling-quartz-jobdetail">JobDetailFactoryBean</a> * <li>lists the view class used by * <a href="http://docs.spring.io/spring/docs/3.0.x/reference/view.html#view-tiles-url"> * <code>UrlBasedViewResolver</code></a> and subclasses</li> * </ul> * * @since 1.1.0 */ public class SpringXmlAnalyzer extends ExtendedXmlAnalyzer { private static void registerPropertyValueAsClass(@Nonnull Path beanPath, @Nonnull String propertyName) { Path propertyPath = beanPath.anyElementNamed("property").withAttributeValue("name", propertyName); propertyPath.registerAttributeAsClass("value"); propertyPath.anyElementNamed("value").registerTextAsClass(); } private static Optional<String> extractClassName(Optional<String> staticMethodCallNotation) { if (!staticMethodCallNotation.isPresent()) { return absent(); } int lastDot = staticMethodCallNotation.get().lastIndexOf('.'); if (lastDot < 1) { return absent(); } return of(staticMethodCallNotation.get().substring(0, lastDot)); } public SpringXmlAnalyzer() { super("_Spring-XML_", ".xml", "beans"); // regular spring beans anyElementNamed("bean").registerAttributeAsClass("class"); // MethodInvokingFactoryBean Path methodInvokingFactoryBean = beanOfClass("org.springframework.beans.factory.config.MethodInvokingFactoryBean"); registerPropertyValueAsClass(methodInvokingFactoryBean, "targetClass"); Path staticMethodProperty = methodInvokingFactoryBean.anyElementNamed("property").withAttributeValue("name", "staticMethod"); staticMethodProperty.registerDependeeExtractor(new DependeeExtractor() { @Nonnull @Override public Optional<String> extractDependee(@Nonnull Iterable<XmlElement> xmlElements, @Nonnull Optional<String> containedText) { return extractClassName(Iterables.getLast(xmlElements).getAttribute("value")); } }); staticMethodProperty.anyElementNamed("value").registerDependeeExtractor(new DependeeExtractor() { @Nonnull @Override public Optional<String> extractDependee(@Nonnull Iterable<XmlElement> xmlElements, @Nonnull Optional<String> containedText) { return extractClassName(containedText); } }); // CXF endpoints anyElementNamed("endpoint").registerAttributeAsClass("implementor"); anyElementNamed("endpoint").anyElementNamed("implementor").registerTextAsClass(); anyElementNamed("endpoint").registerAttributeAsClass("implementorClass"); // JobDetailBean registerPropertyValueAsClass( beanOfClass("org.springframework.scheduling.quartz.JobDetailBean"), "jobClass"); // JobDetailFactoryBean registerPropertyValueAsClass( beanOfClass("org.springframework.scheduling.quartz.JobDetailFactoryBean"), "jobClass"); // view resolver; this is not restricted to a class bc there are loads of subclasses registerPropertyValueAsClass(anyElementNamed("bean"), "viewClass"); } @Nonnull private Path beanOfClass(@Nonnull String beanClass) { return anyElementNamed("bean").withAttributeValue("class", beanClass); } }