You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cayenne.apache.org by nt...@apache.org on 2022/11/10 11:51:33 UTC
[cayenne] 01/03: Merge remote-tracking branch 'parent/pr/463' into asf-master
This is an automated email from the ASF dual-hosted git repository.
ntimofeev pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/cayenne.git
commit 63f738d220c9c81649002eaeeef1869dc7d82277
Merge: 4198c1bcd 5f9ab4f8b
Author: Nikita Timofeev <st...@gmail.com>
AuthorDate: Thu Nov 10 11:15:59 2022 +0300
Merge remote-tracking branch 'parent/pr/463' into asf-master
# Conflicts:
# cayenne-project/src/main/java/org/apache/cayenne/project/FileProjectSaver.java
.../cayenne/project/ConfigurationSourceSetter.java | 23 +-
.../apache/cayenne/project/FileProjectSaver.java | 637 +++++++++++----------
.../org/apache/cayenne/project/ProjectModule.java | 3 +
.../xml/XMLDataChannelDescriptorLoader.java | 1 -
.../apache/cayenne/modeler/ProjectController.java | 65 +--
.../apache/cayenne/modeler/action/SaveAction.java | 2 +
.../apache/cayenne/modeler/editor/DataMapView.java | 30 +-
.../cayenne/modeler/event/ProjectSavedEvent.java | 37 ++
.../modeler/event/ProjectSavedListener.java | 29 +
9 files changed, 443 insertions(+), 384 deletions(-)
diff --cc cayenne-project/src/main/java/org/apache/cayenne/project/FileProjectSaver.java
index a83cf7693,11325634c..cd76e0a64
--- a/cayenne-project/src/main/java/org/apache/cayenne/project/FileProjectSaver.java
+++ b/cayenne-project/src/main/java/org/apache/cayenne/project/FileProjectSaver.java
@@@ -50,321 -50,322 +50,322 @@@ import java.util.List
*/
public class FileProjectSaver implements ProjectSaver {
- @Inject
- protected ConfigurationNameMapper nameMapper;
-
- protected ConfigurationNodeVisitor<Resource> resourceGetter;
- protected ConfigurationNodeVisitor<Collection<ConfigurationNode>> saveableNodesGetter;
- protected String fileEncoding;
-
- protected Collection<ProjectExtension> extensions;
- protected SaverDelegate delegate;
-
- public FileProjectSaver(@Inject List<ProjectExtension> extensions) {
- resourceGetter = new ConfigurationSourceGetter();
- saveableNodesGetter = new SaveableNodesGetter();
-
- // this is not configurable yet... probably doesn't have to be
- fileEncoding = "UTF-8";
-
- this.extensions = extensions;
- Collection<SaverDelegate> delegates = new ArrayList<>(extensions.size());
- for(ProjectExtension extension : extensions) {
- delegates.add(extension.createSaverDelegate());
- }
- delegate = new CompoundSaverDelegate(delegates);
- }
-
- @Override
- public String getSupportedVersion() {
- return String.valueOf(Project.VERSION);
- }
-
- @Override
- public void save(Project project) {
- save(project, project.getConfigurationResource(), true);
- }
-
- @Override
- public void saveAs(Project project, Resource baseDirectory) {
- if (baseDirectory == null) {
- throw new NullPointerException("Null 'baseDirectory'");
- }
- save(project, baseDirectory, false);
- }
-
- void save(Project project, Resource baseResource, boolean deleteOldResources) {
- Collection<ConfigurationNode> nodes = project.getRootNode().acceptVisitor(saveableNodesGetter);
- Collection<SaveUnit> units = new ArrayList<>(nodes.size());
-
- delegate.setBaseDirectory(baseResource);
-
- for(ConfigurationNode node : nodes) {
- String targetLocation = nameMapper.configurationLocation(node);
- Resource targetResource = baseResource.getRelativeResource(targetLocation);
- units.add(createSaveUnit(node, targetResource, null));
-
- for(ProjectExtension extension : extensions) {
- ConfigurationNodeVisitor<String> namingDelegate = extension.createNamingDelegate();
- SaverDelegate unitSaverDelegate = extension.createSaverDelegate();
- String fileName = node.acceptVisitor(namingDelegate);
- if(fileName != null) {
- // not null means that this should go to a separate file
- targetResource = baseResource.getRelativeResource(fileName);
- units.add(createSaveUnit(node, targetResource, unitSaverDelegate));
- }
- }
- }
-
- checkAccess(units);
-
- try {
- saveToTempFiles(units);
- saveCommit(units);
- } finally {
- clearTempFiles(units);
- }
-
- try {
- if (deleteOldResources) {
- clearRenamedFiles(units);
-
- Collection<URL> unusedResources = project.getUnusedResources();
- for (SaveUnit unit : units) {
- unusedResources.remove(unit.sourceConfiguration.getURL());
- }
- deleteUnusedFiles(unusedResources);
- }
- } catch (IOException ex) {
- throw new CayenneRuntimeException(ex);
- }
-
- // I guess we should reset projects state regardless of the value of
- // 'deleteOldResources'
- project.getUnusedResources().clear();
- }
-
- SaveUnit createSaveUnit(ConfigurationNode node, Resource targetResource, SaverDelegate delegate) {
-
- SaveUnit unit = new SaveUnit();
- unit.node = node;
- unit.delegate = delegate;
- unit.sourceConfiguration = node.acceptVisitor(resourceGetter);
-
- if (unit.sourceConfiguration == null) {
- unit.sourceConfiguration = targetResource;
- }
-
- // attempt to convert targetResource to a File... if that fails,
- // FileProjectSaver is not appropriate for handling a given project..
-
- URL targetUrl = targetResource.getURL();
-
- try {
- unit.targetFile = Util.toFile(targetUrl);
- } catch (IllegalArgumentException e) {
- throw new CayenneRuntimeException("Can't save configuration to the following location: '%s'. "
- + "Is this a valid file location?. (%s)", e, targetUrl, e.getMessage());
- }
-
- return unit;
- }
-
- void checkAccess(Collection<SaveUnit> units) {
- for (SaveUnit unit : units) {
-
- File targetFile = unit.targetFile;
-
- File parent = targetFile.getParentFile();
- if (!parent.exists()) {
- if (!parent.mkdirs()) {
- throw new CayenneRuntimeException("Error creating directory tree for '%s'",
- parent.getAbsolutePath());
- }
- }
-
- if (targetFile.isDirectory()) {
- throw new CayenneRuntimeException("Target file '%s' is a directory", targetFile.getAbsolutePath());
- }
-
- if (targetFile.exists() && !targetFile.canWrite()) {
- throw new CayenneRuntimeException("Can't write to file '%s'", targetFile.getAbsolutePath());
- }
-
- }
- }
-
- void saveToTempFiles(Collection<SaveUnit> units) {
-
- for (SaveUnit unit : units) {
-
- String name = unit.targetFile.getName();
- if (name.length() < 3) {
- name = "cayenne-project";
- }
-
- File parent = unit.targetFile.getParentFile();
-
- try {
- unit.targetTempFile = File.createTempFile(name, null, parent);
- } catch (IOException e) {
- throw new CayenneRuntimeException("Error creating temp file (%s)", e, e.getMessage());
- }
-
- if (unit.targetTempFile.exists()) {
- unit.targetTempFile.delete();
- }
-
- try (PrintWriter printWriter = new PrintWriter(new OutputStreamWriter(
- new FileOutputStream(unit.targetTempFile), fileEncoding))) {
- saveToTempFile(unit, printWriter);
- } catch (UnsupportedEncodingException e) {
- throw new CayenneRuntimeException("Unsupported encoding '%s' (%s)", e, fileEncoding, e.getMessage());
- } catch (FileNotFoundException e) {
- throw new CayenneRuntimeException("File not found '%s' (%s)", e, unit.targetTempFile.getAbsolutePath(),
- e.getMessage());
- }
- }
- }
-
- void saveToTempFile(SaveUnit unit, PrintWriter printWriter) {
- ConfigurationNodeVisitor<?> visitor;
- if(unit.delegate == null) {
- visitor = new ConfigurationSaver(printWriter, getSupportedVersion(), delegate);
- } else {
- XMLEncoder encoder = new XMLEncoder(printWriter, "\t", getSupportedVersion());
- encoder.println("<?xml version=\"1.0\" encoding=\"utf-8\"?>");
- unit.delegate.setXMLEncoder(encoder);
- visitor = unit.delegate;
- }
-
- unit.node.acceptVisitor(visitor);
- }
-
- void saveCommit(Collection<SaveUnit> units) {
-
- for (SaveUnit unit : units) {
-
- File targetFile = unit.targetFile;
-
- // Per CAY-2119, this is an ugly hack to force Windows to unlock the file that was previously locked by
- // our process. Without it, the delete operation downstream would fail
- System.gc();
-
- if (targetFile.exists()) {
- if (!targetFile.delete()) {
- throw new CayenneRuntimeException("Unable to remove old master file '%s'",
- targetFile.getAbsolutePath());
- }
- }
-
- File tempFile = unit.targetTempFile;
- if (!tempFile.renameTo(targetFile)) {
- throw new CayenneRuntimeException("Unable to move '%s' to '%s'", tempFile.getAbsolutePath(),
- targetFile.getAbsolutePath());
- }
-
- unit.targetTempFile = null;
- try {
- if(unit.delegate == null) {
- unit.node.acceptVisitor(new ConfigurationSourceSetter(new URLResource(targetFile.toURI().toURL())));
- }
- } catch (MalformedURLException e) {
- throw new CayenneRuntimeException("Malformed URL for file '%s'", e, targetFile.getAbsolutePath());
- }
- }
- }
-
- private void clearTempFiles(Collection<SaveUnit> units) {
- for (SaveUnit unit : units) {
-
- if (unit.targetTempFile != null && unit.targetTempFile.exists()) {
- unit.targetTempFile.delete();
- unit.targetTempFile = null;
- }
- }
- }
-
- private void clearRenamedFiles(Collection<SaveUnit> units) throws IOException {
- for (SaveUnit unit : units) {
-
- if (unit.sourceConfiguration == null) {
- continue;
- }
-
- URL sourceUrl = unit.sourceConfiguration.getURL();
- File sourceFile;
- try {
- sourceFile = Util.toFile(sourceUrl);
- } catch (IllegalArgumentException e) {
- // ignore non-file configurations...
- continue;
- }
-
- if (!sourceFile.exists()) {
- continue;
- }
-
- // compare against ALL unit target files, not just the current
- // unit... if the
- // target matches, skip this file
- boolean isTarget = false;
- for (SaveUnit xunit : units) {
- if (isFilesEquals(sourceFile, xunit.targetFile)) {
- isTarget = true;
- break;
- }
- }
-
- if (!isTarget) {
- if (!sourceFile.delete()) {
- throw new CayenneRuntimeException("Could not delete file '%s'", sourceFile.getCanonicalPath());
- }
- }
- }
- }
-
- private boolean isFilesEquals(File firstFile, File secondFile) throws IOException {
- boolean isFirstFileExists = firstFile.exists();
- boolean isSecondFileExists = secondFile.exists();
-
- String firstFilePath = firstFile.getCanonicalPath();
- String secondFilePath = secondFile.getCanonicalPath();
-
- return isFirstFileExists && isSecondFileExists && firstFilePath.equals(secondFilePath);
- }
-
- private void deleteUnusedFiles(Collection<URL> unusedResources) throws IOException {
- for (URL unusedResource : unusedResources) {
-
- File unusedFile;
- try {
- unusedFile = Util.toFile(unusedResource);
- } catch (IllegalArgumentException e) {
- // ignore non-file configurations...
- continue;
- }
-
- if (!unusedFile.exists()) {
- continue;
- }
-
- if (!unusedFile.delete()) {
- throw new CayenneRuntimeException("Could not delete file '%s'", unusedFile.getCanonicalPath());
- }
-
- }
- }
-
- static class SaveUnit {
-
- private ConfigurationNode node;
- private SaverDelegate delegate;
-
- // source can be an abstract resource, but target is always a file...
- private Resource sourceConfiguration;
- private File targetFile;
- private File targetTempFile;
-
- }
+ @Inject
+ protected ConfigurationNameMapper nameMapper;
+
+ protected ConfigurationNodeVisitor<Resource> resourceGetter;
+ protected ConfigurationNodeVisitor<Collection<ConfigurationNode>> saveableNodesGetter;
+ protected String fileEncoding;
+
+ protected Collection<ProjectExtension> extensions;
+ protected SaverDelegate delegate;
+
+ public FileProjectSaver(@Inject List<ProjectExtension> extensions) {
+ resourceGetter = new ConfigurationSourceGetter();
+ saveableNodesGetter = new SaveableNodesGetter();
+
+ // this is not configurable yet... probably doesn't have to be
+ fileEncoding = "UTF-8";
+
+ this.extensions = extensions;
+ Collection<SaverDelegate> delegates = new ArrayList<>(extensions.size());
+ for (ProjectExtension extension : extensions) {
+ delegates.add(extension.createSaverDelegate());
+ }
+ delegate = new CompoundSaverDelegate(delegates);
+ }
+
+ @Override
+ public String getSupportedVersion() {
+ return String.valueOf(Project.VERSION);
+ }
+
+ @Override
+ public void save(Project project) {
+ save(project, project.getConfigurationResource(), true);
+ }
+
+ @Override
+ public void saveAs(Project project, Resource baseDirectory) {
+ if (baseDirectory == null) {
+ throw new NullPointerException("Null 'baseDirectory'");
+ }
+ save(project, baseDirectory, false);
+ }
+
+ void save(Project project, Resource baseResource, boolean deleteOldResources) {
+ Collection<ConfigurationNode> nodes = project.getRootNode().acceptVisitor(saveableNodesGetter);
+ Collection<SaveUnit> units = new ArrayList<>(nodes.size());
+
+ delegate.setBaseDirectory(baseResource);
+
+ for (ConfigurationNode node : nodes) {
+ String targetLocation = nameMapper.configurationLocation(node);
+ Resource targetResource = baseResource.getRelativeResource(targetLocation);
+ units.add(createSaveUnit(node, targetResource, null));
+
+ for (ProjectExtension extension : extensions) {
+ ConfigurationNodeVisitor<String> namingDelegate = extension.createNamingDelegate();
+ SaverDelegate unitSaverDelegate = extension.createSaverDelegate();
+ String fileName = node.acceptVisitor(namingDelegate);
+ if (fileName != null) {
+ // not null means that this should go to a separate file
+ targetResource = baseResource.getRelativeResource(fileName);
+ units.add(createSaveUnit(node, targetResource, unitSaverDelegate));
+ }
+ }
+ }
+
+ checkAccess(units);
+
+ try {
+ saveToTempFiles(units);
+ saveCommit(units);
+ } finally {
+ clearTempFiles(units);
+ }
+
+ try {
+ if (deleteOldResources) {
+ clearRenamedFiles(units);
+
+ Collection<URL> unusedResources = project.getUnusedResources();
+ for (SaveUnit unit : units) {
+ unusedResources.remove(unit.sourceConfiguration.getURL());
+ }
+ deleteUnusedFiles(unusedResources);
+ }
+ } catch (IOException ex) {
+ throw new CayenneRuntimeException(ex);
+ }
+
+ // I guess we should reset projects state regardless of the value of
+ // 'deleteOldResources'
+ project.getUnusedResources().clear();
+ }
+
+ SaveUnit createSaveUnit(ConfigurationNode node, Resource targetResource, SaverDelegate delegate) {
+
+ SaveUnit unit = new SaveUnit();
+ unit.node = node;
+ unit.delegate = delegate;
+ unit.sourceConfiguration = node.acceptVisitor(resourceGetter);
+
+ if (unit.sourceConfiguration == null) {
+ unit.sourceConfiguration = targetResource;
+ }
+
+ // attempt to convert targetResource to a File... if that fails,
+ // FileProjectSaver is not appropriate for handling a given project..
+
+ URL targetUrl = targetResource.getURL();
+
+ try {
+ unit.targetFile = Util.toFile(targetUrl);
+ } catch (IllegalArgumentException e) {
+ throw new CayenneRuntimeException("Can't save configuration to the following location: '%s'. "
+ + "Is this a valid file location?. (%s)", e, targetUrl, e.getMessage());
+ }
+
+ return unit;
+ }
+
+ void checkAccess(Collection<SaveUnit> units) {
+ for (SaveUnit unit : units) {
+
+ File targetFile = unit.targetFile;
+
+ File parent = targetFile.getParentFile();
+ if (!parent.exists()) {
+ if (!parent.mkdirs()) {
+ throw new CayenneRuntimeException("Error creating directory tree for '%s'",
+ parent.getAbsolutePath());
+ }
+ }
+
+ if (targetFile.isDirectory()) {
+ throw new CayenneRuntimeException("Target file '%s' is a directory", targetFile.getAbsolutePath());
+ }
+
+ if (targetFile.exists() && !targetFile.canWrite()) {
+ throw new CayenneRuntimeException("Can't write to file '%s'", targetFile.getAbsolutePath());
+ }
+
+ }
+ }
+
+ void saveToTempFiles(Collection<SaveUnit> units) {
+
+ for (SaveUnit unit : units) {
+
+ String name = unit.targetFile.getName();
+ if (name.length() < 3) {
+ name = "cayenne-project";
+ }
+
+ File parent = unit.targetFile.getParentFile();
+
+ try {
+ unit.targetTempFile = File.createTempFile(name, null, parent);
+ } catch (IOException e) {
+ throw new CayenneRuntimeException("Error creating temp file (%s)", e, e.getMessage());
+ }
+
+ if (unit.targetTempFile.exists()) {
+ unit.targetTempFile.delete();
+ }
+
+ try (PrintWriter printWriter = new PrintWriter(new OutputStreamWriter(
+ new FileOutputStream(unit.targetTempFile), fileEncoding))) {
+ saveToTempFile(unit, printWriter);
+ } catch (UnsupportedEncodingException e) {
+ throw new CayenneRuntimeException("Unsupported encoding '%s' (%s)", e, fileEncoding, e.getMessage());
+ } catch (FileNotFoundException e) {
+ throw new CayenneRuntimeException("File not found '%s' (%s)", e, unit.targetTempFile.getAbsolutePath(),
+ e.getMessage());
+ }
+ }
+ }
+
+ void saveToTempFile(SaveUnit unit, PrintWriter printWriter) {
+ ConfigurationNodeVisitor<?> visitor;
+ if (unit.delegate == null) {
+ visitor = new ConfigurationSaver(printWriter, getSupportedVersion(), delegate);
+ } else {
+ XMLEncoder encoder = new XMLEncoder(printWriter, "\t", getSupportedVersion());
+ encoder.println("<?xml version=\"1.0\" encoding=\"utf-8\"?>");
+ unit.delegate.setXMLEncoder(encoder);
+ visitor = unit.delegate;
+ }
+
+ unit.node.acceptVisitor(visitor);
+ }
+
+ void saveCommit(Collection<SaveUnit> units) {
+
+ for (SaveUnit unit : units) {
+
+ File targetFile = unit.targetFile;
+
+ // Per CAY-2119, this is an ugly hack to force Windows to unlock the file that was previously locked by
+ // our process. Without it, the delete operation downstream would fail
+ System.gc();
+
+ if (targetFile.exists()) {
+ if (!targetFile.delete()) {
+ throw new CayenneRuntimeException("Unable to remove old master file '%s'",
+ targetFile.getAbsolutePath());
+ }
+ }
+
+ File tempFile = unit.targetTempFile;
+ if (!tempFile.renameTo(targetFile)) {
+ throw new CayenneRuntimeException("Unable to move '%s' to '%s'", tempFile.getAbsolutePath(),
+ targetFile.getAbsolutePath());
+ }
+
+ unit.targetTempFile = null;
+ try {
+ if (unit.delegate == null) {
+ URLResource targetUrlResource = new URLResource(targetFile.toURI().toURL());
+ unit.node.acceptVisitor(new ConfigurationSourceSetter(targetUrlResource, nameMapper));
+ }
+ } catch (MalformedURLException e) {
+ throw new CayenneRuntimeException("Malformed URL for file '%s'", e, targetFile.getAbsolutePath());
+ }
+ }
+ }
+
+ private void clearTempFiles(Collection<SaveUnit> units) {
+ for (SaveUnit unit : units) {
+
+ if (unit.targetTempFile != null && unit.targetTempFile.exists()) {
+ unit.targetTempFile.delete();
+ unit.targetTempFile = null;
+ }
+ }
+ }
+
+ private void clearRenamedFiles(Collection<SaveUnit> units) throws IOException {
+ for (SaveUnit unit : units) {
+
+ if (unit.sourceConfiguration == null) {
+ continue;
+ }
+
+ URL sourceUrl = unit.sourceConfiguration.getURL();
+ File sourceFile;
+ try {
+ sourceFile = Util.toFile(sourceUrl);
+ } catch (IllegalArgumentException e) {
+ // ignore non-file configurations...
+ continue;
+ }
+
+ if (!sourceFile.exists()) {
+ continue;
+ }
+
+ // compare against ALL unit target files, not just the current
+ // unit... if the
+ // target matches, skip this file
+ boolean isTarget = false;
+ for (SaveUnit xunit : units) {
+ if (isFilesEquals(sourceFile, xunit.targetFile)) {
+ isTarget = true;
+ break;
+ }
+ }
+
+ if (!isTarget) {
+ if (!sourceFile.delete()) {
+ throw new CayenneRuntimeException("Could not delete file '%s'", sourceFile.getCanonicalPath());
+ }
+ }
+ }
+ }
+
+ private boolean isFilesEquals(File firstFile, File secondFile) throws IOException {
+ boolean isFirstFileExists = firstFile.exists();
+ boolean isSecondFileExists = secondFile.exists();
+
+ String firstFilePath = firstFile.getCanonicalPath();
+ String secondFilePath = secondFile.getCanonicalPath();
+
+ return isFirstFileExists && isSecondFileExists && firstFilePath.equals(secondFilePath);
+ }
+
+ private void deleteUnusedFiles(Collection<URL> unusedResources) throws IOException {
+ for (URL unusedResource : unusedResources) {
+
+ File unusedFile;
+ try {
+ unusedFile = Util.toFile(unusedResource);
+ } catch (IllegalArgumentException e) {
+ // ignore non-file configurations...
+ continue;
+ }
+
+ if (!unusedFile.exists()) {
+ continue;
+ }
+
+ if (!unusedFile.delete()) {
+ throw new CayenneRuntimeException("Could not delete file '%s'", unusedFile.getCanonicalPath());
+ }
+
+ }
+ }
+
- class SaveUnit {
++ static class SaveUnit {
+
+ private ConfigurationNode node;
+ private SaverDelegate delegate;
+
+ // source can be an abstract resource, but target is always a file...
+ private Resource sourceConfiguration;
+ private File targetFile;
+ private File targetTempFile;
+
+ }
}