You are viewing a plain text version of this content. The canonical link for it is here.
Posted to server-dev@james.apache.org by bt...@apache.org on 2019/11/20 01:56:49 UTC

[james-project] 21/49: [Refactoring] Property can be a POJO instead of an interface

This is an automated email from the ASF dual-hosted git repository.

btellier pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/james-project.git

commit 30161bbe07cd990d696b768954b3d0d010064914
Author: Benoit Tellier <bt...@linagora.com>
AuthorDate: Sat Nov 16 11:08:19 2019 +0700

    [Refactoring] Property can be a POJO instead of an interface
    
    Remove SimpleProperty
---
 .../cassandra/mail/CassandraMessageDAO.java        |   4 +-
 .../elasticsearch/json/IndexableMessage.java       |   4 +-
 .../elasticsearch/json/IndexableMessageTest.java   |   4 +-
 .../james/mailbox/jpa/mail/model/JPAProperty.java  |   3 +-
 .../james/mailbox/store/mail/model/Property.java   |  92 ++++++++++++++--
 .../store/mail/model/impl/PropertyBuilder.java     |  28 ++---
 .../store/mail/model/impl/SimpleProperty.java      | 116 ---------------------
 .../model/ListMessagePropertiesAssertTest.java     |  24 +----
 .../SimplePropertyTest.java => PropertyTest.java}  |   6 +-
 .../store/mail/model/impl/PropertyBuilderTest.java |   5 +-
 10 files changed, 117 insertions(+), 169 deletions(-)

diff --git a/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageDAO.java b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageDAO.java
index 0899903..e663b98 100644
--- a/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageDAO.java
+++ b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageDAO.java
@@ -70,8 +70,8 @@ import org.apache.james.mailbox.model.MessageAttachment;
 import org.apache.james.mailbox.model.MessageId;
 import org.apache.james.mailbox.store.mail.MessageMapper.FetchType;
 import org.apache.james.mailbox.store.mail.model.MailboxMessage;
+import org.apache.james.mailbox.store.mail.model.Property;
 import org.apache.james.mailbox.store.mail.model.impl.PropertyBuilder;
-import org.apache.james.mailbox.store.mail.model.impl.SimpleProperty;
 import org.apache.james.util.streams.Limit;
 
 import com.datastax.driver.core.BoundStatement;
