/*
* =============================================================================
*
* Copyright (c) 2011-2016, The THYMELEAF team (http://www.thymeleaf.org)
*
* 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.thymeleaf.cache;
import java.io.Serializable;
import java.util.Map;
import java.util.Set;
import org.thymeleaf.templatemode.TemplateMode;
import org.thymeleaf.util.LoggingUtils;
import org.thymeleaf.util.Validate;
/**
* <p>
* This class models objects used as keys in the Template Cache.
* </p>
* <p>
* Objects of this class <strong>should only be created from inside the engine</strong>.
* </p>
*
* @author Daniel Fernández
*
* @since 3.0.0
*/
public final class TemplateCacheKey implements Serializable {
private static final long serialVersionUID = 45842555123291L;
private final String ownerTemplate;
private final String template;
private final Set<String> templateSelectors;
private final int lineOffset;
private final int colOffset;
private final TemplateMode templateMode;
private final Map<String,Object> templateResolutionAttributes;
private final int h;
public TemplateCacheKey(
final String ownerTemplate, final String template, final Set<String> templateSelectors,
final int lineOffset, final int colOffset, final TemplateMode templateMode,
final Map<String,Object> templateResolutionAttributes) {
// NOTE we are assuming that templateSelectors is either null or a non-empty, naturally-ordered set. Also,
// we are also assuming that templateResolutionAttributes is either null or non-empty. Also, BOTH
// should be unmodifiable. This should have been sorted out at the TemplateSpec constructor.
super();
// ownerTemplate will be null if this template is standalone (not something we are processing from inside another one like e.g. an inlining)
Validate.notNull(template, "Template cannot be null");
// templateSelectors can be null if we are selecting the entire template
// templateMode can be null if this template is standalone (no owner template) AND we are forcing a specific template mode for its processing
// templateResolutionAttributes
this.ownerTemplate = ownerTemplate;
this.template = template;
this.templateSelectors = templateSelectors;
this.lineOffset = lineOffset;
this.colOffset = colOffset;
this.templateMode = templateMode;
this.templateResolutionAttributes = templateResolutionAttributes;
// This being a cache key, its equals and hashCode methods will potentially execute many
// times, so this could help performance
this.h = computeHashCode();
}
public String getOwnerTemplate() {
return this.ownerTemplate;
}
public String getTemplate() {
return this.template;
}
public Set<String> getTemplateSelectors() {
return this.templateSelectors;
}
public int getLineOffset() {
return this.lineOffset;
}
public int getColOffset() {
return this.colOffset;
}
public TemplateMode getTemplateMode() {
return this.templateMode;
}
public Map<String, Object> getTemplateResolutionAttributes() {
return this.templateResolutionAttributes;
}
@Override
public boolean equals(final Object o) {
if (this == o) {
return true;
}
if (!(o instanceof TemplateCacheKey)) {
return false;
}
final TemplateCacheKey that = (TemplateCacheKey) o;
if (this.h != that.h) { // fail fast
return false;
}
if (this.lineOffset != that.lineOffset) {
return false;
}
if (this.colOffset != that.colOffset) {
return false;
}
if (this.ownerTemplate != null ? !this.ownerTemplate.equals(that.ownerTemplate) : that.ownerTemplate != null) {
return false;
}
if (!this.template.equals(that.template)) {
return false;
}
if (this.templateSelectors != null ? !this.templateSelectors.equals(that.templateSelectors) : that.templateSelectors != null) {
return false;
}
if (this.templateMode != that.templateMode) {
return false;
}
// Note how it is important that template resolution attribute values correctly implement equals() and hashCode()
return !(this.templateResolutionAttributes != null ? !this.templateResolutionAttributes.equals(that.templateResolutionAttributes) : that.templateResolutionAttributes != null);
}
@Override
public int hashCode() {
return this.h;
}
private int computeHashCode() {
int result = this.ownerTemplate != null ? this.ownerTemplate.hashCode() : 0;
result = 31 * result + this.template.hashCode();
result = 31 * result + (this.templateSelectors != null ? this.templateSelectors.hashCode() : 0);
result = 31 * result + this.lineOffset;
result = 31 * result + this.colOffset;
result = 31 * result + (this.templateMode != null ? this.templateMode.hashCode() : 0);
result = 31 * result + (this.templateResolutionAttributes != null ? this.templateResolutionAttributes.hashCode() : 0);
return result;
}
@Override
public String toString() {
final StringBuilder strBuilder = new StringBuilder();
strBuilder.append(LoggingUtils.loggifyTemplateName(this.template));
if (this.ownerTemplate != null) {
strBuilder.append('@');
strBuilder.append('(');
strBuilder.append(LoggingUtils.loggifyTemplateName(this.ownerTemplate));
strBuilder.append(';');
strBuilder.append(this.lineOffset);
strBuilder.append(',');
strBuilder.append(this.colOffset);
strBuilder.append(')');
}
if (this.templateSelectors != null) {
strBuilder.append("::");
strBuilder.append(this.templateSelectors);
}
if (this.templateMode != null) {
strBuilder.append(" @");
strBuilder.append(this.templateMode);
}
if (this.templateResolutionAttributes != null) {
strBuilder.append(" (");
strBuilder.append(this.templateResolutionAttributes);
strBuilder.append(")");
}
return strBuilder.toString();
}
}