or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

index.mdjaxb-lifecycle.mdjaxb-model.mdobject-locators.mdstrategic-patterns.mdutilities.md
tile.json

object-locators.mddocs/

Object Location Tracking

Object locator system for tracking navigation paths through object hierarchies. Essential for debugging, error reporting, and providing context during strategic operations. The locator system enables precise error reporting and supports JAXB validation integration.

Capabilities

Core Interfaces

ObjectLocator

Main interface that denotes a location in an object structure, extending both ValidationEventLocator and Reportable for comprehensive location tracking and reporting.

interface ObjectLocator extends ValidationEventLocator, Reportable {
    ObjectLocator getParentLocator();
    ObjectLocator[] getPath(); 
    String getPathAsString();
    PropertyObjectLocator property(String propertyName, Object propertyValue);
    ItemObjectLocator item(int itemIndex, Object itemValue);
}

Specialized Locator Interfaces

interface RootObjectLocator extends ObjectLocator {
    // Marker interface for root object locators
}

interface PropertyObjectLocator extends ObjectLocator {
    String getPropertyName();
    Object getObject();
}

interface ItemObjectLocator extends ObjectLocator {
    int getIndex();
    Object getObject();
}

Abstract Base Implementation

abstract class AbstractObjectLocator implements ObjectLocator {
    protected AbstractObjectLocator(ObjectLocator parentLocator, Object object);
    
    // ObjectLocator methods
    public ObjectLocator getParentLocator();
    public ObjectLocator[] getPath();
    public String getPathAsString();
    public Object getObject();
    public ItemObjectLocator item(int index, Object value);
    public PropertyObjectLocator property(String name, Object value);
    
    // ValidationEventLocator methods (return default values)
    public int getColumnNumber();
    public int getLineNumber(); 
    public int getOffset();
    public URL getURL();
    public Node getNode();
    
    // Reportable methods
    public String getMessageCode();
    public String getMessage();
    public String getMessage(ResourceBundle bundle);
    
    // Abstract methods for subclasses
    protected abstract String getStepAsString();
    protected abstract String getDefaultMessage();
}

Concrete Implementations

final class DefaultRootObjectLocator extends AbstractObjectLocator implements RootObjectLocator {
    public DefaultRootObjectLocator(Object rootObject);
    public Object[] getMessageParameters();
}

final class DefaultPropertyObjectLocator extends AbstractObjectLocator implements PropertyObjectLocator {
    protected DefaultPropertyObjectLocator(ObjectLocator parentLocator, String propertyName, Object propertyValue);
    public String getPropertyName();
    public Object[] getMessageParameters();
}

final class DefaultItemObjectLocator extends AbstractObjectLocator implements ItemObjectLocator {
    protected DefaultItemObjectLocator(ObjectLocator parentLocator, int itemIndex, Object itemValue);
    public int getIndex();
    public Object[] getMessageParameters();
}

Utility Classes

final class LocatorUtils {
    // SAX Locator formatting
    static String getLocation(Locator locator);
    
    // Null-safe property locator creation
    static PropertyObjectLocator property(ObjectLocator parent, String name, Object value);
    static PropertyObjectLocator property(ObjectLocator parent, String name, boolean value);
    static PropertyObjectLocator property(ObjectLocator parent, String name, byte value);
    static PropertyObjectLocator property(ObjectLocator parent, String name, char value);
    static PropertyObjectLocator property(ObjectLocator parent, String name, double value);
    static PropertyObjectLocator property(ObjectLocator parent, String name, float value);
    static PropertyObjectLocator property(ObjectLocator parent, String name, int value);
    static PropertyObjectLocator property(ObjectLocator parent, String name, long value);
    static PropertyObjectLocator property(ObjectLocator parent, String name, short value);
    
    // Null-safe item locator creation
    static ItemObjectLocator item(ObjectLocator parent, int index, Object value);
    static ItemObjectLocator item(ObjectLocator parent, int index, boolean value);
    static ItemObjectLocator item(ObjectLocator parent, int index, byte value);
    static ItemObjectLocator item(ObjectLocator parent, int index, char value);
    static ItemObjectLocator item(ObjectLocator parent, int index, double value);
    static ItemObjectLocator item(ObjectLocator parent, int index, float value);
    static ItemObjectLocator item(ObjectLocator parent, int index, int value);
    static ItemObjectLocator item(ObjectLocator parent, int index, long value);
    static ItemObjectLocator item(ObjectLocator parent, int index, short value);
}

Usage Examples

Basic Object Path Tracking

import org.jvnet.jaxb2_commons.locator.*;
import org.jvnet.jaxb2_commons.locator.util.LocatorUtils;

// Create root locator for a customer object
Customer customer = new Customer();
customer.setName("John Doe");
customer.setAddress(new Address());
customer.getAddress().setStreet("123 Main St");

DefaultRootObjectLocator rootLocator = new DefaultRootObjectLocator(customer);

// Navigate to properties
PropertyObjectLocator nameLocator = rootLocator.property("name", customer.getName());
PropertyObjectLocator addressLocator = rootLocator.property("address", customer.getAddress());
PropertyObjectLocator streetLocator = addressLocator.property("street", customer.getAddress().getStreet());

// Get path information
System.out.println(streetLocator.getPathAsString()); 
// Output: <customer>.address.street

ObjectLocator[] path = streetLocator.getPath();
// Returns array: [rootLocator, addressLocator, streetLocator]

