cayenne-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From aadamc...@apache.org
Subject [1/2] cayenne git commit: Refactoring MergerContext:
Date Thu, 29 Sep 2016 09:13:12 GMT
Repository: cayenne
Updated Branches:
  refs/heads/master dacbe043f -> 700623048


Refactoring MergerContext:

* No need for it to be an interface. It is a simple holder of values
* Adding builder API to avoid constructor insanity (we are expecting more properties)


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

Branch: refs/heads/master
Commit: 2a205976f8805ef375beddf14433c999e0232453
Parents: dacbe04
Author: Andrus Adamchik <andrus@objectstyle.com>
Authored: Thu Sep 29 11:28:08 2016 +0300
Committer: Andrus Adamchik <andrus@objectstyle.com>
Committed: Thu Sep 29 11:28:08 2016 +0300

----------------------------------------------------------------------
 .../apache/cayenne/merge/AbstractToDbToken.java | 12 +--
 .../apache/cayenne/merge/CreateTableToDb.java   | 10 +--
 .../merge/DefaultModelMergeDelegate.java        | 17 +++-
 .../cayenne/merge/ExecutingMergerContext.java   | 73 ----------------
 .../org/apache/cayenne/merge/MergerContext.java | 89 ++++++++++++++++++--
 .../org/apache/cayenne/merge/MergeCase.java     | 20 ++---
 .../cayenne/merge/TokensToModelExecution.java   |  4 +-
 .../tools/dbimport/DbImportActionDefault.java   | 18 ++--
 .../modeler/dialog/db/MergerOptions.java        | 35 ++++----
 9 files changed, 142 insertions(+), 136 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cayenne/blob/2a205976/cayenne-server/src/main/java/org/apache/cayenne/merge/AbstractToDbToken.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/merge/AbstractToDbToken.java
b/cayenne-server/src/main/java/org/apache/cayenne/merge/AbstractToDbToken.java
index e22d789..7430ba3 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/merge/AbstractToDbToken.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/merge/AbstractToDbToken.java
@@ -18,17 +18,17 @@
  ****************************************************************/
 package org.apache.cayenne.merge;
 
-import java.sql.Connection;
-import java.sql.SQLException;
-import java.sql.Statement;
-import java.util.List;
-
 import org.apache.cayenne.dba.DbAdapter;
 import org.apache.cayenne.log.JdbcEventLogger;
 import org.apache.cayenne.map.DbAttribute;
 import org.apache.cayenne.map.DbEntity;
 import org.apache.cayenne.validation.SimpleValidationFailure;
 
