Skip to content

Commit f41017b

Browse files
Introduced a ResourceBuilder to offer a fluid interface to creating resources.
1 parent ed7da4f commit f41017b

File tree

12 files changed

+777
-55
lines changed

12 files changed

+777
-55
lines changed

pom.xml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
<modelVersion>4.0.0</modelVersion>
55
<groupId>io.frictionlessdata</groupId>
66
<artifactId>datapackage-java</artifactId>
7-
<version>0.9.2-SNAPSHOT</version>
7+
<version>0.9.5-SNAPSHOT</version>
88
<packaging>jar</packaging>
99
<issueManagement>
1010
<url>https://github.com/frictionlessdata/datapackage-java/issues</url>
@@ -23,7 +23,7 @@
2323
<maven.compiler.source>${java.version}</maven.compiler.source>
2424
<maven.compiler.target>${java.version}</maven.compiler.target>
2525
<maven.compiler.compiler>${java.version}</maven.compiler.compiler>
26-
<tableschema-java-version>0.9.2</tableschema-java-version>
26+
<tableschema-java-version>0.9.5</tableschema-java-version>
2727
<junit.version>5.12.0</junit.version>
2828
<slf4j-simple.version>2.0.17</slf4j-simple.version>
2929
<apache-commons-collections4.version>4.4</apache-commons-collections4.version>

src/main/java/io/frictionlessdata/datapackage/Package.java

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717
import io.frictionlessdata.datapackage.resource.Resource;
1818
import io.frictionlessdata.tableschema.exception.JsonParsingException;
1919
import io.frictionlessdata.tableschema.exception.ValidationException;
20-
import io.frictionlessdata.tableschema.io.LocalFileReference;
2120
import io.frictionlessdata.tableschema.util.JsonUtil;
2221
import org.apache.commons.collections.list.UnmodifiableList;
2322
import org.apache.commons.collections.set.UnmodifiableSet;
@@ -607,16 +606,6 @@ public void write (File outputDir, Consumer<Path> callback, boolean zipCompresse
607606
r.writeData(outFs.getPath(parentDirName ));
608607
r.writeSchema(outFs.getPath(parentDirName));
609608
r.writeDialect(outFs.getPath(parentDirName));
610-
611-
// write out dialect file only if not null or URL
612-
613-
/*Dialect dia = r.getDialect();
614-
if (null != dia) {
615-
String dialectP = r.getPathForWritingDialect();
616-
Path dialectPath = outFs.getPath(parentDirName + File.separator + dialectP);
617-
dia.writeDialect(dialectPath);
618-
dia.setReference(new LocalFileReference(new File(dialectP)));
619-
}*/
620609
}
621610
writeDescriptor(outFs, parentDirName);
622611

