Add OriginCapablePropertySource interfaces

Add `OriginCapablePropertySource` interface and supporting classes so
that `PropertySource` implementations can reveal the source of any
given property.

The `TextResourcePropertyOrigin` can be used for sources that load
values from a text `Resource` and can track line/column numbers.

See gh-8517
See gh-8142
pull/8526/head
Phillip Webb 8 years ago
parent 2136a39004
commit 43a54c8db1

@ -0,0 +1,39 @@
/*
* Copyright 2012-2017 the original author or authors.
*
* 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.springframework.boot.env;
import org.springframework.core.env.PropertySource;
/**
* An additional interface that may be implemented by a {@link PropertySource} that can
* return origin information. For example a property source that's backed by a file may
* return origin information for line and column numbers.
*
* @author Phillip Webb
* @since 2.0.0
*/
public interface OriginCapablePropertySource {
/**
* Return the origin of the given property name or {@code null} if the origin cannot
* be determined.
* @param name the property name
* @return the origin of the property or {@code null}
*/
PropertyOrigin getPropertyOrigin(String name);
}

@ -0,0 +1,35 @@
/*
* Copyright 2012-2017 the original author or authors.
*
* 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.springframework.boot.env;
import java.io.File;
/**
* Interface that uniquely represents the origin of a property. For example, a property
* loaded from a {@link File} may have an origin made up of the file name along with
* line/column numbers.
* <p>
* Implementations must provide sensible {@link #hashCode()}, {@link #equals(Object)} and
* {@link #toString()} implementations.
*
* @author Madhura Bhave
* @author Phillip Webb
* @since 2.0.0
*/
public interface PropertyOrigin {
}

@ -0,0 +1,156 @@
/*
* Copyright 2012-2017 the original author or authors.
*
* 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.springframework.boot.env;
import org.springframework.core.io.Resource;
import org.springframework.util.ObjectUtils;
/**
* {@link PropertyOrigin} for an item loaded from a text resource. Provides access to the
* origina {@link Resource} that loaded the text and a {@link Location} within it.
*
* @author Madhura Bhave
* @author Phillip Webb
* @since 2.0.0
*/
public class TextResourcePropertyOrigin implements PropertyOrigin {
private final Resource resource;
private final Location location;
public TextResourcePropertyOrigin(Resource resource, Location location) {
this.resource = resource;
this.location = location;
}
/**
* Return the resource where the property originated.
* @return the resource the text resource or {@code null}
*/
public Resource getResource() {
return this.resource;
}
/**
* Return the location of the property within the source (if known).
* @return the location or {@code null}
*/
public Location getLocation() {
return this.location;
}
@Override
public int hashCode() {
int result = 1;
result = 31 * result + ObjectUtils.nullSafeHashCode(this.resource);
result = 31 * result + ObjectUtils.nullSafeHashCode(this.location);
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (obj instanceof TextResourcePropertyOrigin) {
TextResourcePropertyOrigin other = (TextResourcePropertyOrigin) obj;
boolean result = true;
result = result && ObjectUtils.nullSafeEquals(this.resource, other.resource);
result = result && ObjectUtils.nullSafeEquals(this.location, other.location);
return result;
}
return super.equals(obj);
}
@Override
public String toString() {
StringBuilder result = new StringBuilder();
result.append(this.resource == null ? "unknown resource [?]"
: this.resource.getDescription());
if (this.location != null) {
result.append(":" + this.location);
}
return result.toString();
}
/**
* A location (line and column number) within the resource.
*/
public final static class Location {
private final int line;
private final int column;
/**
* Create a new {@link Location} instance.
* @param line the line number (zero indexed)
* @param column the column number (zero indexed)
*/
public Location(int line, int column) {
this.line = line;
this.column = column;
}
/**
* Return the line of the text resource where the property originated.
* @return the line number (zero indexed)
*/
public int getLine() {
return this.line;
}
/**
* Return the column of the text resource where the property originated.
* @return the column number (zero indexed)
*/
public int getColumn() {
return this.column;
}
@Override
public int hashCode() {
return (31 * this.line) + this.column;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null || getClass() != obj.getClass()) {
return false;
}
Location other = (Location) obj;
boolean result = true;
result = result && this.line == other.line;
result = result && this.column == other.column;
return result;
}
@Override
public String toString() {
return (this.line + 1) + ":" + (this.column + 1);
}
}
}

