cayenne-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From ntimof...@apache.org
Subject cayenne git commit: - hashCode() implementation for Expression - updated equals() and hashCode() for Property
Date Tue, 24 Jan 2017 15:05:16 GMT
Repository: cayenne
Updated Branches:
  refs/heads/master 27849ce59 -> 6f76f057a


 - hashCode() implementation for Expression
 - updated equals() and hashCode() for Property


Project: http://git-wip-us.apache.org/repos/asf/cayenne/repo
Commit: http://git-wip-us.apache.org/repos/asf/cayenne/commit/6f76f057
Tree: http://git-wip-us.apache.org/repos/asf/cayenne/tree/6f76f057
Diff: http://git-wip-us.apache.org/repos/asf/cayenne/diff/6f76f057

Branch: refs/heads/master
Commit: 6f76f057a1a32af2d09e4f5e0ba11ca8cfc51a40
Parents: 27849ce
Author: Nikita Timofeev <stariy95@gmail.com>
Authored: Tue Jan 24 18:05:03 2017 +0300
Committer: Nikita Timofeev <stariy95@gmail.com>
Committed: Tue Jan 24 18:05:03 2017 +0300

----------------------------------------------------------------------
 .../java/org/apache/cayenne/exp/Expression.java | 11 +++
 .../java/org/apache/cayenne/exp/Property.java   | 16 ++++-
 .../cayenne/exp/parser/ASTFunctionCall.java     | 13 ++++
 .../org/apache/cayenne/exp/parser/ASTList.java  |  6 ++
 .../org/apache/cayenne/exp/parser/ASTPath.java  |  5 ++
 .../apache/cayenne/exp/parser/ASTScalar.java    | 16 ++++-
 .../org/apache/cayenne/exp/PropertyTest.java    | 43 +++++++++++-
 .../cayenne/exp/parser/ASTDbPathTest.java       | 14 +++-
 .../cayenne/exp/parser/ASTFunctionCallTest.java | 73 ++++++++++++++++++++
 .../apache/cayenne/exp/parser/ASTListTest.java  | 45 ++++++++++--
 .../cayenne/exp/parser/ASTScalarTest.java       | 57 +++++++++++++++
 11 files changed, 285 insertions(+), 14 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cayenne/blob/6f76f057/cayenne-server/src/main/java/org/apache/cayenne/exp/Expression.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/exp/Expression.java b/cayenne-server/src/main/java/org/apache/cayenne/exp/Expression.java
index a422f7e..b302a04 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/exp/Expression.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/exp/Expression.java
@@ -33,6 +33,7 @@ import java.util.Map;
 import org.apache.cayenne.CayenneRuntimeException;
 import org.apache.cayenne.exp.parser.ASTScalar;
 import org.apache.cayenne.util.ConversionUtil;
+import org.apache.cayenne.util.HashCodeBuilder;
 import org.apache.cayenne.util.Util;
 import org.apache.cayenne.util.XMLEncoder;
 import org.apache.cayenne.util.XMLSerializable;
@@ -259,6 +260,16 @@ public abstract class Expression implements Serializable, XMLSerializable
{
 		return true;
 	}
 