@@ -785,7 +774,7 @@ private void setJson(ObjectNode jsonNodeSource) throws Exception {
785774
ObjectNode resourceJson = (ObjectNode) resourcesJsonArray.get(i);
786775
Resource resource = null;
787776
try {
788-
resource = Resource.build(resourceJson, basePath, isArchivePackage);
777+
resource = Resource.fromJSON(resourceJson, basePath, isArchivePackage);
789778
} catch (DataPackageException dpe) {
790779
if(this.strictValidation){
791780
this.jsonObject = null;

src/main/java/io/frictionlessdata/datapackage/resource/AbstractDataResource.java

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,12 @@
33
import com.fasterxml.jackson.annotation.JsonIgnore;
44
import com.fasterxml.jackson.annotation.JsonInclude;
55
import com.fasterxml.jackson.annotation.JsonProperty;
6-
import io.frictionlessdata.datapackage.Dialect;
6+
import io.frictionlessdata.datapackage.Package;
77
import io.frictionlessdata.datapackage.exceptions.DataPackageException;
8+
import io.frictionlessdata.datapackage.exceptions.DataPackageValidationException;
89
import io.frictionlessdata.tableschema.Table;
910

1011
import java.io.IOException;
11-
import java.nio.file.Path;
1212
import java.util.ArrayList;
1313
import java.util.HashSet;
1414
import java.util.List;
@@ -77,6 +77,28 @@ public Set<String> getDatafileNamesForWriting() {
7777
return names;
7878
}
7979

80+
@Override
81+
public void validate(Package pkg) throws DataPackageValidationException {
82+
super.validate(pkg);
83+
try {
84+
if (getRawData() == null) {
85+
throw new DataPackageValidationException("Data resource must have data");
86+
}
87+
88+
if (getFormat() == null) {
89+
throw new DataPackageValidationException("Data resource must specify a format");
90+
}
91+
} catch (Exception ex) {
92+
if (ex instanceof DataPackageValidationException) {
93+
errors.add((DataPackageValidationException) ex);
94+
}
95+
else {
96+
errors.add(new DataPackageValidationException(ex));
97+
}
98+
}
99+
}
100+
101+
80102
@JsonIgnore
81103
abstract String getResourceFormat();
82104
}

src/main/java/io/frictionlessdata/datapackage/resource/AbstractReferencebasedResource.java

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,18 @@
44
import com.fasterxml.jackson.annotation.JsonInclude;
55
import com.fasterxml.jackson.annotation.JsonProperty;
66
import com.fasterxml.jackson.databind.JsonNode;
7+
import io.frictionlessdata.datapackage.Package;
8+
import io.frictionlessdata.datapackage.exceptions.DataPackageException;
9+
import io.frictionlessdata.datapackage.exceptions.DataPackageValidationException;
710
import io.frictionlessdata.tableschema.Table;
811
import io.frictionlessdata.tableschema.tabledatasource.TableDataSource;
912
import io.frictionlessdata.tableschema.util.JsonUtil;
1013

1114
import java.io.ByteArrayOutputStream;
15+
import java.io.File;
1216
import java.io.IOException;
1317
import java.io.InputStream;
18+
import java.net.URL;
1419
import java.util.*;
1520
import java.util.stream.Collectors;
1621

@@ -90,6 +95,68 @@ public Set<String> getDatafileNamesForWriting() {
9095
}).collect(Collectors.toSet());
9196
}
9297

98+
@Override
99+
public void validate(Package pkg) throws DataPackageValidationException {
100+
super.validate(pkg);
101+
List<T> paths = new ArrayList<>(getPaths());
102+
try {
103+
if (paths.isEmpty()) {
104+
throw new DataPackageValidationException("File- or URL-based resource must have at least one path");
105+
}
106+
107+
for (T path : paths) {
108+
String name = null;
109+
if (path instanceof File) {
110+
name = ((File) path).getName();
111+
} else if (path instanceof URL) {
112+
name = ((URL) path).getPath();
113+
}
114+
if (name == null || name.trim().isEmpty()) {
115+
throw new DataPackageValidationException("Resource path cannot be null or empty");
116+
}
117+
}
118+
} catch (Exception ex) {
119+
if (ex instanceof DataPackageValidationException) {
120+
errors.add((DataPackageValidationException) ex);
121+
}
122+
else {
123+
errors.add(new DataPackageValidationException(ex));
124+
}
125+
}
126+
}
127+
128+
129+
static String sniffFormat(Collection<?> paths) {
130+
Set<String> foundFormats = new HashSet<>();
131+
for (Object p : paths) {
132+
String name;
133+
if (p instanceof String) {
134+
name = (String) p;
135+
} else if (p instanceof File) {
136+
name = ((File) p).getName();
137+
} else if (p instanceof URL) {
138+
name = ((URL) p).getPath();
139+
} else {
140+
throw new DataPackageException("Unsupported path type: " + p.getClass().getName());
141+
}
142+
if (name.toLowerCase().endsWith(TableDataSource.Format.FORMAT_CSV.getLabel())) {
143+
foundFormats.add(TableDataSource.Format.FORMAT_CSV.getLabel());
144+
} else if (name.toLowerCase().endsWith(TableDataSource.Format.FORMAT_JSON.getLabel())) {
145+
foundFormats.add(TableDataSource.Format.FORMAT_JSON.getLabel());
146+
} else {
147+
// something else -> not a tabular resource
148+
int pos = name.lastIndexOf('.');
149+
return name.substring(pos + 1).toLowerCase();
150+
}
151+
}
152+
if (foundFormats.size() > 1) {
153+
throw new DataPackageException("Resources cannot be mixed JSON/CSV");
154+
}
155+
if (foundFormats.isEmpty())
156+
return TableDataSource.Format.FORMAT_CSV.getLabel();
157+
return foundFormats.iterator().next();
158+
}
159+
93160
abstract Table createTable(T reference) throws Exception;
94161

