Skip to content

Commit d0cdd04

Browse files
authored
Merge pull request #4529 from xdelox/4308-static-schema-subfolders
Segregate the schema definitions under relevant sub folders
2 parents 19263a5 + 4353eae commit d0cdd04

File tree

19 files changed

+722
-143
lines changed

19 files changed

+722
-143
lines changed

core/src/main/java/org/apache/hop/metadata/api/HopMetadata.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,4 +51,12 @@
5151
* @return the type of metadata this property represents.
5252
*/
5353
HopMetadataPropertyType hopMetadataPropertyType() default HopMetadataPropertyType.NONE;
54+
55+
/**
56+
* A boolean flag, represented as a string, which tells whether the given HopMetadata
57+
* implementation has to be shown into a tree of folders
58+
*
59+
* @return a boolean as string flag that will enable the UI to allow folders creation
60+
*/
61+
String subfoldersEnabled() default "false";
5462
}

core/src/main/java/org/apache/hop/metadata/api/HopMetadataBase.java

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@
2222

2323
public class HopMetadataBase implements IHopMetadata {
2424

25+
@HopMetadataProperty protected String path;
26+
2527
/** All metadata objects have a name to uniquely identify it. */
2628
@HopMetadataProperty protected String name;
2729

@@ -36,6 +38,13 @@ public HopMetadataBase() {}
3638
public HopMetadataBase(String name) {
3739
this();
3840
this.name = name;
41+
this.path = "";
42+
}
43+
44+
public HopMetadataBase(String name, String path) {
45+
this();
46+
this.name = name;
47+
this.path = path;
3948
}
4049

4150
@Override
@@ -96,4 +105,27 @@ public String getMetadataProviderName() {
96105
public void setMetadataProviderName(String metadataProviderName) {
97106
this.metadataProviderName = metadataProviderName;
98107
}
108+
109+
@Override
110+
public String getPath() {
111+
return path;
112+
}
113+
114+
@Override
115+
public void setPath(String path) {
116+
this.path = path;
117+
}
118+
119+
@Override
120+
public String getFullName() {
121+
if (path == null || path.isBlank()) {
122+
return name;
123+
} else {
124+
if (path.endsWith("/")) {
125+
return path + name;
126+
} else {
127+
return path + "/" + name;
128+
}
129+
}
130+
}
99131
}

core/src/main/java/org/apache/hop/metadata/api/IHopMetadata.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,4 +52,10 @@ public interface IHopMetadata {
5252
* @param metadataProviderName The source of metadata or null if it's not specified
5353
*/
5454
void setMetadataProviderName(String metadataProviderName);
55+
56+
String getPath();
57+
58+
void setPath(String path);
59+
60+
String getFullName();
5561
}

core/src/main/java/org/apache/hop/metadata/api/IHopMetadataSerializer.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
import java.util.List;
2121
import org.apache.hop.core.exception.HopException;
22+
import org.apache.hop.metadata.serializer.FileSystemNode;
2223

2324
/**
2425
* This metadata interface describes how an object T can be serialized and analyzed.
@@ -67,6 +68,11 @@ public interface IHopMetadataSerializer<T extends IHopMetadata> {
6768
*/
6869
List<String> listObjectNames() throws HopException;
6970

71+
default FileSystemNode getFileSystemTree() throws HopException {
72+
throw new UnsupportedOperationException(
73+
"listObjectNames(String folderName, Boolean recursive) is not supported by this metadata serializer");
74+
}
75+
7076
/**
7177
* See if an object with the given name exists.
7278
*
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
package org.apache.hop.metadata.serializer;
19+
20+
import java.util.ArrayList;
21+
import java.util.List;
22+
23+
// Represents a node in the file system tree
24+
public class FileSystemNode {
25+
private String name; // Name of the file or folder
26+
private String path; // Absolute path to the file or folder
27+
private Type type; // Type (file or folder)
28+
private FileSystemNode parent; // Parent node
29+
private List<FileSystemNode> children; // Children nodes (only for folder)
30+
31+
// Enum for node type
32+
public enum Type {
33+
FILE,
34+
FOLDER
35+
}
36+
37+
// Constructor
38+
39+
public FileSystemNode(String name, String path, Type type, FileSystemNode parent) {
40+
this.name = name;
41+
this.path = path;
42+
this.type = type;
43+
this.parent = parent;
44+
this.children = new ArrayList<>();
45+
46+
if (this.parent != null) {
47+
this.parent.addChild(this);
48+
}
49+
}
50+
51+
// Add a child node
52+
53+
public void addChild(FileSystemNode child) {
54+
if (this.type == Type.FOLDER) {
55+
this.children.add(child);
56+
}
57+
}
58+
59+
// Getters
60+
61+
public String getName() {
62+
return name;
63+
}
64+
65+
public String getPath() {
66+
return path;
67+
}
68+
69+
public Type getType() {
70+
return type;
71+
}
72+
73+
public FileSystemNode getParent() {
74+
return parent;
75+
}
76+
77+
public List<FileSystemNode> getChildren() {
78+
return children;
79+
}
80+
81+
public boolean isFolder() {
82+
return type == Type.FOLDER;
83+
}
84+
85+
public boolean isFile() {
86+
return type == Type.FILE;
87+
}
88+
}

core/src/main/java/org/apache/hop/metadata/serializer/json/JsonMetadataSerializer.java

Lines changed: 113 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import java.io.OutputStream;
2828
import java.nio.charset.StandardCharsets;
2929
import java.util.ArrayList;
30+
import java.util.Arrays;
3031
import java.util.Collections;
3132
import java.util.List;
3233
import org.apache.commons.lang.StringUtils;
@@ -39,6 +40,7 @@
3940
import org.apache.hop.metadata.api.IHopMetadata;
4041
import org.apache.hop.metadata.api.IHopMetadataProvider;
4142
import org.apache.hop.metadata.api.IHopMetadataSerializer;
43+
import org.apache.hop.metadata.serializer.FileSystemNode;
4244
import org.json.simple.JSONObject;
4345

4446
/**
@@ -140,11 +142,11 @@ private void inheritVariables(T t) {
140142

141143
@Override
142144
public void save(T t) throws HopException {
143-
if (StringUtils.isEmpty(t.getName())) {
145+
if (StringUtils.isEmpty(t.getFullName())) {
144146
throw new HopException("Error: To save a metadata object it needs to have a name");
145147
}
146148

147-
String filename = calculateFilename(t.getName());
149+
String filename = calculateFilename(t);
148150
try {
149151

150152
JSONObject jObject = parser.getJsonObject(t);
@@ -170,29 +172,63 @@ public void save(T t) throws HopException {
170172
}
171173
}
172174

175+
public String calculateFilename(T t) {
176+
String prefix = t.getPath() == null ? baseFolder : t.getPath();
177+
if (!prefix.endsWith("/")) {
178+
prefix += "/";
179+
}
180+
return prefix + t.getName() + ".json";
181+
}
182+
173183
public String calculateFilename(String name) {
184+
if (name.startsWith(baseFolder)) {
185+
return name;
186+
}
174187
return baseFolder + "/" + name + ".json";
175188
}
176189

190+
public String calculateFolderName(String name) {
191+
if (name.startsWith(baseFolder)) {
192+
return name;
193+
}
194+
return baseFolder + "/" + name;
195+
}
196+
177197
@Override
178198
public T delete(String name) throws HopException {
179199
if (name == null) {
180200
throw new HopException(
181201
"Error: you need to specify the name of the metadata object to delete");
182202
}
183-
if (!exists(name)) {
203+
boolean exists = exists(name);
204+
if (!exists) {
184205
throw new HopException("Error: Object '" + name + "' doesn't exist");
185206
}
186-
T t = load(name);
187-
String filename = calculateFilename(name);
207+
208+
T t = null;
209+
String objectName = null;
210+
if (existsFolder(name)) {
211+
objectName = calculateFolderName(name);
212+
}
213+
if (existsFile(name)) {
214+
t = load(name);
215+
objectName = calculateFilename(name);
216+
}
217+
188218
try {
189-
boolean deleted = HopVfs.getFileObject(filename).delete();
219+
var object = HopVfs.getFileObject(objectName);
220+
boolean deleted = false;
221+
if (object.isFolder()) {
222+
deleted = object.deleteAll() > 0;
223+
} else {
224+
deleted = object.delete();
225+
}
190226
if (!deleted) {
191227
throw new HopException(
192-
"Error: Object '" + name + "' could not be deleted, filename : " + filename);
228+
"Error: Object '" + name + "' could not be deleted, name : " + objectName);
193229
}
194230
} catch (FileSystemException e) {
195-
throw new HopException("Error deleting Object '" + name + "' with filename : " + filename);
231+
throw new HopException("Error deleting Object '" + name + "' with name : " + objectName);
196232
}
197233
return t;
198234
}
@@ -214,9 +250,77 @@ public List<String> listObjectNames() throws HopException {
214250
}
215251
}
216252

253+
@Override
254+
public FileSystemNode getFileSystemTree() throws HopException {
255+
FileObject currentItem = HopVfs.getFileObject(baseFolder);
256+
FileSystemNode root =
257+
new FileSystemNode(
258+
currentItem.getName().getBaseName(),
259+
currentItem.getName().getPath(),
260+
FileSystemNode.Type.FOLDER,
261+
null);
262+
return getFileSystemTree(root);
263+
}
264+
265+
private FileSystemNode getFileSystemTree(FileSystemNode parent) throws HopException {
266+
FileObject currentItem = HopVfs.getFileObject(parent.getPath());
267+
try {
268+
Arrays.asList(currentItem.getChildren()).stream()
269+
.forEach(
270+
f -> {
271+
try {
272+
if (f.isFolder()) {
273+
FileSystemNode currentFolder =
274+
new FileSystemNode(
275+
f.getName().getBaseName(),
276+
f.getName().getPath(),
277+
FileSystemNode.Type.FOLDER,
278+
parent);
279+
getFileSystemTree(currentFolder); // Recursive call for the folder
280+
} else {
281+
if (f.getName().getExtension().equals("json")) {
282+
FileSystemNode currentFile =
283+
new FileSystemNode(
284+
f.getName().getBaseName().replaceAll("\\.json$", ""),
285+
f.getName().getPath(),
286+
FileSystemNode.Type.FILE,
287+
parent);
288+
}
289+
}
290+
} catch (FileSystemException | HopException e) {
291+
throw new RuntimeException(e);
292+
}
293+
});
294+
} catch (Exception e) {
295+
throw new HopException("Error searching for JSON files", e);
296+
}
297+
return parent;
298+
}
299+
300+
// try {
301+
// List<FileObject> jsonFiles = HopVfs.findFiles(folder, "json", false);
302+
// List<String> names = new ArrayList<>();
303+
// for (FileObject jsonFile : jsonFiles) {
304+
// String baseName = jsonFile.getName().getBaseName();
305+
// names.add(baseName.replaceAll("\\.json$", ""));
306+
// }
307+
// return names;
308+
// } catch (Exception e) {
309+
// throw new HopException("Error searching for JSON files", e);
310+
// }
217311
@Override
218312
public boolean exists(String name) throws HopException {
219-
return HopVfs.fileExists(calculateFilename(name));
313+
return existsFolder(name) || existsFile(name);
314+
}
315+
316+
private boolean existsFile(String name) throws HopException {
317+
318+
return !existsFolder(name)
319+
&& (HopVfs.fileExists(name) || HopVfs.fileExists(calculateFilename(name)));
320+
}
321+
322+
private boolean existsFolder(String name) throws HopException {
323+
return HopVfs.fileExists(name) || HopVfs.fileExists(calculateFolderName(name));
220324
}
221325

222326
/**

core/src/main/java/org/apache/hop/metadata/serializer/multi/MultiMetadataSerializer.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
import org.apache.hop.metadata.api.IHopMetadata;
3030
import org.apache.hop.metadata.api.IHopMetadataProvider;
3131
import org.apache.hop.metadata.api.IHopMetadataSerializer;
32+
import org.apache.hop.metadata.serializer.FileSystemNode;
3233

3334
public class MultiMetadataSerializer<T extends IHopMetadata> implements IHopMetadataSerializer<T> {
3435

@@ -141,6 +142,15 @@ public List<String> listObjectNames() throws HopException {
141142
return new ArrayList<>(set);
142143
}
143144

145+
@Override
146+
public FileSystemNode getFileSystemTree() throws HopException {
147+
FileSystemNode root = new FileSystemNode("root", "/", FileSystemNode.Type.FOLDER, null);
148+
for (IHopMetadataProvider provider : multiProvider.getProviders()) {
149+
root.addChild(provider.getSerializer(managedClass).getFileSystemTree());
150+
}
151+
return root;
152+
}
153+
144154
@Override
145155
public boolean exists(String name) throws HopException {
146156
for (IHopMetadataProvider provider : multiProvider.getProviders()) {

0 commit comments

Comments
 (0)