+import java.sql.Connection;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.List;
+
 /**
  * Common abstract superclass for all {@link MergerToken}s going from the model
  * to the database.
@@ -53,7 +53,7 @@ public abstract class AbstractToDbToken implements MergerToken, Comparable<Merge
 
 	@Override
 	public void execute(MergerContext mergerContext) {
-		for (String sql : createSql(mergerContext.getAdapter())) {
+		for (String sql : createSql(mergerContext.getDataNode().getAdapter())) {
 			executeSql(mergerContext, sql);
 		}
 	}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/2a205976/cayenne-server/src/main/java/org/apache/cayenne/merge/CreateTableToDb.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/merge/CreateTableToDb.java b/cayenne-server/src/main/java/org/apache/cayenne/merge/CreateTableToDb.java
index 12f1003..077ec74 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/merge/CreateTableToDb.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/merge/CreateTableToDb.java
@@ -18,15 +18,15 @@
  ****************************************************************/
 package org.apache.cayenne.merge;
 
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
 import org.apache.cayenne.access.DataNode;
 import org.apache.cayenne.dba.DbAdapter;
 import org.apache.cayenne.map.DbEntity;
 import org.apache.cayenne.validation.SimpleValidationFailure;
 
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
 public class CreateTableToDb extends AbstractToDbToken.Entity {
 
     public CreateTableToDb(DbEntity entity) {
@@ -46,7 +46,7 @@ public class CreateTableToDb extends AbstractToDbToken.Entity {
     public void execute(MergerContext mergerContext) {
         try {
             DataNode node = mergerContext.getDataNode();
-            DbAdapter adapter = mergerContext.getAdapter();
+            DbAdapter adapter = node.getAdapter();
             adapter.getPkGenerator().createAutoPk(
                     node,
                     Collections.singletonList(getEntity()));

http://git-wip-us.apache.org/repos/asf/cayenne/blob/2a205976/cayenne-server/src/main/java/org/apache/cayenne/merge/DefaultModelMergeDelegate.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/merge/DefaultModelMergeDelegate.java
b/cayenne-server/src/main/java/org/apache/cayenne/merge/DefaultModelMergeDelegate.java
index 6a138a9..e457f31 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/merge/DefaultModelMergeDelegate.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/merge/DefaultModelMergeDelegate.java
@@ -26,50 +26,63 @@ import org.apache.cayenne.map.ObjEntity;
 import org.apache.cayenne.map.ObjRelationship;
 
 /**
- * A default implementation of {@link ModelMergeDelegate} that does nothing by
- * itself.
+ * A default noop implementation of {@link ModelMergeDelegate}.
  */
 public class DefaultModelMergeDelegate implements ModelMergeDelegate {
 
+    @Override
     public void dbAttributeAdded(DbAttribute att) {
     }
 
+    @Override
     public void dbAttributeModified(DbAttribute att) {
     }
 
+    @Override
     public void dbAttributeRemoved(DbAttribute att) {
     }
 
+    @Override
     public void dbEntityAdded(DbEntity ent) {
     }
 
+    @Override
     public void dbEntityRemoved(DbEntity ent) {
     }
 
+    @Override
     public void dbRelationshipAdded(DbRelationship rel) {
     }
 
+    @Override
     public void dbRelationshipRemoved(DbRelationship rel) {
     }
 
+    @Override
     public void objAttributeAdded(ObjAttribute att) {
     }
 
+    @Override
     public void objAttributeModified(ObjAttribute att) {
     }
 
+    @Override
     public void objAttributeRemoved(ObjAttribute att) {
     }
 
+    @Override
     public void objEntityAdded(ObjEntity ent) {
     }
 
+    @Override
     public void objEntityRemoved(ObjEntity ent) {
     }
 
+    @Override
     public void objRelationshipAdded(ObjRelationship rel) {
     }
 
+    @Override
     public void objRelationshipRemoved(ObjRelationship rel) {
     }
 

http://git-wip-us.apache.org/repos/asf/cayenne/blob/2a205976/cayenne-server/src/main/java/org/apache/cayenne/merge/ExecutingMergerContext.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/merge/ExecutingMergerContext.java
b/cayenne-server/src/main/java/org/apache/cayenne/merge/ExecutingMergerContext.java
deleted file mode 100644
index 6a357a7..0000000
--- a/cayenne-server/src/main/java/org/apache/cayenne/merge/ExecutingMergerContext.java
+++ /dev/null
@@ -1,73 +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.cayenne.merge;
-
-import javax.sql.DataSource;
-
-import org.apache.cayenne.access.DataNode;
-import org.apache.cayenne.dba.DbAdapter;
-import org.apache.cayenne.dba.JdbcAdapter;
-import org.apache.cayenne.map.DataMap;
-import org.apache.cayenne.validation.ValidationResult;
-
-public class ExecutingMergerContext implements MergerContext {
-
-    private DataMap map;
-    private DataNode node;
-    private ValidationResult result = new ValidationResult();
-    private ModelMergeDelegate delegate;
-
-    public ExecutingMergerContext(DataMap map, DataNode node) {
-        this.map = map;
-        this.node = node;
-        this.delegate = new DefaultModelMergeDelegate();
-    }
-
-    public ExecutingMergerContext(DataMap map, DataSource dataSource, JdbcAdapter adapter,
-            ModelMergeDelegate delegate) {
-        this.map = map;
-        // create a fake DataNode as lots of DbAdapter/PkGenerator methods
-        // take a DataNode instead of just a DataSource
-        this.node = new DataNode();
-        this.node.setDataSource(dataSource);
-        this.node.setAdapter(adapter);
-        this.delegate = delegate;
-    }
-
-    public DbAdapter getAdapter() {
-        return getDataNode().getAdapter();
-    }
-
-    public DataMap getDataMap() {
-        return map;
-    }
-
-    public DataNode getDataNode() {
-        return node;
-    }
-
-    public ValidationResult getValidationResult() {
-        return result;
-    }
-
-    public ModelMergeDelegate getModelMergeDelegate() {
-        return delegate;
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/2a205976/cayenne-server/src/main/java/org/apache/cayenne/merge/MergerContext.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/merge/MergerContext.java b/cayenne-server/src/main/java/org/apache/cayenne/merge/MergerContext.java
index 22628e2..f592ebe 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/merge/MergerContext.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/merge/MergerContext.java
@@ -23,19 +23,96 @@ import org.apache.cayenne.dba.DbAdapter;
 import org.apache.cayenne.map.DataMap;
 import org.apache.cayenne.validation.ValidationResult;
 
+import javax.sql.DataSource;
+import java.util.Objects;
+
 /**
  * An object passed as an argument to {@link MergerToken#execute(MergerContext)}s that a
  * {@link MergerToken} can do its work.
  */
-public interface MergerContext {
+public class MergerContext {
+
+    private DataMap dataMap;
+    private DataNode dataNode;
+    private ValidationResult validationResult;
+    private ModelMergeDelegate delegate;
+
+    protected MergerContext() {
+    }
+
+    public static Builder builder(DataMap dataMap) {
+        return new Builder().dataMap(dataMap);
+    }
+
+    /**
+     * @deprecated since 4.0 use {@link #getDataNode()} and its {@link DataNode#getAdapter()}
method.
+     */
+    @Deprecated
+    public DbAdapter getAdapter() {
+        return getDataNode().getAdapter();
+    }
+
+    /**
+     * Returns the DataMap that is the target of a the merge operation.
+     *
+     * @return the DataMap that is the target of a the merge operation.
+     */
+    public DataMap getDataMap() {
+        return dataMap;
+    }
+
+    public DataNode getDataNode() {
+        return dataNode;
+    }
+
+    public ValidationResult getValidationResult() {
+        return validationResult;
+    }
+
+    /**
+     * Returns a callback object that is invoked as the merge proceeds through tokens, modifying
the DataMap.
+     *
+     * @return a callback object that is invoked as the merge proceeds through tokens, modifying
the DataMap.
+     */
+    public ModelMergeDelegate getModelMergeDelegate() {
+        return delegate;
+    }
+
+    public static class Builder {
+
+        private MergerContext context;
+
+        private Builder() {
+            this.context = new MergerContext();
+            this.context.validationResult = new ValidationResult();
+            this.context.delegate = new DefaultModelMergeDelegate();
+            this.context.dataNode = new DataNode();
+        }
 
-    ModelMergeDelegate getModelMergeDelegate();
+        public MergerContext build() {
+            return context;
+        }
 
-    DbAdapter getAdapter();
+        public Builder delegate(ModelMergeDelegate delegate) {
+            context.delegate = Objects.requireNonNull(delegate);
+            return this;
+        }
 
-    DataMap getDataMap();
+        public Builder dataNode(DataNode dataNode) {
+            this.context.dataNode = Objects.requireNonNull(dataNode);
+            return this;
+        }
 
-    DataNode getDataNode();
+        public Builder syntheticDataNode(DataSource dataSource, DbAdapter adapter) {
+            DataNode dataNode = new DataNode();
+            dataNode.setDataSource(dataSource);
+            dataNode.setAdapter(adapter);
+            return dataNode(dataNode);
+        }
 
-    ValidationResult getValidationResult();
+        public Builder dataMap(DataMap dataMap) {
+            context.dataMap = Objects.requireNonNull(dataMap);
+            return this;
+        }
+    }
 }

http://git-wip-us.apache.org/repos/asf/cayenne/blob/2a205976/cayenne-server/src/test/java/org/apache/cayenne/merge/MergeCase.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/merge/MergeCase.java b/cayenne-server/src/test/java/org/apache/cayenne/merge/MergeCase.java
index 2764c3a..dfbffa9 100644
--- a/cayenne-server/src/test/java/org/apache/cayenne/merge/MergeCase.java
+++ b/cayenne-server/src/test/java/org/apache/cayenne/merge/MergeCase.java
@@ -18,14 +18,6 @@
  ****************************************************************/
 package org.apache.cayenne.merge;
 
-import static org.junit.Assert.assertEquals;
-
-import java.sql.Connection;
-import java.sql.Statement;
-import java.sql.Types;
-import java.util.ArrayList;
-import java.util.List;
-
 import org.apache.cayenne.access.DataNode;
 import org.apache.cayenne.access.loader.DbLoaderConfiguration;
 import org.apache.cayenne.access.loader.filters.FiltersConfig;
@@ -48,6 +40,14 @@ import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 import org.junit.Before;
 
+import java.sql.Connection;
+import java.sql.Statement;
+import java.sql.Types;
+import java.util.ArrayList;
+import java.util.List;
+
+import static org.junit.Assert.assertEquals;
+
 @UseServerRuntime(CayenneProjects.TESTMAP_PROJECT)
 public abstract class MergeCase extends ServerCase {
 
@@ -146,14 +146,14 @@ public abstract class MergeCase extends ServerCase {
 	}
 
 	protected void execute(List<MergerToken> tokens) {
-		MergerContext mergerContext = new ExecutingMergerContext(map, node);
+		MergerContext mergerContext = MergerContext.builder(map).dataNode(node).build();
 		for (MergerToken tok : tokens) {
 			tok.execute(mergerContext);
 		}
 	}
 
 	protected void execute(MergerToken token) throws Exception {
-		MergerContext mergerContext = new ExecutingMergerContext(map, node);
+		MergerContext mergerContext = MergerContext.builder(map).dataNode(node).build();
 		token.execute(mergerContext);
 	}
 

http://git-wip-us.apache.org/repos/asf/cayenne/blob/2a205976/cayenne-server/src/test/java/org/apache/cayenne/merge/TokensToModelExecution.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/merge/TokensToModelExecution.java
b/cayenne-server/src/test/java/org/apache/cayenne/merge/TokensToModelExecution.java
index b5d63de..094914b 100644
--- a/cayenne-server/src/test/java/org/apache/cayenne/merge/TokensToModelExecution.java
+++ b/cayenne-server/src/test/java/org/apache/cayenne/merge/TokensToModelExecution.java
@@ -43,7 +43,7 @@ public class TokensToModelExecution {
         assertTrue(dataMap.getDbEntityMap().isEmpty());
         assertTrue(dataMap.getObjEntityMap().isEmpty());
 
-        MergerContext context = new ExecutingMergerContext(dataMap, new DataNode());
+        MergerContext context = MergerContext.builder(dataMap).dataNode(new DataNode()).build();
         factory().createCreateTableToModel(entity).execute(context);
 
         assertEquals(1, dataMap.getDbEntityMap().size());
@@ -64,7 +64,7 @@ public class TokensToModelExecution {
         assertEquals(1, dataMap.getDbEntityMap().size());
         assertTrue(dataMap.getObjEntityMap().isEmpty());
 
-        MergerContext context = new ExecutingMergerContext(dataMap, new DataNode());
+        MergerContext context = MergerContext.builder(dataMap).dataNode(new DataNode()).build();
         factory().createAddColumnToModel(entity, attr).execute(context);
 
         assertEquals(1, dataMap.getDbEntityMap().size());

http://git-wip-us.apache.org/repos/asf/cayenne/blob/2a205976/cayenne-tools/src/main/java/org/apache/cayenne/tools/dbimport/DbImportActionDefault.java
----------------------------------------------------------------------
diff --git a/cayenne-tools/src/main/java/org/apache/cayenne/tools/dbimport/DbImportActionDefault.java
b/cayenne-tools/src/main/java/org/apache/cayenne/tools/dbimport/DbImportActionDefault.java
index fd1c235..92648d1 100644
--- a/cayenne-tools/src/main/java/org/apache/cayenne/tools/dbimport/DbImportActionDefault.java
+++ b/cayenne-tools/src/main/java/org/apache/cayenne/tools/dbimport/DbImportActionDefault.java
@@ -33,7 +33,6 @@ import org.apache.cayenne.map.ObjRelationship;
 import org.apache.cayenne.merge.AbstractToModelToken;
 import org.apache.cayenne.merge.AddRelationshipToDb;
 import org.apache.cayenne.merge.DbMerger;
-import org.apache.cayenne.merge.ExecutingMergerContext;
 import org.apache.cayenne.merge.MergerContext;
 import org.apache.cayenne.merge.MergerFactory;
 import org.apache.cayenne.merge.MergerToken;
@@ -227,8 +226,8 @@ public class DbImportActionDefault implements DbImportAction {
      * Performs configured schema operations via DbGenerator.
      */
     private DataMap execute(ModelMergeDelegate mergeDelegate, DataMap dataMap, Collection<MergerToken>
tokens) {
-        MergerContext mergerContext = new ExecutingMergerContext(
-                dataMap, null, null, mergeDelegate);
+
+        MergerContext mergerContext = MergerContext.builder(dataMap).delegate(mergeDelegate).build();
 
         for (MergerToken tok : tokens) {
             try {
@@ -243,24 +242,19 @@ public class DbImportActionDefault implements DbImportAction {
         }
 
         ValidationResult failures = mergerContext.getValidationResult();
-        if (failures == null || !failures.hasFailures()) {
-            logger.info("Migration Complete Successfully.");
-        } else {
+        if (failures.hasFailures()) {
             logger.info("Migration Complete.");
-            logger.warn("Migration finished. The following problem(s) were ignored.");
+            logger.warn("Migration finished. The following problem(s) were encountered and
ignored.");
             for (ValidationFailure failure : failures.getFailures()) {
                 logger.warn(failure.toString());
             }
+        } else {
+            logger.info("Migration Complete Successfully.");
         }
 
         return dataMap;
     }
 
-    private DbLoader getLoader(DbImportConfiguration config, DbAdapter adapter, Connection
connection) 
-            throws InstantiationException, IllegalAccessException, ClassNotFoundException
{
-        return config.createLoader(adapter, connection, config.createLoaderDelegate());
-    }
-
     protected void saveLoaded(DataMap dataMap) throws FileNotFoundException {
         ConfigurationTree<DataMap> projectRoot = new ConfigurationTree<>(dataMap);
         Project project = new Project(projectRoot);

http://git-wip-us.apache.org/repos/asf/cayenne/blob/2a205976/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/dialog/db/MergerOptions.java
----------------------------------------------------------------------
diff --git a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/dialog/db/MergerOptions.java
b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/dialog/db/MergerOptions.java
index b18fe9c..ab09801 100644
--- a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/dialog/db/MergerOptions.java
+++ b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/dialog/db/MergerOptions.java
@@ -19,25 +19,10 @@
 
 package org.apache.cayenne.modeler.dialog.db;
 
-import java.awt.Component;
-import java.io.File;
-import java.io.FileWriter;
-import java.io.IOException;
-import java.io.PrintWriter;
-import java.util.Iterator;
-import java.util.List;
-
-import javax.sql.DataSource;
-import javax.swing.JFileChooser;
-import javax.swing.JOptionPane;
-import javax.swing.WindowConstants;
-import javax.swing.event.ChangeEvent;
-import javax.swing.event.ChangeListener;
-
 import org.apache.cayenne.access.loader.DbLoaderConfiguration;
-import org.apache.cayenne.access.loader.filters.TableFilter;
 import org.apache.cayenne.access.loader.filters.FiltersConfig;
 import org.apache.cayenne.access.loader.filters.PatternFilter;
+import org.apache.cayenne.access.loader.filters.TableFilter;
 import org.apache.cayenne.configuration.DataChannelDescriptor;
 import org.apache.cayenne.configuration.DataNodeDescriptor;
 import org.apache.cayenne.dba.JdbcAdapter;
@@ -52,7 +37,6 @@ import org.apache.cayenne.map.event.EntityEvent;
 import org.apache.cayenne.map.event.MapEvent;
 import org.apache.cayenne.merge.AbstractToDbToken;
 import org.apache.cayenne.merge.DbMerger;
-import org.apache.cayenne.merge.ExecutingMergerContext;
 import org.apache.cayenne.merge.MergeDirection;
 import org.apache.cayenne.merge.MergerContext;
 import org.apache.cayenne.merge.MergerToken;
@@ -70,6 +54,18 @@ import org.apache.cayenne.swing.BindingBuilder;
 import org.apache.cayenne.swing.ObjectBinding;
 import org.apache.cayenne.validation.ValidationResult;
 
+import javax.sql.DataSource;
+import javax.swing.*;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+import java.awt.*;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.Iterator;
+import java.util.List;
+
 public class MergerOptions extends CayenneController {
 
     protected MergerOptionsView view;
@@ -416,10 +412,9 @@ public class MergerOptions extends CayenneController {
         try {
             DataSource dataSource = connectionInfo.makeDataSource(getApplication()
                     .getClassLoadingService());
-            // generator.runGenerator(dataSource);
 
-            MergerContext mergerContext = new ExecutingMergerContext(
-                    dataMap, dataSource, adapter, delegate);
+            MergerContext mergerContext = MergerContext.builder(dataMap).syntheticDataNode(dataSource,
adapter)
+                    .delegate(delegate).build();
 
             boolean modelChanged = false;
             for (MergerToken tok : tokensToMigrate) {


Mime
View raw message