001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041: package org.netbeans.modules.db.sql.visualeditor.querybuilder;
042:
043: import org.openide.nodes.AbstractNode;
044: import org.openide.nodes.Sheet;
045: import org.openide.nodes.Children;
046: import org.openide.nodes.PropertySupport;
047: import org.openide.nodes.PropertySupport.Reflection;
048:
049: import org.openide.util.NbBundle;
050: import org.openide.NotifyDescriptor;
051: import org.openide.DialogDisplayer;
052:
053: import org.netbeans.modules.db.sql.visualeditor.Log;
054: import org.openide.util.Exceptions;
055:
056: /**
057: * Provides a place to hang properties for a node (table) in the query graph.
058: * <p>
059: *
060: * @author Jim Davidson
061: */
062:
063: public class TableNode extends AbstractNode {
064: // Private variable
065: private int SQL_IDENTIFIER_LENGTH = 32;
066:
067: private boolean DEBUG = false;
068: private String _fullTableName = null;
069: private String _corrName = null;
070: private QueryBuilder _queryBuilder;
071:
072: // Constructor
073:
074: TableNode(String fullTableName) {
075: super (Children.LEAF);
076: _fullTableName = fullTableName;
077: }
078:
079: TableNode(String fullTableName, String corrName,
080: QueryBuilder queryBuilder) {
081: super (Children.LEAF);
082: _fullTableName = fullTableName;
083: _corrName = corrName;
084: _queryBuilder = queryBuilder;
085: }
086:
087: // Accessors/mutators
088:
089: public String getTableName() {
090: return _fullTableName;
091: }
092:
093: public String getCorrName() {
094: return _corrName;
095: }
096:
097: public void setCorrName(String corrName) {
098:
099: Log.getLogger().entering("TableNode", "setCorrName", corrName); // NOI18N
100:
101: // Note the old value
102: String oldCorrName = getCorrName();
103: String oldTableSpec = (oldCorrName == null) ? getTableName()
104: : oldCorrName;
105:
106: // Sometimes we are called when the user has not made any changes. Just return
107: if (((corrName == null) && (oldCorrName == null))
108: || ((corrName != null) && (corrName.equals(oldCorrName))))
109: return;
110:
111: // Save the new value
112: if (corrName.trim().length() == 0) {
113: _corrName = null;
114: } else {
115: // Modify the corrName if necessary, to ensure that it's unique
116: // Addresses 5005528 Setting same alias for two tables produces incorrect query.
117: // A return value of null means the name was already unique
118: if (!isAliasValid(corrName.trim())) {
119: // display an error message "Invalid alias name"
120: // return without changing anything.
121: String msg = NbBundle.getMessage(TableNode.class,
122: "INVALID_ALIAS"); // NOI18N
123: NotifyDescriptor d = new NotifyDescriptor.Message(msg
124: + "\n\n" + corrName,
125: NotifyDescriptor.ERROR_MESSAGE); // NOI18N
126: DialogDisplayer.getDefault().notify(d);
127: return;
128: }
129: String tmp = _queryBuilder._queryModel
130: .genUniqueName(corrName);
131: _corrName = (tmp == null) ? corrName : tmp;
132: }
133:
134: // Update the entire model
135: _queryBuilder._queryModel.renameTableSpec(oldTableSpec,
136: _corrName);
137: // _queryBuilder.setTableColumnCorrName(oldTableSpec, _corrName);
138:
139: // Clear all panes and generate fresh
140: _queryBuilder.generate();
141: }
142:
143: // Create minimal property sheet
144:
145: protected Sheet createSheet() {
146: Sheet s = Sheet.createDefault();
147: Sheet.Set ss = s.get(Sheet.PROPERTIES);
148: try {
149: PropertySupport.Reflection p;
150: p = new Reflection(this , String.class, "getTableName", null); // NOI18N
151: p.setName("tableName"); // NOI18N
152: String tableDisplayName = NbBundle.getMessage(
153: TableNode.class, "TABLE_DISPLAY_NAME"); // NOI18N
154: // p.setDisplayName("Table Name"); // NOI18N
155: p.setDisplayName(tableDisplayName);
156:
157: String tableShortDescription = NbBundle.getMessage(
158: TableNode.class, "TABLE_SHORT_DESCRIPTION"); // NOI18N
159: // p.setShortDescription("Table name"); // NOI18N
160: p.setShortDescription(tableShortDescription);
161: ss.put(p);
162: p = new Reflection(this , String.class, "getCorrName",
163: "setCorrName"); // NOI18N
164: p.setName("aliasName"); // NOI18N
165: String aliasDisplayName = NbBundle.getMessage(
166: TableNode.class, "ALIAS_DISPLAY_NAME"); // NOI18N
167: // p.setDisplayName("Table Alias"); // NOI18N
168: p.setDisplayName(aliasDisplayName);
169: String aliasShortDescription = NbBundle.getMessage(
170: TableNode.class, "ALIAS_SHORT_DESCRIPTION"); // NOI18N
171: // p.setShortDescription("Alias name for the table"); // NOI18N
172: p.setShortDescription(aliasShortDescription);
173: ss.put(p);
174: } catch (NoSuchMethodException nsme) {
175: Exceptions.printStackTrace(nsme);
176: }
177: return s;
178: }
179:
180: public boolean isAliasValid(String aliasName) {
181: // As per the SQL 92,
182: // SQL syntax requires users to supply names for elements such as
183: // tables, aliases, views, cursors, and columns when they define
184: // them. SQL statements must use those names to refer to the table,
185: // view, or other element.
186: //
187: // The maximum length for SQL identifiers is 32 characters.
188:
189: // There are two types of SQL identifiers:
190: //
191: // * Conventional identifiers
192: // Conventional SQL identifiers must:
193: // * Begin with an uppercase or lowercase letter.
194: // * Contain only letters, digits, or the underscore character ( _ ).
195: // * Not be reserved words.
196: // * Use ASCII characters only.
197: // * SQL does not distinguish between uppercase and lowercase
198: // letters in SQL identifiers. It converts all names specified
199: // as conventional identifiers to uppercase, but statements
200: // can refer to the names in mixed case.
201: //
202: // * Delimited identifiers enclosed in double quotation marks
203: // * Delimited identifiers are strings of no more than 32 ASCII
204: // characters enclosed in double quotation marks ( " " ).
205: // Enclosing a name in double quotation marks preserves the
206: // case of the name and allows it to be a reserved word or to
207: // contain special characters. Special characters are any
208: // characters other than letters, digits, or the underscore
209: // character. Subsequent references to a delimited identifier
210: // must also use enclosing double quotation marks. To include
211: // a double quotation mark character in a delimited identifier,
212: // precede it with another double quotation mark.
213: //
214:
215: if (aliasName.startsWith("\"") && aliasName.endsWith("\"")) { // NOI18N
216: return isValidDelimitedIdentifier(aliasName.substring(1,
217: (aliasName.length() - 1)));
218: } else {
219: return isValidConventionalIdentifier(aliasName);
220: }
221: }
222:
223: boolean isValidDelimitedIdentifier(String identifier) {
224: if (identifier.length() > SQL_IDENTIFIER_LENGTH)
225: return false;
226:
227: return true;
228: }
229:
230: boolean isValidConventionalIdentifier(String identifier) {
231:
232: if (identifier.length() > SQL_IDENTIFIER_LENGTH)
233: return false;
234:
235: char[] charArray = identifier.toCharArray();
236:
237: // has to begin with uppercase or lower case letter
238: if (!Character.isLetter(charArray[0])) {
239: if (DEBUG)
240: System.out
241: .println("isValidConventionalIdentifier called. charArray[0] = "
242: + charArray[0] + "\n"); // NOI18N
243: return false;
244: }
245:
246: for (int i = 1; i < charArray.length; i++) {
247: // Contain only letters, digits, or the underscore character ( _ ).
248: if ((!Character.isLetter(charArray[i]))
249: && (!Character.isDigit(charArray[i]))
250: && (charArray[i] != '_')) {
251: return false;
252: }
253: }
254: return true;
255: }
256: }
|