git clone https://github.com/Cantara/lib-electronic-components
T=$(mktemp -d) && git clone --depth=1 https://github.com/Cantara/lib-electronic-components "$T" && mkdir -p ~/.claude/skills && cp -r "$T/.claude/skills/component-spec-extraction" ~/.claude/skills/cantara-lib-electronic-components-component-spec-extraction && rm -rf "$T"
.claude/skills/component-spec-extraction/SKILL.mdComponent Spec Extraction
Use this skill when working with component specification extraction, SpecValue wrappers, or type-specific extraction patterns.
Spec Class Locations
24 spec classes organized in 4 packages:
base/ Package (Core Specs)
- Base interface for all specsComponentSpecs.java
- Generic spec value holderSpec.java
- Enum of measurement unitsSpecUnit.java
- Type-safe unit-aware wrapperSpecValue.java
passive/ Package (Passive Components)
- Resistance, tolerance, power ratingResistorSpecs.java
- Capacitance, voltage, dielectric, ESRCapacitorSpecs.java
- Inductance, current rating, DCRInductorSpecs.java
semiconductor/ Package (Semiconductors)
- Forward voltage, reverse voltage, currentDiodeSpecs.java
- VCE, IC, hFE, polarityTransistorSpecs.java
- VDS, ID, RDS(on), channel typeMosfetSpecs.java
- Configuration, GBW, input offsetOpAmpSpecs.java
- Output voltage, current, dropoutVoltageRegulatorSpecs.java
power/ Package (Power Components)
- Input/output voltage, efficiencyPowerSupplySpecs.java
- Capacity, chemistry, voltageBatterySpecs.java
Additional specs:
- MemorySpecs, SensorSpecs, ConnectorSpecs, LEDSpecs, LogicICSpecs, MCUSpecs, etc.
SpecValue<T> Wrapper Pattern
Purpose: Type-safe, unit-aware value representation.
Constructor (NO static factory method):
// ✅ CORRECT: Use constructor directly SpecValue<Double> resistance = new SpecValue<>(1000.0, SpecUnit.OHM); SpecValue<String> dielectric = new SpecValue<>("X7R", SpecUnit.NONE); // ❌ WRONG: No factory method exists // SpecValue<Double> resistance = SpecValue.of(1000.0, SpecUnit.OHM); // DOESN'T EXIST!
Implementation:
public class SpecValue<T> { private final T value; private final SpecUnit unit; private final T minValue; // Optional range private final T maxValue; // Optional range // Constructor public SpecValue(T value, SpecUnit unit) { this.value = value; this.unit = unit; this.minValue = null; this.maxValue = null; } // Constructor with range public SpecValue(T value, SpecUnit unit, T minValue, T maxValue) { this.value = value; this.unit = unit; this.minValue = minValue; this.maxValue = maxValue; } public T getValue() { return value; } public SpecUnit getUnit() { return unit; } public T getMinValue() { return minValue; } public T getMaxValue() { return maxValue; } }
Examples:
// Resistance with tolerance SpecValue<Double> resistance = new SpecValue<>(10000.0, SpecUnit.OHM, 9500.0, 10500.0); // Capacitance SpecValue<Double> capacitance = new SpecValue<>(100e-9, SpecUnit.FARAD); // 100nF // Voltage rating SpecValue<Double> voltage = new SpecValue<>(50.0, SpecUnit.VOLT); // Dielectric (no unit) SpecValue<String> dielectric = new SpecValue<>("X7R", SpecUnit.NONE);
Extraction Method Naming
Convention: Consistent camelCase method names across all files.
Patterns:
// ✅ CORRECT: extractXxx() pattern public String extractResistance(String mpn); public String extractCapacitance(String mpn); public String extractVoltage(String mpn); public String extractPackage(String mpn); public String extractTolerance(String mpn); public String extractDielectric(String mpn); // ❌ WRONG: Inconsistent naming public String getResistance(String mpn); // Don't mix get/extract public String parseCapacitance(String mpn); // Don't use parse public String resistance(String mpn); // Too terse
Rationale:
implies parsing from MPN stringextract
implies retrieving existing valueget- Consistency aids navigation and maintenance
Unit Awareness
SpecUnit Enum Values:
public enum SpecUnit { // Electrical OHM("Ω"), // Resistance FARAD("F"), // Capacitance HENRY("H"), // Inductance VOLT("V"), // Voltage AMPERE("A"), // Current WATT("W"), // Power HERTZ("Hz"), // Frequency // Dimensionless PERCENTAGE("%"), NONE(""); private final String symbol; SpecUnit(String symbol) { this.symbol = symbol; } public String getSymbol() { return symbol; } }
Unit Conversion Patterns:
// Resistor values: k, M multipliers "1k" → 1000.0 Ω "10k" → 10000.0 Ω "1M" → 1000000.0 Ω // Capacitor values: p, n, u, m multipliers "10pF" → 10e-12 F "100nF" → 100e-9 F "1uF" → 1e-6 F "10mF" → 10e-3 F // Voltage: m, k multipliers "3.3V" → 3.3 V "5V" → 5.0 V "400kV" → 400000.0 V
Type-Specific Extraction Patterns
Resistor: Value from MPN Position
Chip resistors (e.g., Yageo RC0805JR-0710KL):
public String extractResistance(String mpn) { // RC0805JR-0710KL // Positions 9-12: "10K" → 10kΩ if (mpn.length() < 13) return ""; String valueCode = mpn.substring(9, 13); // "10KL" // Parse "10K" → 10000Ω }
Capacitor: Value from Code (104 = 100nF)
Ceramic capacitors use 3-digit codes:
public String extractCapacitance(String mpn) { // GRM188R71H104KA01D // "104" in MPN → 10 × 10^4 pF = 100,000pF = 100nF Pattern codePattern = Pattern.compile("(\\d{3})"); Matcher matcher = codePattern.matcher(mpn); if (matcher.find()) { String code = matcher.group(1); int digit1 = code.charAt(0) - '0'; int digit2 = code.charAt(1) - '0'; int multiplier = code.charAt(2) - '0'; double picofarads = (digit1 * 10 + digit2) * Math.pow(10, multiplier); return picofarads + "pF"; } }
Examples:
- 104 → 10 × 10^4 pF = 100nF
- 105 → 10 × 10^5 pF = 1µF
- 106 → 10 × 10^6 pF = 10µF
MOSFET: Voltage/Current from Suffix
Infineon IRFxxx series:
public String extractVoltage(String mpn) { // IRF530 → 100V // IRF540 → 100V // IRFZ44 → 55V // Known mappings for Infineon MOSFETs if (mpn.startsWith("IRF530")) return "100V"; if (mpn.startsWith("IRF540")) return "100V"; if (mpn.startsWith("IRFZ44")) return "55V"; // Or parse from datasheet mapping table }
Null Handling Strategies
Return null vs empty string vs Optional
Pattern 1: Return empty string (most common)
public String extractPackage(String mpn) { if (mpn == null || mpn.isEmpty()) { return ""; // Empty string indicates "not found" } // Extraction logic... if (packageNotFound) { return ""; } return packageCode; }
Pattern 2: Return null (for numeric values)
public Double extractCapacitance(String mpn) { if (mpn == null) { return null; // null indicates "not found" } // Extraction logic... if (cannotParse) { return null; } return capacitanceValue; }
Pattern 3: Return Optional (modern approach)
public Optional<String> extractDielectric(String mpn) { if (mpn == null) { return Optional.empty(); } // Extraction logic... if (dielectricNotFound) { return Optional.empty(); } return Optional.of(dielectricType); }
Recommendation: Use empty string for String returns, null for numeric returns.
Validation Before Extraction
public String extractResistance(String mpn) { // ✅ CORRECT: Validate first if (mpn == null || mpn.isEmpty()) { return ""; } if (mpn.length() < MINIMUM_LENGTH) { return ""; // MPN too short to contain resistance code } // Now safe to extract String valueCode = mpn.substring(startIndex, endIndex); }
Integration with Similarity Calculators
Metadata-driven similarity uses spec extraction:
// In ResistorSimilarityCalculator.calculateMetadataDrivenSimilarity(): // Extract resistance spec String resistance1 = extractResistance(mpn1); String resistance2 = extractResistance(mpn2); if (!resistance1.isEmpty() && !resistance2.isEmpty()) { // Create SpecValue SpecValue<Double> r1 = new SpecValue<>(parseResistance(resistance1), SpecUnit.OHM); SpecValue<Double> r2 = new SpecValue<>(parseResistance(resistance2), SpecUnit.OHM); // Get tolerance rule from metadata ComponentTypeMetadata.SpecConfig resistanceConfig = metadata.getSpecConfig("resistance"); ToleranceRule rule = resistanceConfig.getToleranceRule(); // Compare double specScore = rule.compare(r1, r2); double specWeight = profile.getEffectiveWeight(resistanceConfig.getImportance()); totalScore += specScore * specWeight; }
Learnings & Quirks
No SpecValue Factory Method
Use
new SpecValue<>(value, unit) constructor, not a factory method (doesn't exist).
camelCase Naming is Mandatory
All extraction methods use extractXxx() pattern. Don't mix with getXxx() or parseXxx().
Null Handling Varies by Type
- String returns: empty string ""
- Numeric returns: null
- Modern APIs: Optional<T>
Units Matter for Comparison
10kΩ and 10000Ω are the same value but different strings. Always convert to base unit (Ω) before comparison.
Manufacturer-Specific Extraction
Each manufacturer has unique MPN encoding. Resistor extraction for Yageo differs from Vishay, KOA, etc.
See Also
- How specs are used in metadata-driven similarity/similarity-metadata
- Converting calculators to use spec extraction/metadata-driven-similarity-conversion- 24 spec classes in 4 packages (base/, passive/, semiconductor/, power/)
- Type-safe wrapper implementationSpecValue.java
- Measurement units enumSpecUnit.java