Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,15 @@ private SchemaConstants() {
/** Schema.org Enumeration identifier. */
public static final String SCHEMA_ENUMERATION = SCHEMA_PREFIX + "Enumeration";

/**
* Returns the type name.
* If the type name does not contain ":", the method adds the prefix SCHEMA_PREFIX.
* Otherwise, the method returns the given type name.
*
* @param typeName type name
* @return updated type name
*/
public static String typeName(String typeName) {
return typeName.contains(":") ? typeName : SCHEMA_PREFIX + typeName;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ public SchemaModelGeneratorApp() {
@picocli.CommandLine.Option(names = {"-m", "--models"}, description = "list of models to be generated. If not specified, all models will be generated.", order = 0)
private List<String> models;

@picocli.CommandLine.Option(names = {"-o", "--output"}, description = "Location of the output directory (default: target/generated-sources/schemaorg)", order = 1)
@picocli.CommandLine.Option(names = {"-o", "--output"}, description = "Location of the output directory \n Default: target/generated-sources/schemaorg", order = 1)
private String output;

@picocli.CommandLine.Option(names = {"-r", "--resource"}, description = "Schema resource to be used: either a \"classpath:\" pseudo URL, a \"file:\" URL, an URL or a plain file path.", order = 2)
Expand All @@ -89,7 +89,10 @@ public SchemaModelGeneratorApp() {
@picocli.CommandLine.Option(names = {"-c", "--custom-datatypes"}, description = "Configures Java types to be used for Schema.org data types during code generation (eg. DateTime=java.time.ZonedDateTime)", split = " ", paramLabel = "<TYPE=JAVA_TYPE>", order = 8)
private Map<String, String> customDataTypes;

@picocli.CommandLine.Option(names = {"-v", "--verbose"}, description = "Verbose mode. Helpful for troubleshooting.", order = 9)
@picocli.CommandLine.Option(names = {"-f", "--filter"}, description = "Filter properties or types. Format: <Type>[:include|exclude][:prop1,prop2]. Default mode is 'exclude'.", converter = FilterOptionConverter.class, paramLabel = "<Type>[:include|exclude][:prop1,prop2]", order = 9)
private List<GeneratorOptions.FilterOption> filters;

@picocli.CommandLine.Option(names = {"-v", "--verbose"}, description = "Verbose mode. Helpful for troubleshooting.", order = 10)
private boolean verboseMode;

/**
Expand Down Expand Up @@ -120,6 +123,7 @@ public Integer call() throws Exception {
.setModelImplPackage(modelImplPackage)
.setDataTypePackage(dataTypePackage)
.setModels(models)
.setFilters(this.filters)
.addCompleteHandler(elapsedTime -> LOG.success(TIMER, "Finished:" + " {} s", elapsedTime.toSeconds()));

ParserOptions parserOptions = new ParserOptions()
Expand All @@ -139,6 +143,13 @@ public Integer call() throws Exception {
}
}

class FilterOptionConverter implements picocli.CommandLine.ITypeConverter<GeneratorOptions.FilterOption> {
@Override
public GeneratorOptions.FilterOption convert(String value) {
return GeneratorOptions.FilterOption.parse(value);
}
}

/**
* Version provider for picocli that reads version information from properties file.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@
import com.weedow.schemaorg.generator.core.handler.SuccessHandler;
import lombok.AccessLevel;
import lombok.Data;
import lombok.Getter;
import lombok.Setter;
import lombok.experimental.Accessors;

import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
Expand Down Expand Up @@ -76,6 +78,14 @@ public final class GeneratorOptions {
*/
private List<String> models;

/**
* Filters to restrict the type properties or the type itself.
*
* @return List of FilterOption
* @param filters to apply
*/
private List<FilterOption> filters;

/**
* Success handlers to be notified when a file is successfully generated.
*
Expand Down Expand Up @@ -173,4 +183,93 @@ public GeneratorOptions addCompleteHandler(CompleteHandler completeHandler) {
completeHandlers.add(completeHandler);
return this;
}

/**
* Filter Mode.
*/
public enum FilterMode {
/** Include the specified type properties or type */
INCLUDE,
/** Exclude the specified type properties or type */
EXCLUDE;

/**
* Whether the given value is a valid FilterMode value.
*
* @param value value to check
* @return {@code true} if the value is valid, {@code false} instead.
*/
public static boolean isFilterMode(String value) {
return Arrays.stream(values()).anyMatch(filterMode -> filterMode.name().equalsIgnoreCase(value));
}
}

/**
* Class representing a filter:
* <ul>
* <li>typeName: required</li>
* <li>mode: optional. Default is EXCLUDE</li>
* <li>properties: optional. If empty, it means that the type itself is filtered.</li>
* </ul>
*/
@Getter
public static class FilterOption {
private static final String SEPARATOR = ":";
private static final String PROP_SEPARATOR = ",";

/**
* The type name.
*
* @return the type name
*/
String typeName;
/**
* The filter mode.
*
* @return the filter mode. Default is FilterMode#EXCLUDE.
*/
FilterMode mode = FilterMode.EXCLUDE;
/**
* The properties
*
* @return the type properties to filter.
*/
List<String> properties = new java.util.ArrayList<>();

/**
* Parses a string with format {@code {typeName}:[mode]:[property1,property2]} and returns the corresponding FilterOption.
* <p>
* Allowed formats:
* <ul>
* <li>{@code {typeName}:{mode}:{property1,*property2*,schema:property3}}</li>
* <li>{@code {typeName}:[property1,*property2*,schema:property3]}</li>
* <li>{@code {typeName}:*}</li>
* <li>{@code {typeName}:{mode}}</li>
* <li>{@code {typeName}}</li>
* </ul>
*
* @param value the value to parse
* @return a FilterOption
*/
public static FilterOption parse(String value) {
FilterOption filter = new FilterOption();
String[] parts = value.split(SEPARATOR, 3);

filter.typeName = parts[0];

if (parts.length > 1) {
// Case: MyType:(include|exclude) or MyType:(include|exclude):prop1,prop2
if (FilterMode.isFilterMode(parts[1])) {
filter.mode = FilterMode.valueOf(parts[1].toUpperCase());
if (parts.length > 2) {
filter.properties = List.of(parts[2].split(PROP_SEPARATOR));
}
} else {
// Case : MyType:prop1,prop2 (use default mode 'exclude')
filter.properties = List.of(parts[1].split(PROP_SEPARATOR));
}
}
return filter;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import com.weedow.schemaorg.commons.generator.GeneratorConstants;
import com.weedow.schemaorg.commons.model.*;
import com.weedow.schemaorg.generator.SchemaConstants;
import com.weedow.schemaorg.generator.core.GeneratorOptions.FilterOption;
import com.weedow.schemaorg.generator.core.copy.CopyService;
import com.weedow.schemaorg.generator.core.filter.SchemaDefinitionFilter;
import com.weedow.schemaorg.generator.core.stream.StreamService;
Expand Down Expand Up @@ -112,10 +113,11 @@ public void generate() {
final String modelImplPackage = options.getModelImplPackage();
final String dataTypePackage = options.getDataTypePackage();
final List<String> models = options.getModels();
final List<FilterOption> filters = options.getFilters();

copyPropertyFile(options.getOutputFolder(), modelPackage, modelImplPackage, dataTypePackage);

Map<String, Type> filteredSchemaDefinitions = schemaDefinitionFilter.filter(schemaDefinitions, models);
Map<String, Type> filteredSchemaDefinitions = schemaDefinitionFilter.filter(schemaDefinitions, models, filters);
if (filteredSchemaDefinitions.isEmpty()) {
LOG.info(LogMarkers.ERROR, GHOST, "No schema models found to generate");
return;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.weedow.schemaorg.generator.core.filter;

import com.weedow.schemaorg.generator.core.GeneratorOptions;
import com.weedow.schemaorg.generator.model.Type;

import java.util.List;
Expand All @@ -20,12 +21,14 @@ public interface SchemaDefinitionFilter {
* <ul>
* <li>Removes types without names (retired/archived types)</li>
* <li>If modelIds is provided and not empty, includes only specified types plus their dependencies</li>
* <li>If filters is provided and not empty, filters the specified types or type properties</li>
* <li>Returns an unmodifiable map of the filtered definitions</li>
* </ul>
*
* @param schemaDefinitions the complete map of schema type definitions
* @param modelIds optional list of specific type IDs to include (null or empty for all types)
* @param filters Optional list of type filters to apply (null or empty for no filters)
* @return an unmodifiable map containing the filtered type definitions
*/
Map<String, Type> filter(Map<String, Type> schemaDefinitions, List<String> modelIds);
Map<String, Type> filter(Map<String, Type> schemaDefinitions, List<String> modelIds, List<GeneratorOptions.FilterOption> filters);
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.weedow.schemaorg.generator.core.filter;

import com.weedow.schemaorg.generator.SchemaConstants;
import com.weedow.schemaorg.generator.core.GeneratorOptions;
import com.weedow.schemaorg.generator.logging.Logger;
import com.weedow.schemaorg.generator.logging.LoggerFactory;
import com.weedow.schemaorg.generator.model.Type;
Expand All @@ -9,7 +10,7 @@
import java.util.function.Function;
import java.util.stream.Collectors;

import static com.weedow.schemaorg.generator.logging.Emojis.ARCHIVED;
import static com.weedow.schemaorg.generator.logging.Emojis.*;
import static com.weedow.schemaorg.generator.logging.LogMarkers.WARNING;

/**
Expand All @@ -20,6 +21,7 @@
* <li>Removes archived/retired types that have no name</li>
* <li>When specific model IDs are provided, includes only those types plus all their dependencies
* (parent types, property types, and enumeration subtypes)</li>
* <li>When filters are provided, remove the type properties or the type itself if properties are not specified</li>
* <li>Recursively adds all required dependencies to ensure completeness</li>
* </ul>
*/
Expand All @@ -33,11 +35,26 @@ public SchemaDefinitionFilterImpl() {
private static final Logger LOG = LoggerFactory.getLogger(SchemaDefinitionFilterImpl.class);

@Override
public Map<String, Type> filter(Map<String, Type> schemaDefinitions, List<String> modelIds) {
Map<String, Type> filteredSchemaDefinitions = new HashMap<>(schemaDefinitions);
public Map<String, Type> filter(Map<String, Type> schemaDefinitions, List<String> modelIds, List<GeneratorOptions.FilterOption> filters) {
Map<String, Type> filteredSchemaDefinitions = filterTypesWithoutName(schemaDefinitions);

// Filters types without a “name.” These types have been retired from the vocabulary, but their IDs are still referenced by some properties (e.g. schema:DeliveryTimeSettings).
filteredSchemaDefinitions = filteredSchemaDefinitions.entrySet().stream()
filteredSchemaDefinitions = applyFilters(filters, filteredSchemaDefinitions);

filteredSchemaDefinitions = applyModelIds(modelIds, filteredSchemaDefinitions);

// Unmodifiable Map
return Collections.unmodifiableMap(filteredSchemaDefinitions);
}

/**
* Filters types without a “name”.
* These types have been retired from the vocabulary, but their IDs are still referenced by some properties (e.g. schema:DeliveryTimeSettings).
*
* @param schemaDefinitions the map of schema type definitions
* @return a map containing the filtered type definitions
*/
private static Map<String, Type> filterTypesWithoutName(Map<String, Type> schemaDefinitions) {
return schemaDefinitions.entrySet().stream()
.filter(entry -> {
Type type = entry.getValue();
if (type.getName() == null || type.getName().isEmpty()) {
Expand All @@ -47,11 +64,52 @@ public Map<String, Type> filter(Map<String, Type> schemaDefinitions, List<String
return true;
})
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
}

/**
* Applies the given filter options to the schema definitions.
*
* @param filters the list of filter options to apply
* @param schemaDefinitions the current map of schema type definitions
* @return a map containing the filtered type definitions
*/
private static Map<String, Type> applyFilters(List<GeneratorOptions.FilterOption> filters, Map<String, Type> schemaDefinitions) {
final Map<String, Type> filteredSchemaDefinitions = new HashMap<>(schemaDefinitions);

if (filters != null && !filters.isEmpty()) {
filters.forEach(filterOption -> {
String typeName = SchemaConstants.typeName(filterOption.getTypeName());
GeneratorOptions.FilterMode mode = filterOption.getMode();
List<String> propertyIds = filterOption.getProperties();
Type type = filteredSchemaDefinitions.get(typeName);
if (type != null) {
if (propertyIds != null && !propertyIds.isEmpty()) {
LOG.info(FILTERS, "Filtering properties: {} {} from {}", mode, propertyIds, typeName);
type.filterProperties(mode, propertyIds);
} else if (mode == GeneratorOptions.FilterMode.EXCLUDE) {
LOG.info(FILTERS, "Filtering type {} with mode {}", typeName, mode);
filteredSchemaDefinitions.remove(typeName);
}
}
});
}

return filteredSchemaDefinitions;
}

/**
* Filters the schema definitions to include only the specified model IDs and their dependencies.
*
* @param modelIds the list of model IDs to include
* @param schemaDefinitions the current map of schema type definitions
* @return a map containing only the requested models and their dependencies
*/
private static Map<String, Type> applyModelIds(List<String> modelIds, Map<String, Type> schemaDefinitions) {
if (modelIds != null && !modelIds.isEmpty()) {
filteredSchemaDefinitions = modelIds.stream()
LOG.info(MODELS, "Model IDs specified: {}", modelIds);
return modelIds.stream()
// Fix model id (format 'schema:xxx')
.map(modelId -> modelId.contains(":") ? modelId : SchemaConstants.SCHEMA_PREFIX + modelId)
.map(SchemaConstants::typeName)
// Filter existing models, skip models not found
.filter(schemaDefinitions::containsKey)
.flatMap(modelId -> {
Expand All @@ -63,8 +121,7 @@ public Map<String, Type> filter(Map<String, Type> schemaDefinitions, List<String
.collect(Collectors.toMap(Type::getId, Function.identity(), (oldValue, newValue) -> oldValue, LinkedHashMap::new));
}

// Unmodifiable Map
return Collections.unmodifiableMap(filteredSchemaDefinitions);
return schemaDefinitions;
}

/**
Expand All @@ -87,7 +144,7 @@ private static void addType(Set<Type> types, Type type) {
type.getParents().forEach(parent -> addType(types, parent));
type.getAllProperties().forEach(property -> property.getTypes().forEach(propertyType -> addType(types, propertyType)));
if (type.isEnumerationType()) {
types.addAll(type.getSubTypes());
type.getSubTypes().forEach(subType -> addType(types, subType));
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ private Emojis() {
public static final Emoji LABEL = Emoji.of("🏷");
/** Emoji for Java-related operations. */
public static final Emoji JAVA = Emoji.of("☕");
/** Emoji for filtering operations. */
public static final Emoji FILTERS = Emoji.of("🔽");
/** Emoji for models-related message. */
public static final Emoji MODELS = Emoji.of("🧱");
/** Emoji for in progress generation operations. */
public static final Emoji GEAR = Emoji.of("⚙");
/** Emoji for completed generation operations. */
Expand Down
Loading
Loading