@ -0,0 +1,135 @@
/*
* Copyright 2012-2017 the original author or authors.
*
* 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.springframework.boot.env;
import org.junit.Test;
import org.springframework.boot.env.TextResourcePropertyOrigin.Location;
import org.springframework.core.io.ClassPathResource;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link TextResourcePropertyOrigin}.
*
* @author Phillip Webb
*/
public class TextResourcePropertyOriginTests {
@Test
public void createWithNullResourceShouldSetNullResource() throws Exception {
TextResourcePropertyOrigin origin = new TextResourcePropertyOrigin(null, null);
assertThat(origin.getResource()).isNull();
}
@Test
public void createWithNullLocationShouldSetNullLocation() throws Exception {
TextResourcePropertyOrigin origin = new TextResourcePropertyOrigin(null, null);
assertThat(origin.getLocation()).isNull();
}
@Test
public void getResourceShouldReturnResource() throws Exception {
ClassPathResource resource = new ClassPathResource("foo.txt");
TextResourcePropertyOrigin origin = new TextResourcePropertyOrigin(resource,
null);
assertThat(origin.getResource()).isEqualTo(resource);
}
@Test
public void getLocationShouldReturnLocation() throws Exception {
Location location = new Location(1, 2);
TextResourcePropertyOrigin origin = new TextResourcePropertyOrigin(null,
location);
assertThat(origin.getLocation()).isEqualTo(location);
}
@Test
public void getLocationLineShouldReturnLine() throws Exception {
Location location = new Location(1, 2);
assertThat(location.getLine()).isEqualTo(1);
}
@Test
public void getLocationColumnShouldReturnColumn() throws Exception {
Location location = new Location(1, 2);
assertThat(location.getColumn()).isEqualTo(2);
}
@Test
public void locationToStringShouldReturnNiceString() throws Exception {
Location location = new Location(1, 2);
assertThat(location.toString()).isEqualTo("2:3");
}
@Test
public void toStringShouldReturnNiceString() throws Exception {
ClassPathResource resource = new ClassPathResource("foo.txt");
Location location = new Location(1, 2);
TextResourcePropertyOrigin origin = new TextResourcePropertyOrigin(resource,
location);
assertThat(origin.toString()).isEqualTo("class path resource [foo.txt]:2:3");
}
@Test
public void toStringWhenResourceIsNullShouldReturnNiceString() throws Exception {
Location location = new Location(1, 2);
TextResourcePropertyOrigin origin = new TextResourcePropertyOrigin(null,
location);
assertThat(origin.toString()).isEqualTo("unknown resource [?]:2:3");
}
@Test
public void toStringWhenLocationIsNullShouldReturnNiceString() throws Exception {
ClassPathResource resource = new ClassPathResource("foo.txt");
TextResourcePropertyOrigin origin = new TextResourcePropertyOrigin(resource,
null);
assertThat(origin.toString()).isEqualTo("class path resource [foo.txt]");
}
@Test
public void locationEqualsAndHashcodeShouldUseLineAndColumn() throws Exception {
Location location1 = new Location(1, 2);
Location location2 = new Location(1, 2);
Location location3 = new Location(2, 2);
assertThat(location1.hashCode()).isEqualTo(location1.hashCode());
assertThat(location1.hashCode()).isEqualTo(location2.hashCode());
assertThat(location1).isEqualTo(location1);
assertThat(location1).isEqualTo(location2);
assertThat(location1).isNotEqualTo(location3);
}
@Test
public void equalsAndHashCodeShouldResourceAndLocation() throws Exception {
TextResourcePropertyOrigin origin1 = new TextResourcePropertyOrigin(
new ClassPathResource("foo.txt"), new Location(1, 2));
TextResourcePropertyOrigin origin2 = new TextResourcePropertyOrigin(
new ClassPathResource("foo.txt"), new Location(1, 2));
TextResourcePropertyOrigin origin3 = new TextResourcePropertyOrigin(
new ClassPathResource("foo.txt"), new Location(2, 2));
TextResourcePropertyOrigin origin4 = new TextResourcePropertyOrigin(
new ClassPathResource("foo2.txt"), new Location(1, 2));
assertThat(origin1.hashCode()).isEqualTo(origin1.hashCode());
assertThat(origin1.hashCode()).isEqualTo(origin2.hashCode());
assertThat(origin1).isEqualTo(origin1);
assertThat(origin1).isEqualTo(origin2);
assertThat(origin1).isNotEqualTo(origin3);
assertThat(origin1).isNotEqualTo(origin4);
}
}
Loading…
Cancel
Save