+	@Override
+	public int hashCode() {
+		HashCodeBuilder builder = new HashCodeBuilder().append(getType());
+		int opCount = getOperandCount();
+		for(int i=0; i<opCount; i++) {
+			builder.append(getOperand(i));
+		}
+		return builder.toHashCode();
+	}
+
 	/**
 	 * Returns a type of expression. Most common types are defined as public
 	 * static fields of this interface.

http://git-wip-us.apache.org/repos/asf/cayenne/blob/6f76f057/cayenne-server/src/main/java/org/apache/cayenne/exp/Property.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/exp/Property.java b/cayenne-server/src/main/java/org/apache/cayenne/exp/Property.java
index 47ac8f0..3ec518a 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/exp/Property.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/exp/Property.java
@@ -155,12 +155,22 @@ public class Property<E> {
 
     @Override
     public int hashCode() {
-        return getName().hashCode();
+        int result = name != null ? name.hashCode() : expressionProvider.get().hashCode();
+        if(type != null) {
+            result = 31 * result + type.hashCode();
+        }
+        return result;
     }
 
     @Override
-    public boolean equals(Object obj) {
-        return obj instanceof Property && ((Property<?>) obj).getName().equals(getName());
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+
+        Property<?> property = (Property<?>) o;
+        if (name != null ? !name.equals(property.name) : property.name != null) return false;
+        if (name == null && !expressionProvider.get().equals(property.expressionProvider.get()))
return false;
+        return (type == null ? property.type == null : type.equals(property.type));
     }
 
     /**

http://git-wip-us.apache.org/repos/asf/cayenne/blob/6f76f057/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTFunctionCall.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTFunctionCall.java
b/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTFunctionCall.java
index 7ce114d..004a306 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTFunctionCall.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTFunctionCall.java
@@ -65,4 +65,17 @@ public abstract class ASTFunctionCall extends SimpleNode {
         return functionName;
     }
 
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        if (!super.equals(o)) return false;
+        ASTFunctionCall that = (ASTFunctionCall) o;
+        return functionName.equals(that.functionName);
+    }
+
+    @Override
+    public int hashCode() {
+        return 31 * super.hashCode() + functionName.hashCode();
+    }
 }

http://git-wip-us.apache.org/repos/asf/cayenne/blob/6f76f057/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTList.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTList.java b/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTList.java
index 806bb18..e9e4d56 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTList.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTList.java
@@ -24,6 +24,7 @@ import org.apache.cayenne.exp.Expression;
 
 import java.io.IOException;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collection;
 import java.util.Iterator;
 import java.util.List;
@@ -225,4 +226,9 @@ public class ASTList extends SimpleNode {
 		// clean children - we are not supposed to use them anymore
 		children = null;
 	}
+
+	@Override
+	public int hashCode() {
+		return Arrays.hashCode(values);
+	}
 }

http://git-wip-us.apache.org/repos/asf/cayenne/blob/6f76f057/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTPath.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTPath.java b/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTPath.java
index 2eb4170..8ab1a39 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTPath.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTPath.java
@@ -105,4 +105,9 @@ public abstract class ASTPath extends SimpleNode {
 		throw new UnsupportedOperationException("No operator for '" + ExpressionParserTreeConstants.jjtNodeName[id]
 				+ "'");
 	}
+
+	@Override
+	public int hashCode() {
+		return path.hashCode();
+	}
 }

http://git-wip-us.apache.org/repos/asf/cayenne/blob/6f76f057/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTScalar.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTScalar.java b/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTScalar.java
index fb1f743..ab73fda 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTScalar.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTScalar.java
@@ -111,7 +111,19 @@ public class ASTScalar extends SimpleNode {
 
     @Override
     protected String getExpressionOperator(int index) {
-        throw new UnsupportedOperationException("No operator for '" + ExpressionParserTreeConstants.jjtNodeName[id]
-                + "'");
+        throw new UnsupportedOperationException("No operator for '" + ExpressionParserTreeConstants.jjtNodeName[id]
+ "'");
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        ASTScalar other = (ASTScalar) o;
+        return value != null ? value.equals(other.value) : other.value == null;
+    }
+
+    @Override
+    public int hashCode() {
+        return value != null ? value.hashCode() : 0;
     }
 }

http://git-wip-us.apache.org/repos/asf/cayenne/blob/6f76f057/cayenne-server/src/test/java/org/apache/cayenne/exp/PropertyTest.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/exp/PropertyTest.java b/cayenne-server/src/test/java/org/apache/cayenne/exp/PropertyTest.java
index 757c4d6..a489d37 100644
--- a/cayenne-server/src/test/java/org/apache/cayenne/exp/PropertyTest.java
+++ b/cayenne-server/src/test/java/org/apache/cayenne/exp/PropertyTest.java
@@ -19,6 +19,7 @@
 package org.apache.cayenne.exp;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 
@@ -133,7 +134,7 @@ public class PropertyTest {
     }
 
     @Test
-    public void testEquals() {
+    public void testEqualsWithName() {
         Property<Integer> INT_FIELD = new Property<>("intField");
         Property<Integer> INT_FIELD2 = new Property<>("intField");
 
@@ -142,7 +143,7 @@ public class PropertyTest {
     }
 
     @Test
-    public void testHashCode() {
+    public void testHashCodeWithName() {
         Property<Integer> INT_FIELD = new Property<>("intField");
         Property<Integer> INT_FIELD2 = new Property<>("intField");
         Property<Long> LONG_FIELD = new Property<>("longField");
@@ -152,6 +153,44 @@ public class PropertyTest {
     }
 
     @Test
+    public void testEqualsWithNameAndType() {
+        Property<Integer> INT_FIELD = new Property<>("intField", Integer.class);
+        Property<Integer> INT_FIELD2 = new Property<>("intField", Integer.class);
+
+        assertTrue(INT_FIELD != INT_FIELD2);
+        assertTrue(INT_FIELD.equals(INT_FIELD2));
+    }
+
+    @Test
+    public void testHashCodeWithNameAndType() {
+        Property<Integer> INT_FIELD = new Property<>("intField", Integer.class);
+        Property<Integer> INT_FIELD2 = new Property<>("intField", Integer.class);
+        Property<Long> LONG_FIELD = new Property<>("longField", Long.class);
+
+        assertTrue(INT_FIELD.hashCode() == INT_FIELD2.hashCode());
+        assertTrue(INT_FIELD.hashCode() != LONG_FIELD.hashCode());
+    }
+
+    @Test
+    public void testEqualsWithExpAndType() {
+        Property<Integer> INT_FIELD = new Property<>(null, ExpressionFactory.exp("1"),
Integer.class);
+        Property<Integer> INT_FIELD2 = new Property<>(null, ExpressionFactory.exp("1"),
Integer.class);
+
+        assertTrue(INT_FIELD != INT_FIELD2);
+        assertTrue(INT_FIELD.equals(INT_FIELD2));
+    }
+
+    @Test
+    public void testHashCodeWithExpAndType() {
+        Property<Integer> INT_FIELD = new Property<>(null, ExpressionFactory.exp("1"),
Integer.class);
+        Property<Integer> INT_FIELD2 = new Property<>(null, ExpressionFactory.exp("1"),
Integer.class);
+        Property<Integer> INT_FIELD3 = new Property<>(null, ExpressionFactory.exp("2"),
Integer.class);
+
+        assertEquals(INT_FIELD.hashCode(), INT_FIELD2.hashCode());
+        assertNotEquals(INT_FIELD.hashCode(), INT_FIELD3.hashCode());
+    }
+
+    @Test
     public void testOuter() {
         Property<String> inner = new Property<>("xyz");
         assertEquals("xyz+", inner.outer().getName());

http://git-wip-us.apache.org/repos/asf/cayenne/blob/6f76f057/cayenne-server/src/test/java/org/apache/cayenne/exp/parser/ASTDbPathTest.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/exp/parser/ASTDbPathTest.java
b/cayenne-server/src/test/java/org/apache/cayenne/exp/parser/ASTDbPathTest.java
index 4e9f243..c4d20ec 100644
--- a/cayenne-server/src/test/java/org/apache/cayenne/exp/parser/ASTDbPathTest.java
+++ b/cayenne-server/src/test/java/org/apache/cayenne/exp/parser/ASTDbPathTest.java
@@ -23,6 +23,7 @@ import org.junit.Test;
 import java.io.IOException;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
 
 public class ASTDbPathTest {
 
@@ -37,6 +38,15 @@ public class ASTDbPathTest {
         new ASTDbPath("x.y").appendAsString(buffer);
         assertEquals("db:x.y", buffer.toString());
     }
-    
-    
+
+    @Test
+    public void testEquals() throws Exception {
+        ASTPath path1 = new ASTDbPath("x.y.z");
+        ASTPath path2 = new ASTDbPath("x.y.z");
+        ASTPath path3 = new ASTDbPath("x.x.z");
+
+        assertEquals(path1, path2);
+        assertNotEquals(path1, path3);
+    }
+
 }

http://git-wip-us.apache.org/repos/asf/cayenne/blob/6f76f057/cayenne-server/src/test/java/org/apache/cayenne/exp/parser/ASTFunctionCallTest.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/exp/parser/ASTFunctionCallTest.java
b/cayenne-server/src/test/java/org/apache/cayenne/exp/parser/ASTFunctionCallTest.java
new file mode 100644
index 0000000..452f406
--- /dev/null
+++ b/cayenne-server/src/test/java/org/apache/cayenne/exp/parser/ASTFunctionCallTest.java
@@ -0,0 +1,73 @@
+/*****************************************************************
+ *   Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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.apache.cayenne.exp.parser;
+
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * @since 4.0
+ */
+public class ASTFunctionCallTest {
+
+    @Test
+    public void testEquals() throws Exception {
+        ASTCount count1 = new ASTCount();
+        ASTCount count2 = new ASTCount();
+        ASTCount count3 = new ASTCount(new ASTDbPath("y"));
+        ASTSum sum1 = new ASTSum(new ASTDbPath("x"));
+        ASTSum sum2 = new ASTSum(new ASTDbPath("x"));
+        ASTSum sum3 = new ASTSum(null);
+        ASTSum sum4 = new ASTSum(new ASTDbPath("y"));
+
+        assertEquals(count1, count2);
+        assertEquals(sum1, sum2);
+
+        assertNotEquals(count1, count3);
+        assertNotEquals(count1, sum1);
+        assertNotEquals(count3, sum4);
+        assertNotEquals(sum1, sum3);
+        assertNotEquals(sum1, sum4);
+        assertNotEquals(sum3, sum4);
+    }
+
+    @Test
+    public void testHashCode() throws Exception {
+        ASTCount count1 = new ASTCount();
+        ASTCount count2 = new ASTCount();
+        ASTCount count3 = new ASTCount(new ASTDbPath("y"));
+        ASTSum sum1 = new ASTSum(new ASTDbPath("x"));
+        ASTSum sum2 = new ASTSum(new ASTDbPath("x"));
+        ASTSum sum3 = new ASTSum(null);
+        ASTSum sum4 = new ASTSum(new ASTDbPath("y"));
+
+        assertEquals(count1.hashCode(), count2.hashCode());
+        assertEquals(sum1.hashCode(), sum2.hashCode());
+
+        assertNotEquals(count1.hashCode(), count3.hashCode());
+        assertNotEquals(count1.hashCode(), sum1.hashCode());
+        assertNotEquals(count3.hashCode(), sum4.hashCode());
+        assertNotEquals(sum1.hashCode(), sum3.hashCode());
+        assertNotEquals(sum1.hashCode(), sum4.hashCode());
+        assertNotEquals(sum3.hashCode(), sum4.hashCode());
+    }
+
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cayenne/blob/6f76f057/cayenne-server/src/test/java/org/apache/cayenne/exp/parser/ASTListTest.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/exp/parser/ASTListTest.java b/cayenne-server/src/test/java/org/apache/cayenne/exp/parser/ASTListTest.java
index c62c373..0df2523 100644
--- a/cayenne-server/src/test/java/org/apache/cayenne/exp/parser/ASTListTest.java
+++ b/cayenne-server/src/test/java/org/apache/cayenne/exp/parser/ASTListTest.java
@@ -23,9 +23,11 @@ import org.apache.cayenne.Persistent;
 import org.junit.Test;
 
 import java.util.ArrayList;
-import java.util.Arrays;
+import java.util.Collections;
 import java.util.List;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
@@ -38,13 +40,46 @@ public class ASTListTest {
 		Persistent artist = mock(Persistent.class);
 		when(artist.getObjectId()).thenReturn(objectId);
 
-		ASTList exp = new ASTList(Arrays.asList(artist));
-		assertNotNull(exp);
+		ASTList exp = new ASTList(Collections.singletonList(artist));
+		assertNotNull(exp.getOperand(0));
 
-		List<Persistent> collection = new ArrayList<Persistent>();
+		List<Persistent> collection = new ArrayList<>();
 		collection.add(artist);
 		exp = new ASTList(collection);
-		assertNotNull(exp);
+		assertNotNull(exp.getOperand(0));
+	}
+
+	@Test
+	public void testEquals() throws Exception {
+		ObjectId objectId = new ObjectId("Artist", "ARTIST_ID", 1);
+		Persistent artist = mock(Persistent.class);
+		when(artist.getObjectId()).thenReturn(objectId);
+
+		ASTList exp = new ASTList(Collections.singletonList(artist));
+
+		List<Persistent> collection = new ArrayList<>();
+		collection.add(artist);
+		ASTList exp2 = new ASTList(collection);
+		ASTList exp3 = new ASTList(Collections.emptyList());
+
+		assertEquals(exp, exp2);
+		assertNotEquals(exp, exp3);
+	}
+
+	@Test
+	public void testHashCode() throws Exception {
+		ObjectId objectId = new ObjectId("Artist", "ARTIST_ID", 1);
+		Persistent artist = mock(Persistent.class);
+		when(artist.getObjectId()).thenReturn(objectId);
+
+		ASTList exp = new ASTList(Collections.singletonList(artist));
+		List<Persistent> collection = new ArrayList<>();
+		collection.add(artist);
+		ASTList exp2 = new ASTList(collection);
+		ASTList exp3 = new ASTList(Collections.emptyList());
+
+		assertEquals(exp.hashCode(), exp2.hashCode());
+		assertNotEquals(exp.hashCode(), exp3.hashCode());
 	}
     
 }

