/* * JBoss, Home of Professional Open Source. * * See the LEGAL.txt file distributed with this work for information regarding copyright ownership and licensing. * * See the AUTHORS.txt file distributed with this work for a full listing of individual contributors. */ package org.teiid.designer.modelgenerator.wsdl.ui.wizards.soap; import java.util.ArrayList; import java.util.Collection; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.osgi.util.NLS; import org.teiid.core.designer.util.CoreArgCheck; import org.teiid.core.designer.util.StringConstants; import org.teiid.designer.core.ModelerCore; import org.teiid.designer.core.types.DatatypeManager; import org.teiid.designer.core.validation.rules.StringNameValidator; import org.teiid.designer.metamodels.core.Datatype; import org.teiid.designer.metamodels.relational.aspects.validation.RelationalStringNameValidator; import org.teiid.designer.modelgenerator.wsdl.ui.Messages; import org.teiid.designer.modelgenerator.wsdl.ui.ModelGeneratorWsdlUiConstants; import org.teiid.designer.query.IQueryFactory; import org.teiid.designer.query.IQueryService; import org.teiid.designer.query.proc.wsdl.IWsdlAttributeInfo; import org.teiid.designer.query.proc.wsdl.IWsdlColumnInfo; import org.teiid.designer.query.sql.symbol.IElementSymbol; /** * @since 8.0 */ public class ColumnInfo implements IWsdlColumnInfo, ModelGeneratorWsdlUiConstants { private static final char SEPARATOR = '/'; public static final String INTEGER_DATATYPE = "integer"; /** Constant value indicating no segments */ private static final String[] NO_SEGMENTS = new String[0]; private static final StringNameValidator nameValidator = new RelationalStringNameValidator(false); private final SchemaPath EMPTY_PATH = new SchemaPath(StringConstants.EMPTY_STRING); /** * The collection of AttributeInfo objects */ private Collection<AttributeInfo> attributeInfoList; /** * The unique column name (never <code>null</code> or empty). */ private IElementSymbol nameSymbol; /** * The unique column datatype (never <code>null</code> or empty). */ private String datatype; /** * The column width value */ private int width = DEFAULT_WIDTH; /** * The column element namespace. This is used for request columns. */ private String namespace; /** * The unique column datatype (never <code>null</code> or empty). */ private boolean forOrdinality; /** * The unique column datatype (never <code>null</code> or empty). */ private String defaultValue = StringConstants.EMPTY_STRING; /** * The full xml path */ private SchemaPath fullXmlPath = EMPTY_PATH; /** * The root xml path */ private SchemaPath rootXmlPath = EMPTY_PATH; /** * The xml element */ private Object xmlElement; /** * Current <code>IStatus</code> representing the state of the input values for this instance of * <code>TeiidColumnInfo</code> */ private IStatus status; /** * * @param name the column name (never <code>null</code> or empty). */ public ColumnInfo(String name) { this(name, DEFAULT_DATATYPE); } /** * * @param name the column name (never <code>null</code> or empty). * @param datatype the column datatype (never <code>null</code> or empty). */ public ColumnInfo(String name, String datatype) { super(); CoreArgCheck.isNotEmpty(name, "name is null"); //$NON-NLS-1$ CoreArgCheck.isNotEmpty(datatype, "datatype is null"); //$NON-NLS-1$ initNameSymbol(name); setDatatype(datatype); this.attributeInfoList = new ArrayList<AttributeInfo>(); validate(); } /** * * @param name the column name (never <code>null</code> or empty). * @param datatype the column datatype (never <code>null</code> or empty). */ public ColumnInfo(String name, String datatype, int width) { this(name, datatype); CoreArgCheck.isPositive(width, "width is zero or less"); //$NON-NLS-1$ this.width = width; } /** * * @param name the column name (never <code>null</code> or empty). * @param datatype the column datatype (never <code>null</code> or empty). */ public ColumnInfo(String name, boolean ordinality, String datatype, String defaultValue, String fullXmlPath, String namespace ) { this(name, datatype); this.forOrdinality = ordinality; if( defaultValue == null ) { this.defaultValue = StringConstants.EMPTY_STRING; } else { this.defaultValue = defaultValue; } if( fullXmlPath == null ) { this.fullXmlPath = EMPTY_PATH; } else { this.fullXmlPath = new SchemaPath(fullXmlPath); } if( namespace != null ) { this.namespace = namespace; } validate(); } /** * Initialise the {@link ElementSymbol} to hold the * name. This validates the symbol's character composition. * * The '.' character is the only puntuation symbol that will cause * problems for an element symbol so these are replaced these with '_'. */ private void initNameSymbol(final String name) { IQueryService service = ModelerCore.getTeiidQueryService(); IQueryFactory factory = service.createQueryFactory(); nameSymbol = factory.createElementSymbol(name.replaceAll("\\.", "_")); //$NON-NLS-1$//$NON-NLS-2$ } /** * Get the column name for display in the UI. This removes any quotes for * aesthetic reasons. Use {@link #getSymbolName()} for retrieving the * fully validated column name. * * @return the column name sans quotes. */ public String getName() { String name = this.nameSymbol.toString(); return name.replaceAll("\"", ""); //$NON-NLS-1$ //$NON-NLS-2$ } /** * Get the fully validated column name. This should be used in SQL string * generation. * * @return name the column name */ public String getSymbolName() { return this.nameSymbol.toString(); } /** * * @param name the column name (never <code>null</code> or empty). */ public void setName(String name) { CoreArgCheck.isNotNull(name, "name is null"); //$NON-NLS-1$ initNameSymbol(name); validate(); } /** * * @return datatype the column datatype */ public String getDatatype() { return this.datatype; } /** * * @param datatype the column datatype (never <code>null</code> or empty). */ public void setDatatype(String datatype) { CoreArgCheck.isNotNull(datatype, "datatype is null"); //$NON-NLS-1$ this.datatype = datatype.equals(INTEGER_DATATYPE) ? BIGINTEGER_DATATYPE : datatype; validate(); } /** * * @return name the column name */ public int getWidth() { return this.width; } /** * * @param name the column name (never <code>null</code> or empty). */ public void setWidth(int width) { CoreArgCheck.isPositive(width, "width is less than 1"); //$NON-NLS-1$ this.width = width; validate(); } /** * * @return defaultValue the column defaultValue */ public String getDefaultValue() { return this.defaultValue; } /** * * @param defaultValue the column defaultValue */ public void setDefaultValue(String defaultValue) { if( defaultValue == null ) { this.defaultValue = StringConstants.EMPTY_STRING; } else { this.defaultValue = defaultValue; } validate(); } /** * * @param xmlPath the column xmlPath */ public void setRelativePath(String relativePath) { if( relativePath != null ) { this.rootXmlPath.append(relativePath); } this.fullXmlPath = new SchemaPath(this.rootXmlPath.toString()); } /** * * @return xmlPath the column xmlPath */ public String getRelativePath() { if( this.xmlElement != null ) { // Get difference between the xmlElement full path minus the root (if applicable) String fullPath = getFullXmlPath(); String rootPath = this.rootXmlPath.toString(); if( fullPath.startsWith(rootPath) ) { return fullPath.substring(rootPath.length(), fullPath.length()); } } if( !this.fullXmlPath.isEmpty() ) { if( !this.rootXmlPath.isEmpty() && this.fullXmlPath.toString().indexOf(rootXmlPath.toString()) > -1) { this.fullXmlPath.removeFirstSegments(this.rootXmlPath.segmentCount()); return this.fullXmlPath.toString(); } else { return this.fullXmlPath.toString(); } } return StringConstants.EMPTY_STRING; } public void setXmlElement(Object element) { this.xmlElement = element; } /** * * @return xmlPath the column xmlPath */ public String getFullXmlPath() { // TODO: FIX THIS // if( this.xmlElement != null ) { // return this.xmlElement.getFullPath(); // } return this.fullXmlPath.toString(); } /** * * @param xmlPath the column xmlPath */ public void setFullXmlPath(String fullPath) { if( fullPath == null ) { this.fullXmlPath = EMPTY_PATH; } else { this.fullXmlPath = new SchemaPath(fullPath); } } public void setRootPath(String thePath) { if( thePath != null && thePath.length() > 0 ) { // Need to remove the OLD root path from FULL path SchemaPath oldRelativePath = new SchemaPath(getRelativePath()); SchemaPath newRelativePath = this.rootXmlPath; StringBuffer buf = new StringBuffer(); for (String seg : newRelativePath.segments){ if (!thePath.contains(seg)){ buf.append("/").append(seg); //$NON-NLS-1$ } } StringBuffer newRelativeBuf = new StringBuffer(); for (String seg : oldRelativePath.segments){ if (!thePath.contains(seg)){ newRelativeBuf.append("/").append(seg); //$NON-NLS-1$ } } String tmpRoot = thePath; if( thePath.endsWith("/")) { //$NON-NLS-1$ tmpRoot = tmpRoot.substring(0, thePath.length()-1); } String newFullPath = tmpRoot + buf + newRelativeBuf; setFullXmlPath(newFullPath); //tmpRoot + oldRelativePath.toString()); this.rootXmlPath = new SchemaPath(thePath); } else { this.rootXmlPath = EMPTY_PATH; } } /** * * @return xmlElement the xmlElement */ public Object getXmlElement() { return this.xmlElement; } /** * * @return forOrdinality the column forOrdinality */ public boolean getOrdinality() { return this.forOrdinality; } public String getNamespace() { return namespace; } public void setNamespace(String namespace) { this.namespace = namespace; } /** * * @param forOrdinality the column forOrdinality */ public void setOrdinality(boolean value) { this.forOrdinality = value; validate(); } public IWsdlAttributeInfo[] getAttributeInfoArray() { return this.attributeInfoList.toArray(new AttributeInfo[this.attributeInfoList.size()]); } public void addAttributeInfo(Object xmlElement, String name) { this.attributeInfoList.add(new AttributeInfo(xmlElement, name, this)); validate(); } public void removeAttributeInfo(IWsdlAttributeInfo theInfo) { this.attributeInfoList.remove(theInfo); validate(); } public String getUniqueAttributeName(String proposedName) { for( IWsdlAttributeInfo info : getAttributeInfoArray()) { ColumnInfo.nameValidator.addExistingName(info.getName()); } String changedName = ColumnInfo.nameValidator.createUniqueName(proposedName); String finalName = changedName == null ? proposedName : changedName; ColumnInfo.nameValidator.clearExistingNames(); return finalName; } /** * * @return status the <code>IStatus</code> representing the validity of the data in this info object */ public IStatus getStatus() { return this.status; } /** * * @param status the <code>IStatus</code> representing the validity of the data in this info object */ public void setStatus(IStatus status) { this.status = status; } private void validate() { /* * No name validation is currently required since the name is automatically * 'fixed' by the teiid element symbol. */ // Check Datatypes if( !ImportManagerValidator.isValidDatatype(getDatatype())) { setStatus(new Status(IStatus.ERROR, PLUGIN_ID, NLS.bind(Messages.InvalidDatatype_0_ForColumn_1, getDatatype(), getName()))); return; } // Validate Paths setStatus(Status.OK_STATUS); } /** * {@inheritDoc} * * @see java.lang.Object#toString() */ @Override public String toString() { StringBuilder text = new StringBuilder(); text.append("Column Info: "); //$NON-NLS-1$ text.append("name = ").append(getSymbolName()); //$NON-NLS-1$ text.append(", ordinal = ").append(getOrdinality()); //$NON-NLS-1$ text.append(", datatype = ").append(getDatatype()); //$NON-NLS-1$ text.append(", default = ").append(getDefaultValue()); //$NON-NLS-1$ text.append(", PATH = ").append(getRelativePath()); //$NON-NLS-1$ return text.toString(); } class SchemaPath { /** The path segments */ private String[] segments; public SchemaPath(String string) { super(); segments = computeSegments(string); } public boolean isEmpty() { return segments.length == 0; } public int segmentCount() { return segments.length; } public void append(String string) { String[] tempSegments = computeSegments(string); int segCount = tempSegments.length + segments.length; Collection<String> allSegments = new ArrayList<String>(segments.length); for( String seg : segments ) { allSegments.add(seg); } for( String seg : tempSegments ) { allSegments.add(seg); } segments = allSegments.toArray(new String[segCount]); } public void removeFirstSegments(int nSegs) { int newSegCount = segments.length - nSegs; Collection<String> newSegments = new ArrayList<String>(newSegCount); int i=1; for( String seg : segments ) { if( i > nSegs) { newSegments.add(seg); } i++; } segments = newSegments.toArray(new String[newSegCount]); } /** * Computes the segment array for the given canonicalized path. */ private String[] computeSegments(String path) { // performance sensitive --- avoid creating garbage int segmentCount = computeSegmentCount(path); if (segmentCount == 0) return NO_SEGMENTS; String[] newSegments = new String[segmentCount]; int len = path.length(); // check for initial slash int firstPosition = (path.charAt(0) == SEPARATOR) ? 1 : 0; // check for UNC if (firstPosition == 1 && len > 1 && (path.charAt(1) == SEPARATOR)) firstPosition = 2; int lastPosition = (path.charAt(len - 1) != SEPARATOR) ? len - 1 : len - 2; // for non-empty paths, the number of segments is // the number of slashes plus 1, ignoring any leading // and trailing slashes int next = firstPosition; for (int i = 0; i < segmentCount; i++) { int start = next; int end = path.indexOf(SEPARATOR, next); if (end == -1) { newSegments[i] = path.substring(start, lastPosition + 1); } else { newSegments[i] = path.substring(start, end); } next = end + 1; } return newSegments; } private int computeSegmentCount(String path) { int len = path.length(); if (len == 0 || (len == 1 && path.charAt(0) == SEPARATOR)) { return 0; } int count = 1; int prev = -1; int i; while ((i = path.indexOf(SEPARATOR, prev + 1)) != -1) { if (i != prev + 1 && i != len) { ++count; } prev = i; } if (path.charAt(len - 1) == SEPARATOR) { --count; } return count; } @Override public String toString() { StringBuffer buf = new StringBuffer(); int segCount = segmentCount(); int nSeg = 0; for( String seg : segments ) { if( nSeg == 0 && seg.charAt(0) != SEPARATOR) { buf.append(SEPARATOR); } buf.append(seg); if( nSeg < segCount-1 ) { buf.append(SEPARATOR); } nSeg++; } return buf.toString(); } } }