Browse Source

Add the new class Attributes holding multiple Attribute(s)

Attributes represents a semantic collector of Attribute(s) and replaces
the anonymous Map<String,Attribute>. This class will be returned by
TreeWalk.getAttributes(). It offers convenient access to the attributes
wrapped in the Attributes object. Adds preparations for a future
Attribute Macro Expansion

Change-Id: I8348c8c457a2a7f1f0c48050e10399b0fa1cdbe1
Signed-off-by: Ivan Motsch <ivan.motsch@bsiag.com>
stable-4.3
Ivan Motsch 9 years ago
parent
commit
75697adc5a
  1. 10
      org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesNodeDirCacheIteratorTest.java
  2. 27
      org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesNodeTest.java
  3. 10
      org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesNodeWorkingTreeIteratorTest.java
  4. 5
      org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/TreeWalkAttributeTest.java
  5. 8
      org.eclipse.jgit/.settings/.api_filters
  6. 16
      org.eclipse.jgit/src/org/eclipse/jgit/attributes/Attribute.java
  7. 202
      org.eclipse.jgit/src/org/eclipse/jgit/attributes/Attributes.java
  8. 12
      org.eclipse.jgit/src/org/eclipse/jgit/attributes/AttributesNode.java
  9. 6
      org.eclipse.jgit/src/org/eclipse/jgit/attributes/AttributesProvider.java
  10. 19
      org.eclipse.jgit/src/org/eclipse/jgit/attributes/AttributesRule.java
  11. 3
      org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/InfoAttributesNode.java
  12. 7
      org.eclipse.jgit/src/org/eclipse/jgit/lib/Constants.java
  13. 41
      org.eclipse.jgit/src/org/eclipse/jgit/treewalk/TreeWalk.java

10
org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesNodeDirCacheIteratorTest.java

@ -52,9 +52,7 @@ import static org.junit.Assert.assertTrue;
import java.io.IOException;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.attributes.Attribute.State;
@ -258,12 +256,14 @@ public class AttributesNodeDirCacheIteratorTest extends RepositoryTestCase {
assertTrue(nodeAttrs == null || nodeAttrs.isEmpty());
else {
Map<String, Attribute> entryAttributes = new LinkedHashMap<String, Attribute>();
attributesNode.getAttributes(pathName, false, entryAttributes);
Attributes entryAttributes = new Attributes();
attributesNode.getAttributes(pathName,
false, entryAttributes);
if (nodeAttrs != null && !nodeAttrs.isEmpty()) {
for (Attribute attribute : nodeAttrs) {
assertThat(entryAttributes.values(), hasItem(attribute));
assertThat(entryAttributes.getAll(),
hasItem(attribute));
}
} else {
assertTrue(

27
org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesNodeTest.java

@ -49,10 +49,6 @@ import static org.junit.Assert.assertEquals;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Set;
import org.junit.After;
import org.junit.Test;
@ -104,8 +100,8 @@ public class AttributesNodeTest {
is = new ByteArrayInputStream(attributeFileContent.getBytes());
AttributesNode node = new AttributesNode();
node.parse(is);
assertAttribute("file.type1", node, Collections.<Attribute> emptySet());
assertAttribute("file.type2", node, Collections.<Attribute> emptySet());
assertAttribute("file.type1", node, new Attributes());
assertAttribute("file.type2", node, new Attributes());
}
@Test
@ -115,7 +111,7 @@ public class AttributesNodeTest {
is = new ByteArrayInputStream(attributeFileContent.getBytes());
AttributesNode node = new AttributesNode();
node.parse(is);
assertAttribute("file.type1", node, Collections.<Attribute> emptySet());
assertAttribute("file.type1", node, new Attributes());
assertAttribute("file.type2", node, asSet(A_UNSET_ATTR));
}
@ -127,8 +123,8 @@ public class AttributesNodeTest {
is = new ByteArrayInputStream(attributeFileContent.getBytes());
AttributesNode node = new AttributesNode();
node.parse(is);
assertAttribute("file.type1", node, Collections.<Attribute> emptySet());
assertAttribute("file.type2", node, Collections.<Attribute> emptySet());
assertAttribute("file.type1", node, new Attributes());
assertAttribute("file.type2", node, new Attributes());
assertAttribute("file.type3", node, asSet(new Attribute("attr", "")));
}
@ -166,17 +162,14 @@ public class AttributesNodeTest {
}
private void assertAttribute(String path, AttributesNode node,
Set<Attribute> attrs) {
HashMap<String, Attribute> attributes = new HashMap<String, Attribute>();
Attributes attrs) {
Attributes attributes = new Attributes();
node.getAttributes(path, false, attributes);
assertEquals(attrs, new HashSet<Attribute>(attributes.values()));
assertEquals(attrs, attributes);
}
static Set<Attribute> asSet(Attribute... attrs) {
Set<Attribute> result = new HashSet<Attribute>();
for (Attribute attr : attrs)
result.add(attr);
return result;
static Attributes asSet(Attribute... attrs) {
return new Attributes(attrs);
}
}

10
org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesNodeWorkingTreeIteratorTest.java

@ -53,9 +53,7 @@ import static org.junit.Assert.assertTrue;
import java.io.File;
import java.io.IOException;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.eclipse.jgit.attributes.Attribute.State;
import org.eclipse.jgit.errors.CorruptObjectException;
@ -227,12 +225,14 @@ public class AttributesNodeWorkingTreeIteratorTest extends RepositoryTestCase {
assertTrue(nodeAttrs == null || nodeAttrs.isEmpty());
else {
Map<String, Attribute> entryAttributes = new LinkedHashMap<String, Attribute>();
attributesNode.getAttributes(pathName, false, entryAttributes);
Attributes entryAttributes = new Attributes();
attributesNode.getAttributes(pathName,
false, entryAttributes);
if (nodeAttrs != null && !nodeAttrs.isEmpty()) {
for (Attribute attribute : nodeAttrs) {
assertThat(entryAttributes.values(), hasItem(attribute));
assertThat(entryAttributes.getAll(),
hasItem(attribute));
}
} else {
assertTrue(

5
org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/TreeWalkAttributeTest.java

@ -802,8 +802,9 @@ public class TreeWalkAttributeTest extends RepositoryTestCase {
assertEquals(type, walk.getFileMode(0));
assertEquals(checkinAttributes,
asSet(ci_walk.getAttributes().values()));
assertEquals(checkoutAttributes, asSet(walk.getAttributes().values()));
asSet(ci_walk.getAttributes().getAll()));
assertEquals(checkoutAttributes,
asSet(walk.getAttributes().getAll()));
if (D.equals(type)) {
walk.enterSubtree();

8
org.eclipse.jgit/.settings/.api_filters

@ -1,5 +1,13 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<component id="org.eclipse.jgit" version="2">
<resource path="src/org/eclipse/jgit/attributes/AttributesNode.java" type="org.eclipse.jgit.attributes.AttributesNode">
<filter comment="attributes weren't really usable in earlier versions" id="338792546">
<message_arguments>
<message_argument value="org.eclipse.jgit.attributes.AttributesNode"/>
<message_argument value="getAttributes(String, boolean, Map&lt;String,Attribute&gt;)"/>
</message_arguments>
</filter>
</resource>
<resource path="src/org/eclipse/jgit/lib/BitmapIndex.java" type="org.eclipse.jgit.lib.BitmapIndex$BitmapBuilder">
<filter comment="interface is implemented by extenders but not clients of the API" id="403804204">
<message_arguments>

16
org.eclipse.jgit/src/org/eclipse/jgit/attributes/Attribute.java

@ -50,8 +50,10 @@ package org.eclipse.jgit.attributes;
* <li>Set - represented by {@link State#SET}</li>
* <li>Unset - represented by {@link State#UNSET}</li>
* <li>Set to a value - represented by {@link State#CUSTOM}</li>
* <li>Unspecified - <code>null</code> is used instead of an instance of this
* class</li>
* <li>Unspecified - used to revert an attribute . This is crucial in order to
* mark an attribute as unspecified in the attributes map and thus preventing
* following (with lower priority) nodes from setting the attribute to a value
* at all</li>
* </ul>
* </p>
*
@ -61,6 +63,7 @@ public final class Attribute {
/**
* The attribute value state
* see also https://www.kernel.org/pub/software/scm/git/docs/gitattributes.html
*/
public static enum State {
/** the attribute is set */
@ -69,6 +72,13 @@ public final class Attribute {
/** the attribute is unset */
UNSET,
/**
* the attribute appears as if it would not be defined at all
*
* @since 4.2
*/
UNSPECIFIED,
/** the attribute is set to a custom value */
CUSTOM
}
@ -176,6 +186,8 @@ public final class Attribute {
return key;
case UNSET:
return "-" + key; //$NON-NLS-1$
case UNSPECIFIED:
return "!" + key; //$NON-NLS-1$
case CUSTOM:
default:
return key + "=" + value; //$NON-NLS-1$

202
org.eclipse.jgit/src/org/eclipse/jgit/attributes/Attributes.java

@ -0,0 +1,202 @@
/*
* Copyright (C) 2015, Ivan Motsch <ivan.motsch@bsiag.com>
*
* This program and the accompanying materials are made available
* under the terms of the Eclipse Distribution License v1.0 which
* accompanies this distribution, is reproduced below, and is
* available at http://www.eclipse.org/org/documents/edl-v10.php
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or
* without modification, are permitted provided that the following
* conditions are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
*
* - Neither the name of the Eclipse Foundation, Inc. nor the
* names of its contributors may be used to endorse or promote
* products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.eclipse.jgit.attributes;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.Map;
import org.eclipse.jgit.attributes.Attribute.State;
/**
* Represents a set of attributes for a path
* <p>
*
* @since 4.2
*/
public final class Attributes {
private final Map<String, Attribute> map = new LinkedHashMap<>();
/**
* Creates a new instance
*
* @param attributes
*/
public Attributes(Attribute... attributes) {
if (attributes != null) {
for (Attribute a : attributes) {
put(a);
}
}
}
/**
* @return true if the set does not contain any attributes
*/
public boolean isEmpty() {
return map.isEmpty();
}
/**
* @param key
* @return the attribute or null
*/
public Attribute get(String key) {
return map.get(key);
}
/**
* @return all attributes
*/
public Collection<Attribute> getAll() {
return new ArrayList<>(map.values());
}
/**
* @param a
*/
public void put(Attribute a) {
map.put(a.getKey(), a);
}
/**
* @param key
*/
public void remove(String key) {
map.remove(key);
}
/**
* @param key
* @return true if the {@link Attributes} contains this key
*/
public boolean containsKey(String key) {
return map.containsKey(key);
}
/**
* Returns the state.
*
* @param key
*
* @return the state (never returns <code>null</code>)
*/
public Attribute.State getState(String key) {
Attribute a = map.get(key);
return a != null ? a.getState() : Attribute.State.UNSPECIFIED;
}
/**
* @param key
* @return true if the key is {@link State#SET}, false in all other cases
*/
public boolean isSet(String key) {
return (getState(key) == State.SET);
}
/**
* @param key
* @return true if the key is {@link State#UNSET}, false in all other cases
*/
public boolean isUnset(String key) {
return (getState(key) == State.UNSET);
}
/**
* @param key
* @return true if the key is {@link State#UNSPECIFIED}, false in all other
* cases
*/
public boolean isUnspecified(String key) {
return (getState(key) == State.UNSPECIFIED);
}
/**
* @param key
* @return true if the key is {@link State#CUSTOM}, false in all other cases
* see {@link #getValue(String)} for the value of the key
*/
public boolean isCustom(String key) {
return (getState(key) == State.CUSTOM);
}
/**
* @param key
* @return the attribute value (may be <code>null</code>)
*/
public String getValue(String key) {
Attribute a = map.get(key);
return a != null ? a.getValue() : null;
}
@Override
public String toString() {
StringBuilder buf = new StringBuilder();
buf.append(getClass().getSimpleName());
buf.append("["); //$NON-NLS-1$
buf.append(" "); //$NON-NLS-1$
for (Attribute a : map.values()) {
buf.append(a.toString());
buf.append(" "); //$NON-NLS-1$
}
buf.append("]"); //$NON-NLS-1$
return buf.toString();
}
@Override
public int hashCode() {
return map.hashCode();
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (!(obj instanceof Attributes))
return false;
Attributes other = (Attributes) obj;
return this.map.equals(other.map);
}
}

12
org.eclipse.jgit/src/org/eclipse/jgit/attributes/AttributesNode.java

@ -50,7 +50,6 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import org.eclipse.jgit.lib.Constants;
@ -134,11 +133,12 @@ public class AttributesNode {
* true if the target item is a directory.
* @param attributes
* Map that will hold the attributes matching this entry path. If
* it is not empty, this method will NOT override any
* existing entry.
* it is not empty, this method will NOT override any existing
* entry.
* @since 4.2
*/
public void getAttributes(String entryPath, boolean isDirectory,
Map<String, Attribute> attributes) {
public void getAttributes(String entryPath,
boolean isDirectory, Attributes attributes) {
// Parse rules in the reverse order that they were read since the last
// entry should be used
ListIterator<AttributesRule> ruleIterator = rules.listIterator(rules
@ -153,7 +153,7 @@ public class AttributesNode {
while (attributeIte.hasPrevious()) {
Attribute attr = attributeIte.previous();
if (!attributes.containsKey(attr.getKey()))
attributes.put(attr.getKey(), attr);
attributes.put(attr);
}
}
}

6
org.eclipse.jgit/src/org/eclipse/jgit/attributes/AttributesProvider.java

@ -42,8 +42,6 @@
*/
package org.eclipse.jgit.attributes;
import java.util.Map;
/**
* Interface for classes which provide git attributes
*
@ -51,7 +49,7 @@ import java.util.Map;
*/
public interface AttributesProvider {
/**
* @return the currently active attributes by attribute key
* @return the currently active attributes
*/
public Map<String, Attribute> getAttributes();
public Attributes getAttributes();
}

19
org.eclipse.jgit/src/org/eclipse/jgit/attributes/AttributesRule.java

@ -84,6 +84,13 @@ public class AttributesRule {
continue;
}
if (attribute.startsWith("!")) {//$NON-NLS-1$
if (attribute.length() > 1)
result.add(new Attribute(attribute.substring(1),
State.UNSPECIFIED));
continue;
}
final int equalsIndex = attribute.indexOf("="); //$NON-NLS-1$
if (equalsIndex == -1)
result.add(new Attribute(attribute, State.SET));
@ -200,4 +207,16 @@ public class AttributesRule {
boolean match = matcher.matches(relativeTarget, isDirectory);
return match;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append(pattern);
for (Attribute a : attributes) {
sb.append(" "); //$NON-NLS-1$
sb.append(a);
}
return sb.toString();
}
}

3
org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/InfoAttributesNode.java

@ -47,6 +47,7 @@ import java.io.File;
import java.io.IOException;
import org.eclipse.jgit.attributes.AttributesNode;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.util.FS;
@ -71,7 +72,7 @@ public class InfoAttributesNode extends AttributesNode {
FS fs = repository.getFS();
File attributes = fs.resolve(repository.getDirectory(),
"info/attributes"); //$NON-NLS-1$
Constants.INFO_ATTRIBUTES);
FileRepository.AttributesNodeProviderImpl.loadRulesFromFile(r, attributes);
return r.getRules().isEmpty() ? null : r;

7
org.eclipse.jgit/src/org/eclipse/jgit/lib/Constants.java

@ -272,6 +272,13 @@ public final class Constants {
*/
public static final String INFO_EXCLUDE = "info/exclude";
/**
* Attributes-override-file
*
* @since 4.2
*/
public static final String INFO_ATTRIBUTES = "info/attributes";
/**
* The system property that contains the system user name
*

41
org.eclipse.jgit/src/org/eclipse/jgit/treewalk/TreeWalk.java

@ -45,17 +45,16 @@
package org.eclipse.jgit.treewalk;
import java.io.IOException;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
import org.eclipse.jgit.annotations.Nullable;
import org.eclipse.jgit.api.errors.JGitInternalException;
import org.eclipse.jgit.attributes.Attribute;
import org.eclipse.jgit.attributes.Attributes;
import org.eclipse.jgit.attributes.AttributesNode;
import org.eclipse.jgit.attributes.AttributesNodeProvider;
import org.eclipse.jgit.attributes.AttributesProvider;
import org.eclipse.jgit.attributes.Attribute.State;
import org.eclipse.jgit.dircache.DirCacheIterator;
import org.eclipse.jgit.errors.CorruptObjectException;
import org.eclipse.jgit.errors.IncorrectObjectTypeException;
@ -258,7 +257,7 @@ public class TreeWalk implements AutoCloseable, AttributesProvider {
AbstractTreeIterator currentHead;
/** Cached attribute for the current entry */
private Map<String, Attribute> attrs = null;
private Attributes attrs = null;
/**
* Create a new tree walker for a given repository.
@ -1119,7 +1118,7 @@ public class TreeWalk implements AutoCloseable, AttributesProvider {
* @return a {@link Set} of {@link Attribute}s that match the current entry.
* @since 4.2
*/
public Map<String, Attribute> getAttributes() {
public Attributes getAttributes() {
if (attrs != null)
return attrs;
@ -1138,35 +1137,42 @@ public class TreeWalk implements AutoCloseable, AttributesProvider {
&& other == null) {
// Can not retrieve the attributes without at least one of the above
// iterators.
return Collections.<String, Attribute> emptyMap();
return new Attributes();
}
String path = currentHead.getEntryPathString();
final boolean isDir = FileMode.TREE.equals(currentHead.mode);
Map<String, Attribute> attributes = new LinkedHashMap<String, Attribute>();
Attributes attributes = new Attributes();
try {
// Gets the info attributes
// Gets the global attributes node
AttributesNode globalNodeAttr = attributesNodeProvider
.getGlobalAttributesNode();
// Gets the info attributes node
AttributesNode infoNodeAttr = attributesNodeProvider
.getInfoAttributesNode();
// Gets the info attributes
if (infoNodeAttr != null) {
infoNodeAttr.getAttributes(path, isDir, attributes);
}
// Gets the attributes located on the current entry path
getPerDirectoryEntryAttributes(path, isDir, operationType,
workingTreeIterator, dirCacheIterator, other,
attributes);
workingTreeIterator, dirCacheIterator, other, attributes);
// Gets the attributes located in the global attribute file
AttributesNode globalNodeAttr = attributesNodeProvider
.getGlobalAttributesNode();
if (globalNodeAttr != null) {
globalNodeAttr.getAttributes(path, isDir, attributes);
}
} catch (IOException e) {
throw new JGitInternalException("Error while parsing attributes", e); //$NON-NLS-1$
}
// now after all attributes are collected - in the correct hierarchy
// order - remove all unspecified entries (the ! marker)
for (Attribute a : attributes.getAll()) {
if (a.getState() == State.UNSPECIFIED)
attributes.remove(a.getKey());
}
return attributes;
}
@ -1195,7 +1201,7 @@ public class TreeWalk implements AutoCloseable, AttributesProvider {
private void getPerDirectoryEntryAttributes(String path, boolean isDir,
OperationType opType, WorkingTreeIterator workingTreeIterator,
DirCacheIterator dirCacheIterator, CanonicalTreeParser other,
Map<String, Attribute> attributes)
Attributes attributes)
throws IOException {
// Prevents infinite recurrence
if (workingTreeIterator != null || dirCacheIterator != null
@ -1208,12 +1214,11 @@ public class TreeWalk implements AutoCloseable, AttributesProvider {
getPerDirectoryEntryAttributes(path, isDir, opType,
getParent(workingTreeIterator, WorkingTreeIterator.class),
getParent(dirCacheIterator, DirCacheIterator.class),
getParent(other, CanonicalTreeParser.class),
attributes);
getParent(other, CanonicalTreeParser.class), attributes);
}
}
private <T extends AbstractTreeIterator> T getParent(T current,
private static <T extends AbstractTreeIterator> T getParent(T current,
Class<T> type) {
if (current != null) {
AbstractTreeIterator parent = current.parent;
@ -1224,7 +1229,7 @@ public class TreeWalk implements AutoCloseable, AttributesProvider {
return null;
}
private <T> T getTree(Class<T> type) {
private <T extends AbstractTreeIterator> T getTree(Class<T> type) {
for (int i = 0; i < trees.length; i++) {
AbstractTreeIterator tree = trees[i];
if (type.isInstance(tree)) {

Loading…
Cancel
Save