http://git-wip-us.apache.org/repos/asf/cayenne/blob/6f76f057/cayenne-server/src/test/java/org/apache/cayenne/exp/parser/ASTScalarTest.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/exp/parser/ASTScalarTest.java
b/cayenne-server/src/test/java/org/apache/cayenne/exp/parser/ASTScalarTest.java
new file mode 100644
index 0000000..b73617f
--- /dev/null
+++ b/cayenne-server/src/test/java/org/apache/cayenne/exp/parser/ASTScalarTest.java
@@ -0,0 +1,57 @@
+/*****************************************************************
+ *   Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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.apache.cayenne.exp.parser;
+
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * @since 4.0
+ */
+public class ASTScalarTest {
+
+    @Test
+    public void testEquals() throws Exception {
+        ASTScalar strScalar1 = new ASTScalar("test");
+        ASTScalar strScalar2 = new ASTScalar("test");
+        assertEquals(strScalar1, strScalar2);
+
+        ASTScalar nullScalar1 = new ASTScalar(null);
+        ASTScalar nullScalar2 = new ASTScalar(null);
+        assertEquals(nullScalar1, nullScalar2);
+
+        assertNotEquals(strScalar1, nullScalar1);
+    }
+
+    @Test
+    public void testHashCode() throws Exception {
+        ASTScalar strScalar1 = new ASTScalar("test");
+        ASTScalar strScalar2 = new ASTScalar("test");
+        assertEquals(strScalar1.hashCode(), strScalar2.hashCode());
+
+        ASTScalar nullScalar1 = new ASTScalar(null);
+        ASTScalar nullScalar2 = new ASTScalar(null);
+        assertEquals(nullScalar1.hashCode(), nullScalar2.hashCode());
+
+        assertNotEquals(strScalar1.hashCode(), nullScalar1.hashCode());
+    }
+
+}
\ No newline at end of file


Mime
View raw message