@@ -278,7 +278,7 @@ public class CassandraMessageDAO {
     private PropertyBuilder getPropertyBuilder(Row row) {
         PropertyBuilder property = new PropertyBuilder(
             row.getList(PROPERTIES, UDTValue.class).stream()
-                .map(x -> new SimpleProperty(x.getString(Properties.NAMESPACE), x.getString(Properties.NAME), x.getString(Properties.VALUE)))
+                .map(x -> new Property(x.getString(Properties.NAMESPACE), x.getString(Properties.NAME), x.getString(Properties.VALUE)))
                 .collect(Collectors.toList()));
         property.setTextualLineCount(row.getLong(TEXTUAL_LINE_COUNT));
         return property;
diff --git a/mailbox/elasticsearch/src/main/java/org/apache/james/mailbox/elasticsearch/json/IndexableMessage.java b/mailbox/elasticsearch/src/main/java/org/apache/james/mailbox/elasticsearch/json/IndexableMessage.java
index 952e046..3409607 100644
--- a/mailbox/elasticsearch/src/main/java/org/apache/james/mailbox/elasticsearch/json/IndexableMessage.java
+++ b/mailbox/elasticsearch/src/main/java/org/apache/james/mailbox/elasticsearch/json/IndexableMessage.java
@@ -32,8 +32,8 @@ import org.apache.james.mailbox.elasticsearch.IndexAttachments;
 import org.apache.james.mailbox.elasticsearch.query.DateResolutionFormater;
 import org.apache.james.mailbox.extractor.TextExtractor;
 import org.apache.james.mailbox.store.mail.model.MailboxMessage;
+import org.apache.james.mailbox.store.mail.model.Property;
 import org.apache.james.mailbox.store.mail.model.impl.PropertyBuilder;
-import org.apache.james.mailbox.store.mail.model.impl.SimpleProperty;
 import org.apache.james.mailbox.store.search.SearchUtil;
 import org.apache.james.mime4j.MimeException;
 
@@ -191,7 +191,7 @@ public class IndexableMessage {
         }
     }
 
-    public static final SimpleProperty HAS_ATTACHMENT_PROPERTY = new SimpleProperty(PropertyBuilder.JAMES_INTERNALS, PropertyBuilder.HAS_ATTACHMENT, "true");
+    public static final Property HAS_ATTACHMENT_PROPERTY = new Property(PropertyBuilder.JAMES_INTERNALS, PropertyBuilder.HAS_ATTACHMENT, "true");
 
     public static Builder builder() {
         return new Builder();
diff --git a/mailbox/elasticsearch/src/test/java/org/apache/james/mailbox/elasticsearch/json/IndexableMessageTest.java b/mailbox/elasticsearch/src/test/java/org/apache/james/mailbox/elasticsearch/json/IndexableMessageTest.java
index efc276d..1d658dd 100644
--- a/mailbox/elasticsearch/src/test/java/org/apache/james/mailbox/elasticsearch/json/IndexableMessageTest.java
+++ b/mailbox/elasticsearch/src/test/java/org/apache/james/mailbox/elasticsearch/json/IndexableMessageTest.java
@@ -40,8 +40,8 @@ import org.apache.james.mailbox.model.MessageId;
 import org.apache.james.mailbox.model.TestId;
 import org.apache.james.mailbox.store.extractor.DefaultTextExtractor;
 import org.apache.james.mailbox.store.mail.model.MailboxMessage;
+import org.apache.james.mailbox.store.mail.model.Property;
 import org.apache.james.mailbox.store.mail.model.impl.PropertyBuilder;
-import org.apache.james.mailbox.store.mail.model.impl.SimpleProperty;
 import org.apache.james.mailbox.tika.TikaConfiguration;
 import org.apache.james.mailbox.tika.TikaContainerSingletonRule;
 import org.apache.james.mailbox.tika.TikaHttpClientImpl;
@@ -333,7 +333,7 @@ public class IndexableMessageTest {
         when(mailboxMessage.getUid())
             .thenReturn(MESSAGE_UID);
         when(mailboxMessage.getProperties())
-            .thenReturn(ImmutableList.of(new SimpleProperty(PropertyBuilder.JAMES_INTERNALS, PropertyBuilder.HAS_ATTACHMENT, "false")));
+            .thenReturn(ImmutableList.of(new Property(PropertyBuilder.JAMES_INTERNALS, PropertyBuilder.HAS_ATTACHMENT, "false")));
 
         // When
         IndexableMessage indexableMessage = IndexableMessage.builder()
diff --git a/mailbox/jpa/src/main/java/org/apache/james/mailbox/jpa/mail/model/JPAProperty.java b/mailbox/jpa/src/main/java/org/apache/james/mailbox/jpa/mail/model/JPAProperty.java
index 0eba4df..ee7c54e 100644
--- a/mailbox/jpa/src/main/java/org/apache/james/mailbox/jpa/mail/model/JPAProperty.java
+++ b/mailbox/jpa/src/main/java/org/apache/james/mailbox/jpa/mail/model/JPAProperty.java
@@ -28,7 +28,6 @@ import javax.persistence.Id;
 import javax.persistence.Table;
 
 import org.apache.james.mailbox.store.mail.model.Property;
-import org.apache.james.mailbox.store.mail.model.impl.SimpleProperty;
 import org.apache.openjpa.persistence.jdbc.Index;
 
 @Entity(name = "Property")
@@ -98,7 +97,7 @@ public class JPAProperty {
     }
 
     public Property toProperty() {
-        return new SimpleProperty(namespace, localName, value);
+        return new Property(namespace, localName, value);
     }
 
     @Override
diff --git a/mailbox/store/src/main/java/org/apache/james/mailbox/store/mail/model/Property.java b/mailbox/store/src/main/java/org/apache/james/mailbox/store/mail/model/Property.java
index 742204e..2e0e7a3 100644
--- a/mailbox/store/src/main/java/org/apache/james/mailbox/store/mail/model/Property.java
+++ b/mailbox/store/src/main/java/org/apache/james/mailbox/store/mail/model/Property.java
@@ -18,6 +18,9 @@
  ****************************************************************/
 package org.apache.james.mailbox.store.mail.model;
 
+import java.util.Objects;
+
+import com.google.common.base.MoreObjects;
 
 /**
  * <p>Values a namespaced property.</p>
@@ -37,25 +40,102 @@ package org.apache.james.mailbox.store.mail.model;
  * object "BASE64".
  * </p>
  */
-public interface Property {
+public class Property {
+    private final String namespace;
+    private final String localName;
+    private final String value;
+
+    /**
+     * Construct a property.
+     * @param namespace not null
+     * @param localName not null
+     * @param value not null
+     */
+    public Property(String namespace, String localName, String value) {
+        super();
+        this.namespace = namespace;
+        this.localName = localName;
+        this.value = value;
+    }
+
+    public Property(Property property) {
+        this(property.getNamespace(), property.getLocalName(), property.getValue());
+    }
 
     /**
      * Gets the namespace for the name.
      * @return not null
      */
-    String getNamespace();
-    
+    public String getNamespace() {
+        return namespace;
+    }
+
     /**
      * Gets the local part of the name of the property.
      * @return not null
      */
-    String getLocalName();
-    
+    public String getLocalName() {
+        return localName;
+    }
+
     /**
      * Gets the value for this property.
      * @return not null
      */
-    String getValue();
+    public String getValue() {
+        return value;
+    }
+
+    /**
+     * Does the full name of the property match that given?
+     * @param namespace not null
+     * @param localName not null
+     * @return true when namespaces and local names match,
+     * false otherwise
+     */
+    public boolean isNamed(String namespace, String localName) {
+        return namespace.equals(this.namespace) && localName.equals(this.localName);
+    }
+
+    /**
+     * Is this property in the given namespace?
+     * @param namespace not null
+     * @return true when this property is in the given namespace,
+     * false otherwise
+     */
+    public boolean isInSpace(String namespace) {
+        return this.namespace.equals(namespace);
+    }
+
+    /**
+     * Constructs a <code>String</code> with all attributes
+     * in name = value format.
+     *
+     * @return a <code>String</code> representation
+     * of this object.
+     */
+    public String toString() {
+        return MoreObjects.toStringHelper(this)
+            .add("namespace", namespace)
+            .add("localName", localName)
+            .add("value", value)
+            .toString();
+    }
+
+    @Override
+    public final boolean equals(Object o) {
+        if (o instanceof Property) {
+            Property that = (Property) o;
 
+            return Objects.equals(namespace, that.namespace) &&
+                Objects.equals(localName, that.localName) &&
+                Objects.equals(value, that.value);
+        }
+        return false;
+    }
 
+    @Override
+    public final int hashCode() {
+        return Objects.hash(namespace, localName, value);
+    }
 }
diff --git a/mailbox/store/src/main/java/org/apache/james/mailbox/store/mail/model/impl/PropertyBuilder.java b/mailbox/store/src/main/java/org/apache/james/mailbox/store/mail/model/impl/PropertyBuilder.java
index f25ebec..1a59241 100644
--- a/mailbox/store/src/main/java/org/apache/james/mailbox/store/mail/model/impl/PropertyBuilder.java
+++ b/mailbox/store/src/main/java/org/apache/james/mailbox/store/mail/model/impl/PropertyBuilder.java
@@ -69,13 +69,13 @@ public class PropertyBuilder {
     }
 
     private Long textualLineCount;
-    private final List<SimpleProperty> properties;
+    private final List<Property> properties;
 
     public PropertyBuilder(List<Property> props) {
         textualLineCount = null;
         properties = new ArrayList<>(props.size());
         for (Property property:props) {
-            properties.add(new SimpleProperty(property));
+            properties.add(new Property(property));
         }
     }
     
@@ -114,7 +114,7 @@ public class PropertyBuilder {
         return properties.stream()
             .filter(property -> property.isNamed(namespace, localName))
             .findFirst()
-            .map(SimpleProperty::getValue)
+            .map(Property::getValue)
             .orElse(null);
     }
     
@@ -127,7 +127,7 @@ public class PropertyBuilder {
     public List<String> getValues(String namespace, String localName) {
         return properties.stream()
             .filter(property -> property.isNamed(namespace, localName))
-            .map(SimpleProperty::getValue)
+            .map(Property::getValue)
             .collect(Guavate.toImmutableList());
     }
     
@@ -138,15 +138,15 @@ public class PropertyBuilder {
      * @param value null to remove property
      */
     public void setProperty(String namespace, String localName, String value) {
-        for (Iterator<SimpleProperty> it = properties.iterator();it.hasNext();) {
-            final SimpleProperty property = it.next();
+        for (Iterator<Property> it = properties.iterator();it.hasNext();) {
+            final Property property = it.next();
             if (property.isNamed(namespace, localName)) {
                 it.remove();
             }
         }
         
         if (value != null) {
-            properties.add(new SimpleProperty(namespace, localName, value));
+            properties.add(new Property(namespace, localName, value));
         }
     }
     
@@ -157,15 +157,15 @@ public class PropertyBuilder {
      * @param values null to remove property
      */
     public void setProperty(String namespace, String localName, List<String> values) {
-        for (Iterator<SimpleProperty> it = properties.iterator();it.hasNext();) {
-            final SimpleProperty property = it.next();
+        for (Iterator<Property> it = properties.iterator();it.hasNext();) {
+            final Property property = it.next();
             if (property.isNamed(namespace, localName)) {
                 it.remove();
             }
         }
         if (values != null) {
             for (String value:values) {
-                properties.add(new SimpleProperty(namespace, localName, value));
+                properties.add(new Property(namespace, localName, value));
             }
         }
     }
@@ -177,7 +177,7 @@ public class PropertyBuilder {
      */
     public SortedMap<String,String> getProperties(String namespace) {
         final SortedMap<String, String> parameters = new TreeMap<>();
-        for (SimpleProperty property : properties) {
+        for (Property property : properties) {
             if (property.isInSpace(namespace)) {
                 parameters.put(property.getLocalName(), property.getValue());
             }
@@ -193,14 +193,14 @@ public class PropertyBuilder {
      * @param valuesByLocalName not null
      */
     public void setProperties(String namespace, Map<String,String> valuesByLocalName) {
-        for (Iterator<SimpleProperty> it = properties.iterator();it.hasNext();) {
-            final SimpleProperty property = it.next();
+        for (Iterator<Property> it = properties.iterator();it.hasNext();) {
+            final Property property = it.next();
             if (property.isInSpace(namespace)) {
                 it.remove();
             }
         }
         for (Map.Entry<String, String> valueByLocalName:valuesByLocalName.entrySet()) {
-            properties.add(new SimpleProperty(namespace, valueByLocalName.getKey().toLowerCase(Locale.US), valueByLocalName.getValue()));
+            properties.add(new Property(namespace, valueByLocalName.getKey().toLowerCase(Locale.US), valueByLocalName.getValue()));
         }
     }
     
diff --git a/mailbox/store/src/main/java/org/apache/james/mailbox/store/mail/model/impl/SimpleProperty.java b/mailbox/store/src/main/java/org/apache/james/mailbox/store/mail/model/impl/SimpleProperty.java
deleted file mode 100644
index aef6244..0000000
--- a/mailbox/store/src/main/java/org/apache/james/mailbox/store/mail/model/impl/SimpleProperty.java
+++ /dev/null
@@ -1,116 +0,0 @@
-/****************************************************************
- * 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.james.mailbox.store.mail.model.impl;
-
-import java.util.Objects;
-
-import org.apache.james.mailbox.store.mail.model.Property;
-
-import com.google.common.base.MoreObjects;
-
-public final class SimpleProperty implements Property {
-    private final String namespace;
-    private final String localName;
-    private final String value;
-    
-    /**
-     * Construct a property.
-     * @param namespace not null
-     * @param localName not null
-     * @param value not null
-     */
-    public SimpleProperty(String namespace, String localName, String value) {
-        super();
-        this.namespace = namespace;
-        this.localName = localName;
-        this.value = value;
-    }
-    
-    public SimpleProperty(Property property) {
-        this(property.getNamespace(), property.getLocalName(), property.getValue());
-    }
-
-    @Override
-    public String getLocalName() {
-        return localName;
-    }
-    
-    @Override
-    public String getNamespace() {
-        return namespace;
-    }
-    
-    @Override
-    public String getValue() {
-        return value;
-    }
-    
-    /**
-     * Does the full name of the property match that given?
-     * @param namespace not null
-     * @param localName not null
-     * @return true when namespaces and local names match,
-     * false otherwise
-     */
-    public boolean isNamed(String namespace, String localName) {
-        return namespace.equals(this.namespace) && localName.equals(this.localName);
-    }
-    
-    /**
-     * Is this property in the given namespace?
-     * @param namespace not null
-     * @return true when this property is in the given namespace,
-     * false otherwise
-     */
-    public boolean isInSpace(String namespace) {
-        return this.namespace.equals(namespace);
-    }
-
-    /**
-     * Constructs a <code>String</code> with all attributes
-     * in name = value format.
-     *
-     * @return a <code>String</code> representation 
-     * of this object.
-     */
-    public String toString() {
-        return MoreObjects.toStringHelper(this)
-            .add("namespace", namespace)
-            .add("localName", localName)
-            .add("value", value)
-            .toString();
-    }
-
-    @Override
-    public final boolean equals(Object o) {
-        if (o instanceof SimpleProperty) {
-            SimpleProperty that = (SimpleProperty) o;
-
-            return Objects.equals(namespace, that.namespace) &&
-                Objects.equals(localName, that.localName) &&
-                Objects.equals(value, that.value);
-        }
-        return false;
-    }
-
-    @Override
-    public final int hashCode() {
-        return Objects.hash(namespace, localName, value);
-    }
-}
diff --git a/mailbox/store/src/test/java/org/apache/james/mailbox/store/mail/model/ListMessagePropertiesAssertTest.java b/mailbox/store/src/test/java/org/apache/james/mailbox/store/mail/model/ListMessagePropertiesAssertTest.java
index 2228d00..36bedef 100644
--- a/mailbox/store/src/test/java/org/apache/james/mailbox/store/mail/model/ListMessagePropertiesAssertTest.java
+++ b/mailbox/store/src/test/java/org/apache/james/mailbox/store/mail/model/ListMessagePropertiesAssertTest.java
@@ -23,7 +23,6 @@ import static org.apache.james.mailbox.store.mail.model.ListMessagePropertiesAss
 
 import java.util.List;
 
-import org.apache.james.mailbox.store.mail.model.impl.SimpleProperty;
 import org.junit.Before;
 import org.junit.Test;
 
@@ -37,8 +36,8 @@ public class ListMessagePropertiesAssertTest {
     private static final String LOCAL_NAME = StandardNames.MIME_CONTENT_TRANSFER_ENCODING_NAME;
     private static final String NAMESPACE = StandardNames.NAMESPACE_RFC_2045;
 
-    private static final Property PROPERTY1 = new SimpleProperty(NAMESPACE, LOCAL_NAME, VALUE);
-    private static final Property PROPERTY2 = new SimpleProperty(OTHER_NAMESPACE, OTHER_LOCAL_NAME, OTHER_VALUE);
+    private static final Property PROPERTY1 = new Property(NAMESPACE, LOCAL_NAME, VALUE);
+    private static final Property PROPERTY2 = new Property(OTHER_NAMESPACE, OTHER_LOCAL_NAME, OTHER_VALUE);
     
     private List<Property> actual;
     
@@ -76,22 +75,7 @@ public class ListMessagePropertiesAssertTest {
             createProperty(NAMESPACE, LOCAL_NAME, OTHER_VALUE)));
     }
 
-    private Property createProperty(final String namespace, final String name, final String value) {
-        return new Property() {
-            @Override
-            public String getValue() {
-                return value;
-            }
-            
-            @Override
-            public String getNamespace() {
-                return namespace;
-            }
-            
-            @Override
-            public String getLocalName() {
-                return name;
-            }
-        };
+    private Property createProperty(String namespace, String name, String value) {
+        return new Property(namespace, name, value);
     }
 }
diff --git a/mailbox/store/src/test/java/org/apache/james/mailbox/store/mail/model/impl/SimplePropertyTest.java b/mailbox/store/src/test/java/org/apache/james/mailbox/store/mail/model/PropertyTest.java
similarity index 90%
rename from mailbox/store/src/test/java/org/apache/james/mailbox/store/mail/model/impl/SimplePropertyTest.java
rename to mailbox/store/src/test/java/org/apache/james/mailbox/store/mail/model/PropertyTest.java
index 5d5e979..31aa0d7 100644
--- a/mailbox/store/src/test/java/org/apache/james/mailbox/store/mail/model/impl/SimplePropertyTest.java
+++ b/mailbox/store/src/test/java/org/apache/james/mailbox/store/mail/model/PropertyTest.java
@@ -17,16 +17,16 @@
  * under the License.                                           *
  ****************************************************************/
 
-package org.apache.james.mailbox.store.mail.model.impl;
+package org.apache.james.mailbox.store.mail.model;
 
 import org.junit.jupiter.api.Test;
 
 import nl.jqno.equalsverifier.EqualsVerifier;
 
-class SimplePropertyTest {
+class PropertyTest {
     @Test
     void shouldMatchBeanContract() {
-        EqualsVerifier.forClass(SimpleProperty.class)
+        EqualsVerifier.forClass(Property.class)
             .verify();
     }
 }
\ No newline at end of file
diff --git a/mailbox/store/src/test/java/org/apache/james/mailbox/store/mail/model/impl/PropertyBuilderTest.java b/mailbox/store/src/test/java/org/apache/james/mailbox/store/mail/model/impl/PropertyBuilderTest.java
index 79b1548..320faf6 100644
--- a/mailbox/store/src/test/java/org/apache/james/mailbox/store/mail/model/impl/PropertyBuilderTest.java
+++ b/mailbox/store/src/test/java/org/apache/james/mailbox/store/mail/model/impl/PropertyBuilderTest.java
@@ -21,6 +21,7 @@ package org.apache.james.mailbox.store.mail.model.impl;
 
 import static org.assertj.core.api.Assertions.assertThat;
 
+import org.apache.james.mailbox.store.mail.model.Property;
 import org.junit.Test;
 
 public class PropertyBuilderTest {
@@ -35,7 +36,7 @@ public class PropertyBuilderTest {
         PropertyBuilder propertyBuilder = new PropertyBuilder();
         propertyBuilder.setHasAttachment(false);
         assertThat(propertyBuilder.toProperties())
-            .containsOnly(new SimpleProperty(PropertyBuilder.JAMES_INTERNALS, PropertyBuilder.HAS_ATTACHMENT, "false"));
+            .containsOnly(new Property(PropertyBuilder.JAMES_INTERNALS, PropertyBuilder.HAS_ATTACHMENT, "false"));
     }
 
     @Test
@@ -43,7 +44,7 @@ public class PropertyBuilderTest {
         PropertyBuilder propertyBuilder = new PropertyBuilder();
         propertyBuilder.setHasAttachment(true);
         assertThat(propertyBuilder.toProperties())
-            .containsOnly(new SimpleProperty(PropertyBuilder.JAMES_INTERNALS, PropertyBuilder.HAS_ATTACHMENT, "true"));
+            .containsOnly(new Property(PropertyBuilder.JAMES_INTERNALS, PropertyBuilder.HAS_ATTACHMENT, "true"));
     }
 
 }


---------------------------------------------------------------------
To unsubscribe, e-mail: server-dev-unsubscribe@james.apache.org
For additional commands, e-mail: server-dev-help@james.apache.org