95162
abstract String getStringRepresentation(T reference);

src/main/java/io/frictionlessdata/datapackage/resource/AbstractResource.java

Lines changed: 37 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,6 @@
66
import com.fasterxml.jackson.annotation.JsonProperty;
77
import com.fasterxml.jackson.core.JsonProcessingException;
88
import com.fasterxml.jackson.databind.ObjectMapper;
9-
import com.fasterxml.jackson.databind.node.ArrayNode;
10-
import com.fasterxml.jackson.databind.node.ObjectNode;
119
import io.frictionlessdata.datapackage.Dialect;
1210
import io.frictionlessdata.datapackage.JSONBase;
1311
import io.frictionlessdata.datapackage.Package;
@@ -443,9 +441,33 @@ public void checkRelations(Package pkg) {
443441
}
444442

445443
public void validate(Package pkg) {
446-
if (null == tables)
447-
return;
444+
448445
try {
446+
// Validate required fields
447+
if (getName() == null || getName().trim().isEmpty()) {
448+
throw new DataPackageValidationException("Resource must have a name");
449+
}
450+
451+
// Validate name format (alphanumeric, dash, underscore only)
452+
if (!getName().matches("^[a-zA-Z0-9_-]+$")) {
453+
throw new DataPackageValidationException("Resource name must contain only alphanumeric characters, dashes, and underscores");
454+
}
455+
456+
// Validate profile
457+
String profile = getProfile();
458+
if (profile != null && !isValidProfile(profile)) {
459+
throw new DataPackageValidationException("Invalid resource profile: " + profile);
460+
}
461+
462+
if (null != schema) {
463+
try {
464+
schema.validate();
465+
} catch (DataPackageValidationException e) {
466+
throw new DataPackageValidationException("Schema validation failed for resource " + getName() + ": " + e.getMessage(), e);
467+
}
468+
}
469+
if (null == tables)
470+
return;
449471
// will validate schema against data
450472
tables.forEach(Table::validate);
451473
checkRelations(pkg);
@@ -698,7 +720,7 @@ public void setShouldSerializeToFile(boolean serializeToFile) {
698720

699721
@Override
700722
public void setSerializationFormat(String format) {
701-
if ((format.equals(TableDataSource.Format.FORMAT_JSON.getLabel()))
723+
if ((null == format) || (format.equals(TableDataSource.Format.FORMAT_JSON.getLabel()))
702724
|| format.equals(TableDataSource.Format.FORMAT_CSV.getLabel())) {
703725
this.serializationFormat = format;
704726
} else
@@ -721,7 +743,7 @@ public String getSerializationFormat() {
721743

722744
public abstract Set<String> getDatafileNamesForWriting();
723745

724-
private List<Table> ensureDataLoaded () throws Exception {
746+
List<Table> ensureDataLoaded () throws Exception {
725747
if (null == tables) {
726748
tables = readData();
727749
}
@@ -781,6 +803,15 @@ public void writeData(Path outputDir) throws Exception {
781803
}
782804
}
783805

806+
807+
private static boolean isValidProfile(String profile) {
808+
return profile.equals(Profile.PROFILE_DATA_RESOURCE_DEFAULT) ||
809+
profile.equals(Profile.PROFILE_TABULAR_DATA_RESOURCE) ||
810+
profile.startsWith("http://") ||
811+
profile.startsWith("https://");
812+
}
813+
814+
784815
/**
785816
* Write the Table as CSV into a file inside `outputDir`.
786817
*

src/main/java/io/frictionlessdata/datapackage/resource/FilebasedResource.java

Lines changed: 16 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import com.fasterxml.jackson.annotation.JsonIgnore;
44
import com.fasterxml.jackson.annotation.JsonInclude;
55
import com.fasterxml.jackson.annotation.JsonInclude.Include;
6-
import io.frictionlessdata.datapackage.exceptions.DataPackageException;
6+
import io.frictionlessdata.datapackage.Profile;
77
import io.frictionlessdata.datapackage.exceptions.DataPackageValidationException;
88
import io.frictionlessdata.tableschema.Table;
99
import io.frictionlessdata.tableschema.tabledatasource.TableDataSource;
@@ -15,7 +15,9 @@
1515
import java.nio.charset.StandardCharsets;
1616
import java.nio.file.Files;
1717
import java.nio.file.Path;
18-
import java.util.*;
18+
import java.util.ArrayList;
19+
import java.util.Collection;
20+
import java.util.List;
1921

2022

2123
@JsonInclude(value = Include.NON_EMPTY, content = Include.NON_EMPTY )
@@ -26,10 +28,11 @@ public class FilebasedResource extends AbstractReferencebasedResource<File> {
2628
@JsonIgnore
2729
private boolean isInArchive;
2830

29-
@JsonIgnore
31+
3032
/**
3133
* The charset (encoding) for writing
3234
*/
35+
@JsonIgnore
3336
private final Charset charset = StandardCharsets.UTF_8;
3437

3538
public FilebasedResource(String name, Collection<File> paths, File basePath, Charset encoding) {
@@ -77,27 +80,6 @@ public String getSerializationFormat() {
7780
return sniffFormat(paths);
7881
}
7982

80-
private static String sniffFormat(Collection<File> paths) {
81-
Set<String> foundFormats = new HashSet<>();
82-
for (File p : paths) {
83-
if (p.getName().toLowerCase().endsWith(TableDataSource.Format.FORMAT_CSV.getLabel())) {
84-
foundFormats.add(TableDataSource.Format.FORMAT_CSV.getLabel());
85-
} else if (p.getName().toLowerCase().endsWith(TableDataSource.Format.FORMAT_JSON.getLabel())) {
86-
foundFormats.add(TableDataSource.Format.FORMAT_JSON.getLabel());
87-
} else {
88-
// something else -> not a tabular resource
89-
int pos = p.getName().lastIndexOf('.');
90-
return p.getName().substring(pos + 1).toLowerCase();
91-
}
92-
}
93-
if (foundFormats.size() > 1) {
94-
throw new DataPackageException("Resources cannot be mixed JSON/CSV");
95-
}
96-
if (foundFormats.isEmpty())
97-
return TableDataSource.Format.FORMAT_CSV.getLabel();
98-
return foundFormats.iterator().next();
99-
}
100-
10183
public static FilebasedResource fromSource(String name, Collection<File> paths, File basePath, Charset encoding) {
10284
FilebasedResource r = new FilebasedResource(name, paths, basePath);
10385
r.encoding = encoding.name();
@@ -136,6 +118,16 @@ String getStringRepresentation(File reference) {
136118
return reference.getPath();
137119
}
138120

121+
@Override
122+
@JsonIgnore
123+
public String[] getHeaders() throws Exception{
124+
if ((null != profile) && (profile.equals(Profile.PROFILE_DATA_PACKAGE_DEFAULT))) {
125+
return null;
126+
}
127+
ensureDataLoaded();
128+
return tables.get(0).getHeaders();
129+
}
130+
139131
@Override
140132
@JsonIgnore
141133
List<Table> readData () throws Exception{

0 commit comments

Comments
 (0)