001package Torello.Browser; 002 003import Torello.JavaDoc.Annotations.LinkJavaSource; 004import Torello.Java.StrIndent; 005import Torello.Java.ReadOnly.ReadOnlyList; 006 007/** 008 * A 'Builder' software design pattern that may be used to construct instances of any of the nested 009 * domain types & classes in CDP. The goal of this class is to make it easier to construct 010 * instances of CDP types that have <B><I>many, many</I></B> fields. Although it is perfectly 011 * legal to invoke the constructor of any CDP data type directly, a class with only 2 or 3 fields 012 * won't provide any benefit above and beyond using the constructor of the class. 013 * 014 * <BR /><BR /> 015 * ⚠️ Only classes with "lots and lots" of fields will benefit from using the {@code 'TypeBuilder'} 016 * design pattern! 017 * 018 * <EMBED CLASS='external-html' DATA-FILE-ID=TB.Example> 019 * 020 * @param <CDP_TYPE> The CDP data type produced by this builder's {@link #build()} method. 021 * 022 * @see NestedDescriptor 023 * @see NestedHelper#descriptor() 024 */ 025public class TypeBuilder<CDP_TYPE extends BaseType<CDP_TYPE>> 026 extends AbstractBuilder<CDP_TYPE> 027 implements java.io.Serializable, Cloneable 028{ 029 /** <EMBED CLASS='external-html' DATA-FILE-ID=SVUID> */ 030 protected static final long serialVersionUID = 1L; 031 032 /** 033 * Convert 'this' Builder-Instance into an actual Object-Instance 034 * @return An instance of {@code CDP_TYPE} 035 */ 036 @Override 037 @SuppressWarnings("unchecked") 038 public CDP_TYPE build() 039 { 040 return BuildInstance.build 041 ((NestedDescriptor<CDP_TYPE>) this.descriptor, this.assignments, this.assigned); 042 } 043 044 // used by 'clone' 045 private TypeBuilder(final TypeBuilder<CDP_TYPE> other) 046 { super(other); } 047 048 // used by 'builder' 049 private TypeBuilder(final NestedDescriptor<CDP_TYPE> descriptor) 050 { super(descriptor); } 051 052 /** 053 * Creates a new builder, ready to accept field assignments. 054 * 055 * @param <T> This is the object type, or {@code 'class'} which you would like to have built by 056 * this {@code TypeBuilder}. 057 * 058 * @param descriptor The singleton descriptor for the CDP data type being built. 059 * Retrieving a descriptor is as easy as invoking {@link NestedHelper#descriptor() descriptor()} 060 * on any CDP data type. 061 * 062 * @return a new builder instance of this class 063 * 064 * @see NestedHelper#descriptor() 065 */ 066 public static <T extends BaseType<T>> TypeBuilder<T> builder 067 (final NestedDescriptor<T> descriptor) 068 { 069 java.util.Objects.requireNonNull(descriptor, "parameter 'descriptor' is null"); 070 return new TypeBuilder<>(descriptor); 071 } 072 073 /** 074 * This creates a new builder, but initializes it with an actual object instance 075 * @param <T> The {@code 'BaseType'} class. 076 * @param descriptor descriptor of the CDP data carrier class being constructed 077 * @param bt instance of the CDP data carrier class, used to populate the builder 078 * @return a {@code 'TypeBuilder'}, initialized with the values in {@code 'bt'} 079 * 080 * @see NestedHelper#descriptor() 081 */ 082 public static <T extends BaseType<T>> TypeBuilder<T> witherBuilder( 083 final NestedDescriptor<T> descriptor, 084 final BaseType<T> bt 085 ) 086 { 087 java.util.Objects.requireNonNull(descriptor, "parameter 'descriptor' is null"); 088 java.util.Objects.requireNonNull(bt, "parameter 'bt' is null"); 089 090 if (! descriptor.baseTypeClass.isInstance(bt)) throw new IllegalArgumentException 091 ("'bt' is not an instance of " + descriptor.baseTypeClass.getCanonicalName()); 092 093 final TypeBuilder<T> tb = new TypeBuilder<>(descriptor); 094 final ReadOnlyList<Boolean> isPresent = bt.isPresent(); 095 096 for (int i=0; i < descriptor.size; i++) if (isPresent.get(i)) 097 { 098 tb.assigned[i] = true; 099 100 try 101 { 102 tb.assignments[i] = descriptor 103 .baseTypeClass 104 .getField(descriptor.names.get(i)) 105 .get(bt); 106 } 107 108 catch (NoSuchFieldException | SecurityException | IllegalAccessException e) 109 { 110 throw new TypeBuilderError( 111 "Could not extract field [" + descriptor.names.get(i) + "] from instance of " + 112 descriptor.baseTypeClass.getCanonicalName(), 113 e 114 ); 115 } 116 } 117 118 return tb; 119 } 120 121 /** {@inheritDoc} */ 122 @Override 123 public TypeBuilder<CDP_TYPE> clone() 124 { return new TypeBuilder<>(this); } 125 126 private static class TypeBuilderError extends Error 127 { 128 protected static final long serialVersionUID = 1L; 129 130 private TypeBuilderError(final String message, final Throwable cause) 131 { super(message, cause); } 132 } 133}