diff --git a/spring-boot/src/main/java/org/springframework/boot/env/OriginCapablePropertySource.java b/spring-boot/src/main/java/org/springframework/boot/env/OriginCapablePropertySource.java new file mode 100644 index 0000000000..34d3032426 --- /dev/null +++ b/spring-boot/src/main/java/org/springframework/boot/env/OriginCapablePropertySource.java @@ -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); + +} diff --git a/spring-boot/src/main/java/org/springframework/boot/env/PropertyOrigin.java b/spring-boot/src/main/java/org/springframework/boot/env/PropertyOrigin.java new file mode 100644 index 0000000000..7c8b8760e3 --- /dev/null +++ b/spring-boot/src/main/java/org/springframework/boot/env/PropertyOrigin.java @@ -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. + *

+ * 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 { + +} diff --git a/spring-boot/src/main/java/org/springframework/boot/env/TextResourcePropertyOrigin.java b/spring-boot/src/main/java/org/springframework/boot/env/TextResourcePropertyOrigin.java new file mode 100644 index 0000000000..6ac740a9c1 --- /dev/null +++ b/spring-boot/src/main/java/org/springframework/boot/env/TextResourcePropertyOrigin.java @@ -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); + } + + } + +} diff --git a/spring-boot/src/test/java/org/springframework/boot/env/TextResourcePropertyOriginTests.java b/spring-boot/src/test/java/org/springframework/boot/env/TextResourcePropertyOriginTests.java new file mode 100644 index 0000000000..21d76f1502 --- /dev/null +++ b/spring-boot/src/test/java/org/springframework/boot/env/TextResourcePropertyOriginTests.java @@ -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); + } + +}