/* * Copyright 2014 Daniel Bechler * * 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 de.danielbechler.diff.inclusion; import de.danielbechler.diff.ObjectDifferBuilder; import de.danielbechler.diff.category.CategoryResolver; import de.danielbechler.diff.node.DiffNode; import de.danielbechler.diff.path.NodePath; import de.danielbechler.util.Assert; import java.util.Collection; import java.util.LinkedList; import static de.danielbechler.diff.inclusion.Inclusion.DEFAULT; import static de.danielbechler.diff.inclusion.Inclusion.EXCLUDED; import static de.danielbechler.diff.inclusion.Inclusion.INCLUDED; @SuppressWarnings("OverlyComplexAnonymousInnerClass") public class InclusionService implements InclusionConfigurer, IsIgnoredResolver { private final ObjectDifferBuilder rootConfiguration; private final CategoryResolver categoryResolver; private final Collection<InclusionResolver> inclusionResolvers = new LinkedList<InclusionResolver>(); private TypeInclusionResolver typeInclusionResolver; private TypePropertyConfigInclusionResolver typePropertyConfigInclusionResolver; private CategoryInclusionResolver categoryInclusionResolver; private NodePathInclusionResolver nodePathInclusionResolver; private PropertyNameInclusionResolver propertyNameInclusionResolver; public InclusionService(final CategoryResolver categoryResolver, final ObjectDifferBuilder rootConfiguration) { Assert.notNull(rootConfiguration, "rootConfiguration"); Assert.notNull(categoryResolver, "categoryResolver"); this.rootConfiguration = rootConfiguration; this.categoryResolver = categoryResolver; addAlwaysOnInclusionResolvers(); } private void addAlwaysOnInclusionResolvers() { inclusionResolvers.add(new TypePropertyAnnotationInclusionResolver()); } Collection<InclusionResolver> getInclusionResolvers() { return inclusionResolvers; } public boolean isIgnored(final DiffNode node) { if (node.isRootNode()) { return false; } boolean strictIncludeModeEnabled = false; boolean isExplicitlyIncluded = false; for (final InclusionResolver inclusionResolver : inclusionResolvers) { if (inclusionResolver.enablesStrictIncludeMode()) { strictIncludeModeEnabled = true; } switch (getInclusion(node, inclusionResolver)) { case EXCLUDED: return true; case INCLUDED: isExplicitlyIncluded = true; break; } } if (strictIncludeModeEnabled && !isExplicitlyIncluded) { return true; } return false; } private static Inclusion getInclusion(final DiffNode node, final InclusionResolver inclusionResolver) { final Inclusion inclusion = inclusionResolver.getInclusion(node); return inclusion != null ? inclusion : DEFAULT; } public ToInclude include() { return new ToInclude() { public ToInclude category(final String category) { setCategoryInclusion(INCLUDED, category); return this; } public ToInclude type(final Class<?> type) { setTypeInclusion(INCLUDED, type); return this; } public ToInclude node(final NodePath nodePath) { setNodePathInclusion(INCLUDED, nodePath); return this; } public ToInclude propertyName(final String propertyName) { setPropertyNameInclusion(INCLUDED, propertyName); return this; } public ToInclude propertyNameOfType(final Class<?> type, final String... propertyNames) { setPropertyNameOfTypeInclusion(INCLUDED, type, propertyNames); return this; } public InclusionConfigurer also() { return InclusionService.this; } public ObjectDifferBuilder and() { return rootConfiguration; } }; } void setCategoryInclusion(final Inclusion inclusion, final String category) { if (categoryInclusionResolver == null) { categoryInclusionResolver = newCategoryInclusionResolver(); inclusionResolvers.add(categoryInclusionResolver); } categoryInclusionResolver.setInclusion(category, inclusion); } void setTypeInclusion(final Inclusion inclusion, final Class<?> type) { if (typeInclusionResolver == null) { typeInclusionResolver = newTypeInclusionResolver(); inclusionResolvers.add(typeInclusionResolver); } typeInclusionResolver.setInclusion(type, inclusion); } void setNodePathInclusion(final Inclusion inclusion, final NodePath nodePath) { if (nodePathInclusionResolver == null) { nodePathInclusionResolver = newNodePathInclusionResolver(); inclusionResolvers.add(nodePathInclusionResolver); } nodePathInclusionResolver.setInclusion(nodePath, inclusion); } void setPropertyNameInclusion(final Inclusion inclusion, final String propertyName) { if (propertyNameInclusionResolver == null) { propertyNameInclusionResolver = newPropertyNameInclusionResolver(); inclusionResolvers.add(propertyNameInclusionResolver); } propertyNameInclusionResolver.setInclusion(propertyName, inclusion); } private void setPropertyNameOfTypeInclusion(final Inclusion inclusion, final Class<?> type, final String... propertyNames) { Assert.notNull(type, "type"); for (final String propertyName : propertyNames) { Assert.hasText(propertyName, "propertyName in propertyNames"); if (typePropertyConfigInclusionResolver == null) { typePropertyConfigInclusionResolver = newTypePropertyConfigInclusionResolver(); inclusionResolvers.add(typePropertyConfigInclusionResolver); } typePropertyConfigInclusionResolver.setInclusion(type, propertyName, inclusion); } } CategoryInclusionResolver newCategoryInclusionResolver() { return new CategoryInclusionResolver(categoryResolver); } TypeInclusionResolver newTypeInclusionResolver() { return new TypeInclusionResolver(); } NodePathInclusionResolver newNodePathInclusionResolver() { return new NodePathInclusionResolver(); } PropertyNameInclusionResolver newPropertyNameInclusionResolver() { return new PropertyNameInclusionResolver(); } TypePropertyConfigInclusionResolver newTypePropertyConfigInclusionResolver() { return new TypePropertyConfigInclusionResolver(); } public ToExclude exclude() { return new ToExclude() { public ToExclude category(final String category) { setCategoryInclusion(EXCLUDED, category); return this; } public ToExclude type(final Class<?> type) { setTypeInclusion(EXCLUDED, type); return this; } public ToExclude node(final NodePath nodePath) { setNodePathInclusion(EXCLUDED, nodePath); return this; } public ToExclude propertyName(final String propertyName) { setPropertyNameInclusion(EXCLUDED, propertyName); return this; } public ToExclude propertyNameOfType(final Class<?> type, final String... propertyNames) { setPropertyNameOfTypeInclusion(EXCLUDED, type, propertyNames); return this; } public InclusionConfigurer also() { return InclusionService.this; } public ObjectDifferBuilder and() { return rootConfiguration; } }; } public InclusionConfigurer resolveUsing(final InclusionResolver inclusionResolver) { Assert.notNull(inclusionResolver, "inclusionResolver"); inclusionResolvers.add(inclusionResolver); return this; } public ObjectDifferBuilder and() { return rootConfiguration; } }