Using LocatorUtils for Null-Safe Operations

import org.jvnet.jaxb2_commons.locator.util.LocatorUtils;

// Null-safe property creation (handles null parent gracefully)
ObjectLocator parentLocator = null; // or any ObjectLocator
PropertyObjectLocator safeLocator = LocatorUtils.property(parentLocator, "name", "John");
// Returns locator even if parent is null

// Primitive type handling with auto-boxing
PropertyObjectLocator intLocator = LocatorUtils.property(parentLocator, "age", 25);
PropertyObjectLocator booleanLocator = LocatorUtils.property(parentLocator, "active", true);

// Array/collection item tracking
List<String> items = Arrays.asList("first", "second", "third");
ObjectLocator listLocator = new DefaultRootObjectLocator(items);
for (int i = 0; i < items.size(); i++) {
    ItemObjectLocator itemLocator = LocatorUtils.item(listLocator, i, items.get(i));
    System.out.println(itemLocator.getPathAsString()); 
    // Output: <list>[0], <list>[1], <list>[2]
}

Integration with Strategic Patterns

public class Order implements Equals2 {
    private String orderId;
    private List<OrderItem> items;
    
    @Override
    public boolean equals(ObjectLocator thisLocator, ObjectLocator thatLocator, 
                         Object object, EqualsStrategy2 strategy) {
        if (!(object instanceof Order)) {
            return false;
        }
        Order that = (Order) object;
        
        // Using LocatorUtils for property navigation
        boolean orderIdEquals = strategy.equals(
            LocatorUtils.property(thisLocator, "orderId", this.orderId),
            LocatorUtils.property(thatLocator, "orderId", that.orderId),
            this.orderId, that.orderId, 
            (this.orderId != null), (that.orderId != null)
        );
        
        boolean itemsEquals = strategy.equals(
            LocatorUtils.property(thisLocator, "items", this.items),
            LocatorUtils.property(thatLocator, "items", that.items),
            this.items, that.items,
            (this.items != null), (that.items != null)
        );
        
        return orderIdEquals && itemsEquals;
    }
}

Error Reporting with Locators

// Custom validation using locators for precise error reporting
public class CustomerValidator {
    public void validate(Customer customer) {
        DefaultRootObjectLocator rootLocator = new DefaultRootObjectLocator(customer);
        
        if (customer.getName() == null || customer.getName().trim().isEmpty()) {
            PropertyObjectLocator nameLocator = LocatorUtils.property(rootLocator, "name", customer.getName());
            throw new ValidationException("Name is required at: " + nameLocator.getPathAsString());
        }
        
        if (customer.getAddresses() != null) {
            PropertyObjectLocator addressesLocator = LocatorUtils.property(rootLocator, "addresses", customer.getAddresses());
            for (int i = 0; i < customer.getAddresses().size(); i++) {
                Address address = customer.getAddresses().get(i);
                ItemObjectLocator addressLocator = LocatorUtils.item(addressesLocator, i, address);
                
                if (address.getZipCode() == null) {
                    PropertyObjectLocator zipLocator = LocatorUtils.property(addressLocator, "zipCode", address.getZipCode());
                    throw new ValidationException("Zip code is required at: " + zipLocator.getPathAsString());
                    // Error message: "Zip code is required at: <customer>.addresses[2].zipCode"
                }
            }
        }
    }
}

JAXB Validation Integration

// Implementing Reportable for internationalized error messages
public class CustomValidationError extends AbstractObjectLocator implements Reportable {
    private final String messageCode;
    private final Object[] parameters;
    
    public CustomValidationError(ObjectLocator parent, String property, Object value, 
                               String messageCode, Object... parameters) {
        super(parent, value);
        this.messageCode = messageCode;
        this.parameters = parameters;
    }
    
    @Override
    public String getMessageCode() {
        return messageCode;
    }
    
    @Override
    public Object[] getMessageParameters() {
        return parameters;
    }
    
    @Override
    protected String getStepAsString() {
        return getPropertyName();
    }
    
    @Override
    protected String getDefaultMessage() {
        return "Validation error at " + getPathAsString();
    }
}

// Usage in JAXB validation event handler
public class ValidationEventHandler implements javax.xml.bind.ValidationEventHandler {
    @Override
    public boolean handleEvent(ValidationEvent event) {
        ValidationEventLocator locator = event.getLocator();
        
        // Convert JAXB locator to our ObjectLocator if needed
        if (locator instanceof ObjectLocator) {
            ObjectLocator objectLocator = (ObjectLocator) locator;
            System.err.println("Validation error at: " + objectLocator.getPathAsString());
            System.err.println("Message: " + objectLocator.getMessage());
        }
        
        return true; // Continue validation
    }
}

Object Path Tracking Architecture

The locator system works as a hierarchical path tracking mechanism:

  1. Root Level: DefaultRootObjectLocator represents the starting point of navigation
  2. Property Navigation: DefaultPropertyObjectLocator tracks object property access
  3. Collection Navigation: DefaultItemObjectLocator tracks array/list element access
  4. Path Building: Each locator maintains parent reference enabling full path reconstruction
  5. String Representation: Paths are formatted as <root>.property[index].subProperty[subIndex]...

This architecture provides complete traceability through complex object hierarchies, making it invaluable for debugging JAXB binding issues, validation errors, and strategic operation failures.